001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.filemanagers;
017
018import io.github.ascopes.jct.utils.ToStringBuilder;
019import java.util.Objects;
020import javax.tools.JavaFileManager.Location;
021import org.apiguardian.api.API;
022import org.apiguardian.api.API.Status;
023import org.jspecify.annotations.Nullable;
024
025/**
026 * Handle that represents the location of a module.
027 *
028 * @author Ashley Scopes
029 * @since 0.0.1
030 */
031@API(since = "0.0.1", status = Status.STABLE)
032public final class ModuleLocation implements Location {
033
034  private final Location parent;
035  private final String moduleName;
036  private final String name;
037
038  /**
039   * Initialize the location.
040   *
041   * @param parent     the parent location.
042   * @param moduleName the module name.
043   * @throws IllegalArgumentException if the parent location is not an output location or a
044   *                                  module-oriented location.
045   */
046  public ModuleLocation(Location parent, String moduleName) {
047    Objects.requireNonNull(parent, "parent");
048    Objects.requireNonNull(moduleName, "moduleName");
049
050    if (!parent.isOutputLocation() && !parent.isModuleOrientedLocation()) {
051      throw new IllegalArgumentException(
052          "The parent of a module location must be either an output location "
053              + "or be module-oriented, but got "
054              + parent.getName()
055      );
056    }
057
058    this.parent = parent;
059    this.moduleName = moduleName;
060    name = parent.getName() + "[" + moduleName + "]";
061  }
062
063  /**
064   * Get the parent location.
065   *
066   * @return the parent location.
067   */
068  public Location getParent() {
069    return parent;
070  }
071
072  /**
073   * Get the module name.
074   *
075   * @return the module name.
076   */
077  public String getModuleName() {
078    return moduleName;
079  }
080
081  @Override
082  public String getName() {
083    return name;
084  }
085
086  @Override
087  public boolean isOutputLocation() {
088    return parent.isOutputLocation();
089  }
090
091  @Override
092  public boolean isModuleOrientedLocation() {
093    // Module locations cannot be module-oriented, but their parents can be.
094    return false;
095  }
096
097  @Override
098  public boolean equals(@Nullable Object other) {
099    if (!(other instanceof ModuleLocation)) {
100      return false;
101    }
102
103    var that = (ModuleLocation) other;
104
105    return parent.equals(that.parent)
106        && moduleName.equals(that.moduleName);
107  }
108
109  @Override
110  public int hashCode() {
111    return Objects.hash(parent, moduleName);
112  }
113
114  @Override
115  public String toString() {
116    return new ToStringBuilder(this)
117        .attribute("parent", parent)
118        .attribute("moduleName", moduleName)
119        .toString();
120  }
121}