001/* 002 * Copyright (C) 2022 - 2024, 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 java.util.Optional; 022import javax.tools.JavaFileManager.Location; 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 */ 031public final class ModuleLocation implements Location { 032 033 private final Location parent; 034 private final String moduleName; 035 private final String name; 036 037 /** 038 * Initialize the location. 039 * 040 * @param parent the parent location. 041 * @param moduleName the module name. 042 * @throws JctIllegalInputException if the parent location is not an output location or a 043 * module-oriented location. 044 */ 045 public ModuleLocation(Location parent, String moduleName) { 046 Objects.requireNonNull(parent, "parent"); 047 Objects.requireNonNull(moduleName, "moduleName"); 048 049 if (!parent.isOutputLocation() && !parent.isModuleOrientedLocation()) { 050 throw new JctIllegalInputException( 051 "The parent of a module location must be either an output location " 052 + "or be module-oriented, but got " 053 + parent.getName() 054 ); 055 } 056 057 this.parent = parent; 058 this.moduleName = moduleName; 059 name = parent.getName() + "[" + moduleName + "]"; 060 } 061 062 /** 063 * Get the parent location. 064 * 065 * @return the parent location. 066 */ 067 public Location getParent() { 068 return parent; 069 } 070 071 /** 072 * Get the module name. 073 * 074 * @return the module name. 075 */ 076 public String getModuleName() { 077 return moduleName; 078 } 079 080 @Override 081 public String getName() { 082 return name; 083 } 084 085 @Override 086 public boolean isOutputLocation() { 087 return parent.isOutputLocation(); 088 } 089 090 @Override 091 public boolean isModuleOrientedLocation() { 092 // Module locations cannot be module-oriented, but their parents can be. 093 return false; 094 } 095 096 @Override 097 public boolean equals(@Nullable Object other) { 098 if (!(other instanceof ModuleLocation)) { 099 return false; 100 } 101 102 var that = (ModuleLocation) other; 103 104 return parent.equals(that.parent) 105 && moduleName.equals(that.moduleName); 106 } 107 108 @Override 109 public int hashCode() { 110 return Objects.hash(parent, moduleName); 111 } 112 113 @Override 114 public String toString() { 115 return new ToStringBuilder(this) 116 .attribute("parent", parent) 117 .attribute("moduleName", moduleName) 118 .toString(); 119 } 120 121 /** 122 * Attempt to upcast a given location to a ModuleLocation if it is an instance of 123 * ModuleLocation. 124 * 125 * @param location the location to attempt to upcast. 126 * @return an optional containing the upcast location if it is a module location, 127 * or an empty optional if not. 128 * @since 1.1.5 129 */ 130 public static Optional<ModuleLocation> upcast(Location location) { 131 return Optional.of(location) 132 .filter(ModuleLocation.class::isInstance) 133 .map(ModuleLocation.class::cast); 134 } 135}