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.ex.JctIllegalInputException;
019import io.github.ascopes.jct.utils.ToStringBuilder;
020import java.util.Objects;
021import javax.tools.JavaFileManager.Location;
022import org.apiguardian.api.API;
023import org.apiguardian.api.API.Status;
024import org.jspecify.annotations.Nullable;
025
026/**
027 * Handle that represents the location of a module.
028 *
029 * @author Ashley Scopes
030 * @since 0.0.1
031 */
032@API(since = "0.0.1", status = Status.STABLE)
033public final class ModuleLocation implements Location {
034
035  private final Location parent;
036  private final String moduleName;
037  private final String name;
038
039  /**
040   * Initialize the location.
041   *
042   * @param parent     the parent location.
043   * @param moduleName the module name.
044   * @throws JctIllegalInputException if the parent location is not an output location or a
045   *                                  module-oriented location.
046   */
047  public ModuleLocation(Location parent, String moduleName) {
048    Objects.requireNonNull(parent, "parent");
049    Objects.requireNonNull(moduleName, "moduleName");
050
051    if (!parent.isOutputLocation() && !parent.isModuleOrientedLocation()) {
052      throw new JctIllegalInputException(
053          "The parent of a module location must be either an output location "
054              + "or be module-oriented, but got "
055              + parent.getName()
056      );
057    }
058
059    this.parent = parent;
060    this.moduleName = moduleName;
061    name = parent.getName() + "[" + moduleName + "]";
062  }
063
064  /**
065   * Get the parent location.
066   *
067   * @return the parent location.
068   */
069  public Location getParent() {
070    return parent;
071  }
072
073  /**
074   * Get the module name.
075   *
076   * @return the module name.
077   */
078  public String getModuleName() {
079    return moduleName;
080  }
081
082  @Override
083  public String getName() {
084    return name;
085  }
086
087  @Override
088  public boolean isOutputLocation() {
089    return parent.isOutputLocation();
090  }
091
092  @Override
093  public boolean isModuleOrientedLocation() {
094    // Module locations cannot be module-oriented, but their parents can be.
095    return false;
096  }
097
098  @Override
099  public boolean equals(@Nullable Object other) {
100    if (!(other instanceof ModuleLocation)) {
101      return false;
102    }
103
104    var that = (ModuleLocation) other;
105
106    return parent.equals(that.parent)
107        && moduleName.equals(that.moduleName);
108  }
109
110  @Override
111  public int hashCode() {
112    return Objects.hash(parent, moduleName);
113  }
114
115  @Override
116  public String toString() {
117    return new ToStringBuilder(this)
118        .attribute("parent", parent)
119        .attribute("moduleName", moduleName)
120        .toString();
121  }
122}