1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.maven.api; 20 21 import javax.tools.DocumentationTool; 22 import javax.tools.JavaFileManager; 23 import javax.tools.StandardLocation; 24 25 import java.io.File; 26 import java.nio.file.Path; 27 import java.util.Objects; 28 import java.util.Optional; 29 import java.util.StringJoiner; 30 31 import org.apache.maven.api.annotations.Experimental; 32 import org.apache.maven.api.annotations.Nonnull; 33 34 /** 35 * The option of a Java command-line tool where to place the paths to some dependencies. 36 * A {@code PathType} can identify the class-path, the module-path, the patches for a specific module, 37 * or another kind of path. 38 * 39 * <p>One path type is handled in a special way: unlike other options, 40 * the paths specified in a {@code --patch-module} Java option is effective only for a specified module. 41 * This type is created by calls to {@link #patchModule(String)} and a new instance must be created for 42 * every module to patch.</p> 43 * 44 * <p>Path types are often exclusive. For example, a dependency should not be both on the Java class-path 45 * and on the Java module-path.</p> 46 * 47 * <h2>Relationship with Java compiler standard location</h2> 48 * This enumeration is closely related to the {@link JavaFileManager.Location} enumerations. 49 * A difference is that the latter enumerates input and output files, while {@code JavaPathType} 50 * enumerates only input dependencies. Another difference is that {@code JavaPathType} contains 51 * some enumeration values used only at runtime and therefore not available in {@code javax.tool}, 52 * such as agent paths. 53 * 54 * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths() 55 * 56 * @since 4.0.0 57 */ 58 @Experimental 59 public enum JavaPathType implements PathType { 60 /** 61 * The path identified by the Java {@code --class-path} option. 62 * Used for compilation, execution and Javadoc among others. 63 * The Java tools location is {@link StandardLocation#CLASS_PATH}. 64 * 65 * <h4>Context-sensitive interpretation</h4> 66 * A dependency with this path type will not necessarily be placed on the class-path. 67 * There are two circumstances where the dependency may nevertheless be placed somewhere else: 68 * 69 * <ul> 70 * <li>If {@link #MODULES} path type is also set, then the dependency can be placed either on the 71 * class-path or on the module-path, but only one of those. The choice is up to the plugin, 72 * possibly using heuristic rules (Maven 3 behavior).</li> 73 * <li>If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path, 74 * then the test dependency will be placed on the Java {@code --patch-module} option instead of the 75 * class-path.</li> 76 * </ul> 77 */ 78 CLASSES(StandardLocation.CLASS_PATH, "--class-path"), 79 80 /** 81 * The path identified by the Java {@code --module-path} option. 82 * Used for compilation, execution and Javadoc among others. 83 * The Java tools location is {@link StandardLocation#MODULE_PATH}. 84 * 85 * <h4>Context-sensitive interpretation</h4> 86 * A dependency with this flag will not necessarily be placed on the module-path. 87 * There are two circumstances where the dependency may nevertheless be placed somewhere else: 88 * 89 * <ul> 90 * <li>If {@link #CLASSES} path type is also set, then the dependency <em>should</em> be placed on the 91 * module-path, but is also compatible with placement on the class-path. Compatibility can 92 * be achieved, for example, by repeating in the {@code META-INF/services/} directory the services 93 * that are declared in the {@code module-info.class} file. In that case, the path type can be chosen 94 * by the plugin.</li> 95 * <li>If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path, 96 * then the test dependency will be placed on the Java {@code --patch-module} option instead of the 97 * {@code --module-path} option.</li> 98 * </ul> 99 */ 100 MODULES(StandardLocation.MODULE_PATH, "--module-path"), 101 102 /** 103 * The path identified by the Java {@code --upgrade-module-path} option. 104 * The Java tools location is {@link StandardLocation#UPGRADE_MODULE_PATH}. 105 */ 106 UPGRADE_MODULES(StandardLocation.UPGRADE_MODULE_PATH, "--upgrade-module-path"), 107 108 /** 109 * The path identified by the Java {@code --patch-module} option. 110 * The Java tools location is {@link StandardLocation#PATCH_MODULE_PATH}. 111 * 112 * Note that this option is incomplete, because it must be followed by a module name. 113 * Use this type only when the module to patch is unknown. 114 * 115 * @see #patchModule(String) 116 */ 117 PATCH_MODULE(StandardLocation.PATCH_MODULE_PATH, "--patch-module"), 118 119 /** 120 * The path identified by the Java {@code --processor-path} option. 121 * The Java tools location is {@link StandardLocation#ANNOTATION_PROCESSOR_PATH}. 122 */ 123 PROCESSOR_CLASSES(StandardLocation.ANNOTATION_PROCESSOR_PATH, "--processor-path"), 124 125 /** 126 * The path identified by the Java {@code --processor-module-path} option. 127 * The Java tools location is {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH}. 128 */ 129 PROCESSOR_MODULES(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, "--processor-module-path"), 130 131 /** 132 * The path identified by the Java {@code -agentpath} option. 133 */ 134 AGENT(null, "-agentpath"), 135 136 /** 137 * The path identified by the Javadoc {@code -doclet} option. 138 * The Java tools location is {@link DocumentationTool.Location#DOCLET_PATH}. 139 */ 140 DOCLET(DocumentationTool.Location.DOCLET_PATH, "-doclet"), 141 142 /** 143 * The path identified by the Javadoc {@code -tagletpath} option. 144 * The Java tools location is {@link DocumentationTool.Location#TAGLET_PATH}. 145 */ 146 TAGLETS(DocumentationTool.Location.TAGLET_PATH, "-tagletpath"); 147 148 /** 149 * Creates a path identified by the Java {@code --patch-module} option. 150 * Contrarily to the other types of paths, this path is applied to only 151 * one specific module. Used for compilation and execution among others. 152 * 153 * <h4>Context-sensitive interpretation</h4> 154 * This path type makes sense only when a main module is added on the module-path by another dependency. 155 * In no main module is found, the patch dependency may be added on the class-path or module-path 156 * depending on whether {@link #CLASSES} or {@link #MODULES} is present. 157 * 158 * @param moduleName name of the module on which to apply the path 159 * @return an identification of the patch-module path for the given module. 160 * 161 * @see Modular#moduleName() 162 */ 163 @Nonnull 164 public static Modular patchModule(@Nonnull String moduleName) { 165 return PATCH_MODULE.new Modular(moduleName); 166 } 167 168 /** 169 * The {@code javax.tool} enumeration value corresponding to this {@code JavaPathType}, or {@code null} if none. 170 * 171 * @see #location() 172 */ 173 private final JavaFileManager.Location location; 174 175 /** 176 * The tools option for this path, or {@code null} if none. 177 * 178 * @see #option() 179 */ 180 private final String option; 181 182 /** 183 * Creates a new enumeration value for a path associated to the given tool option. 184 * 185 * @param location the {@code javax.tool} enumeration value, or {@code null} if none. 186 * @param option the Java tools option for this path, or {@code null} if none 187 */ 188 JavaPathType(JavaFileManager.Location location, String option) { 189 this.location = location; 190 this.option = option; 191 } 192 193 /** 194 * Returns the unique name of this path type. 195 * 196 * @return the programmatic name of this enumeration value 197 */ 198 @Override 199 public String id() { 200 return name(); 201 } 202 203 /** 204 * Returns the identification of this path in the {@code javax.tool} API. 205 * The value may be an instance of {@link StandardLocation} or {@link DocumentationTool.Location}, 206 * depending which tool will use this location. 207 * 208 * @return the {@code javax.tool} enumeration value corresponding to this {@code JavaPathType} 209 */ 210 public Optional<JavaFileManager.Location> location() { 211 return Optional.ofNullable(location); 212 } 213 214 /** 215 * Returns the path type associated to the given {@code javax.tool} location. 216 * This method is the converse of {@link #location()}. 217 * 218 * @param location identification of a path in the {@code javax.tool} API 219 * @return Java path type associated to the given location 220 */ 221 public static Optional<JavaPathType> valueOf(JavaFileManager.Location location) { 222 for (JavaPathType type : JavaPathType.values()) { 223 if (location.equals(type.location)) { 224 return Optional.of(type); 225 } 226 } 227 return Optional.empty(); 228 } 229 230 /** 231 * Returns the name of the tool option for this path. For example, if this path type 232 * is {@link #MODULES}, then this method returns {@code "--module-path"}. The option 233 * does not include the {@linkplain Modular#moduleName() module name} on which it applies. 234 * 235 * @return the name of the tool option for this path type 236 */ 237 @Nonnull 238 @Override 239 public Optional<String> option() { 240 return Optional.ofNullable(option); 241 } 242 243 /** 244 * Returns the option followed by a string representation of the given path elements. 245 * For example, if this type is {@link #MODULES}, then the option is {@code "--module-path"} 246 * followed by the specified path elements. 247 * 248 * @param paths the path to format as a tool option 249 * @return the option associated to this path type followed by the given path elements, 250 * or an empty array if there is no path element 251 * @throws IllegalStateException if no option is associated to this path type 252 */ 253 @Nonnull 254 @Override 255 public String[] option(Iterable<? extends Path> paths) { 256 return format(null, paths); 257 } 258 259 /** 260 * Implementation shared with {@link Modular}. 261 */ 262 final String[] format(String moduleName, Iterable<? extends Path> paths) { 263 if (option == null) { 264 throw new IllegalStateException("No option is associated to this path type."); 265 } 266 String prefix = (moduleName == null) ? "" : (moduleName + '='); 267 StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, ""); 268 joiner.setEmptyValue(""); 269 for (Path p : paths) { 270 joiner.add(p.toString()); 271 } 272 String value = joiner.toString(); 273 if (value.isEmpty()) { 274 return new String[0]; 275 } 276 return new String[] {option, value}; 277 } 278 279 /** 280 * {@return a string representation of this path type for debugging purposes}. 281 */ 282 @Override 283 public String toString() { 284 return "PathType[" + id() + "]"; 285 } 286 287 /** 288 * Type of path which is applied to only one specific Java module. 289 * The main case is the Java {@code --patch-module} option. 290 * 291 * @see #PATCH_MODULE 292 * @see #patchModule(String) 293 */ 294 public final class Modular implements PathType { 295 /** 296 * Name of the module for which a path is specified. 297 */ 298 @Nonnull 299 private final String moduleName; 300 301 /** 302 * Creates a new path type for the specified module. 303 * 304 * @param moduleName name of the module for which a path is specified 305 */ 306 private Modular(@Nonnull String moduleName) { 307 this.moduleName = Objects.requireNonNull(moduleName); 308 } 309 310 /** 311 * Returns the type of path without indication about the target module. 312 * This is usually {@link #PATCH_MODULE}. 313 * 314 * @return type of path without indication about the target module 315 */ 316 @Nonnull 317 public JavaPathType rawType() { 318 return JavaPathType.this; 319 } 320 321 /** 322 * Returns the name of the tool option for this path, including the module name. 323 * 324 * @return name of the tool option for this path, including the module name 325 */ 326 @Override 327 public String id() { 328 return JavaPathType.this.name() + ":" + moduleName; 329 } 330 331 /** 332 * Returns the name of the tool option for this path, not including the module name. 333 * 334 * @return name of the tool option for this path, not including the module name 335 */ 336 @Nonnull 337 @Override 338 public String name() { 339 return JavaPathType.this.name(); 340 } 341 342 /** 343 * Returns the name of the module for which a path is specified 344 * 345 * @return name of the module for which a path is specified 346 */ 347 @Nonnull 348 public String moduleName() { 349 return moduleName; 350 } 351 352 /** 353 * Returns the name of the tool option for this path. 354 * The option does not include the {@linkplain #moduleName() module name} on which it applies. 355 * 356 * @return the name of the tool option for this path type 357 */ 358 @Nonnull 359 @Override 360 public Optional<String> option() { 361 return JavaPathType.this.option(); 362 } 363 364 /** 365 * Returns the option followed by a string representation of the given path elements. 366 * The path elements are separated by an option-specific or platform-specific separator. 367 * If the given {@code paths} argument contains no element, then this method returns an empty string. 368 * 369 * @param paths the path to format as a string 370 * @return the option associated to this path type followed by the given path elements, 371 * or an empty array if there is no path element. 372 */ 373 @Nonnull 374 @Override 375 public String[] option(Iterable<? extends Path> paths) { 376 return format(moduleName, paths); 377 } 378 379 /** 380 * Returns the programmatic name of this path type, including the module to patch. 381 * For example, if this type was created by {@code JavaPathType.patchModule("foo.bar")}, 382 * then this method returns {@code "PathType[PATCH_MODULE:foo.bar]")}. 383 * 384 * @return the programmatic name together with the module name on which it applies 385 */ 386 @Nonnull 387 @Override 388 public String toString() { 389 return "PathType[" + id() + "]"; 390 } 391 } 392 }