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 java.io.File; 22 import java.nio.file.Path; 23 import java.util.Objects; 24 import java.util.Optional; 25 import java.util.StringJoiner; 26 27 import org.apache.maven.api.annotations.Experimental; 28 import org.apache.maven.api.annotations.Nonnull; 29 30 /** 31 * The option of a Java command-line tool where to place the paths to some dependencies. 32 * A {@code PathType} can identify the class-path, the module-path, the patches for a specific module, 33 * or another kind of path. 34 * 35 * <p>One path type is handled in a special way: unlike other options, 36 * the paths specified in a {@code --patch-module} Java option is effective only for a specified module. 37 * This type is created by calls to {@link #patchModule(String)} and a new instance must be created for 38 * every module to patch.</p> 39 * 40 * <p>Path types are often exclusive. For example, a dependency should not be both on the Java class-path 41 * and on the Java module-path.</p> 42 * 43 * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths() 44 * 45 * @since 4.0.0 46 */ 47 @Experimental 48 public enum JavaPathType implements PathType { 49 /** 50 * The path identified by the Java {@code --class-path} option. 51 * Used for compilation, execution and Javadoc among others. 52 * 53 * <p><b>Context-sensitive interpretation:</b> 54 * A dependency with this path type will not necessarily be placed on the class-path. 55 * There are two circumstances where the dependency may nevertheless be placed somewhere else: 56 * </p> 57 * <ul> 58 * <li>If {@link #MODULES} path type is also set, then the dependency can be placed either on the 59 * class-path or on the module-path, but only one of those. The choice is up to the plugin, 60 * possibly using heuristic rules (Maven 3 behavior).</li> 61 * <li>If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path, 62 * then the test dependency will be placed on the Java {@code --patch-module} option instead of the 63 * class-path.</li> 64 * </ul> 65 */ 66 CLASSES("--class-path"), 67 68 /** 69 * The path identified by the Java {@code --module-path} option. 70 * Used for compilation, execution and Javadoc among others. 71 * 72 * <p><b>Context-sensitive interpretation:</b> 73 * A dependency with this flag will not necessarily be placed on the module-path. 74 * There are two circumstances where the dependency may nevertheless be placed somewhere else: 75 * </p> 76 * <ul> 77 * <li>If {@link #CLASSES} path type is also set, then the dependency <em>should</em> be placed on the 78 * module-path, but is also compatible with placement on the class-path. Compatibility can 79 * be achieved, for example, by repeating in the {@code META-INF/services/} directory the services 80 * that are declared in the {@code module-info.class} file. In that case, the path type can be chosen 81 * by the plugin.</li> 82 * <li>If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path, 83 * then the test dependency will be placed on the Java {@code --patch-module} option instead of the 84 * {@code --module-path} option.</li> 85 * </ul> 86 */ 87 MODULES("--module-path"), 88 89 /** 90 * The path identified by the Java {@code --upgrade-module-path} option. 91 */ 92 UPGRADE_MODULES("--upgrade-module-path"), 93 94 /** 95 * The path identified by the Java {@code --patch-module} option. 96 * Note that this option is incomplete, because it must be followed by a module name. 97 * Use this type only when the module to patch is unknown. 98 * 99 * @see #patchModule(String) 100 */ 101 PATCH_MODULE("--patch-module"), 102 103 /** 104 * The path identified by the Java {@code --processor-path} option. 105 */ 106 PROCESSOR_CLASSES("--processor-path"), 107 108 /** 109 * The path identified by the Java {@code --processor-module-path} option. 110 */ 111 PROCESSOR_MODULES("--processor-module-path"), 112 113 /** 114 * The path identified by the Java {@code -agentpath} option. 115 */ 116 AGENT("-agentpath"), 117 118 /** 119 * The path identified by the Javadoc {@code -doclet} option. 120 */ 121 DOCLET("-doclet"), 122 123 /** 124 * The path identified by the Javadoc {@code -tagletpath} option. 125 */ 126 TAGLETS("-tagletpath"); 127 128 /** 129 * Creates a path identified by the Java {@code --patch-module} option. 130 * Contrarily to the other types of paths, this path is applied to only 131 * one specific module. Used for compilation and execution among others. 132 * 133 * <p><b>Context-sensitive interpretation:</b> 134 * This path type makes sense only when a main module is added on the module-path by another dependency. 135 * In no main module is found, the patch dependency may be added on the class-path or module-path 136 * depending on whether {@link #CLASSES} or {@link #MODULES} is present. 137 * </p> 138 * 139 * @param moduleName name of the module on which to apply the path 140 * @return an identification of the patch-module path for the given module. 141 * 142 * @see Modular#moduleName() 143 */ 144 @Nonnull 145 public static Modular patchModule(@Nonnull String moduleName) { 146 return PATCH_MODULE.new Modular(moduleName); 147 } 148 149 /** 150 * The tools option for this path, or {@code null} if none. 151 * 152 * @see #option() 153 */ 154 private final String option; 155 156 /** 157 * Creates a new enumeration value for a path associated to the given tool option. 158 * 159 * @param option the Java tools option for this path, or {@code null} if none 160 */ 161 JavaPathType(String option) { 162 this.option = option; 163 } 164 165 @Override 166 public String id() { 167 return name(); 168 } 169 170 /** 171 * Returns the name of the tool option for this path. For example, if this path type 172 * is {@link #MODULES}, then this method returns {@code "--module-path"}. The option 173 * does not include the {@linkplain Modular#moduleName() module name} on which it applies. 174 * 175 * @return the name of the tool option for this path type 176 */ 177 @Nonnull 178 @Override 179 public Optional<String> option() { 180 return Optional.ofNullable(option); 181 } 182 183 /** 184 * Returns the option followed by a string representation of the given path elements. 185 * For example, if this type is {@link #MODULES}, then the option is {@code "--module-path"} 186 * followed by the specified path elements. 187 * 188 * @param paths the path to format as a tool option 189 * @return the option associated to this path type followed by the given path elements, 190 * or an empty string if there is no path element 191 * @throws IllegalStateException if no option is associated to this path type 192 */ 193 @Nonnull 194 @Override 195 public String option(Iterable<? extends Path> paths) { 196 return format(null, paths); 197 } 198 199 /** 200 * Implementation shared with {@link Modular}. 201 */ 202 String format(String moduleName, Iterable<? extends Path> paths) { 203 if (option == null) { 204 throw new IllegalStateException("No option is associated to this path type."); 205 } 206 String prefix = (moduleName == null) ? (option + ' ') : (option + ' ' + moduleName + '='); 207 StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, ""); 208 joiner.setEmptyValue(""); 209 for (Path p : paths) { 210 joiner.add(p.toString()); 211 } 212 return joiner.toString(); 213 } 214 215 @Override 216 public String toString() { 217 return "PathType[" + id() + "]"; 218 } 219 220 /** 221 * Type of path which is applied to only one specific Java module. 222 * The main case is the Java {@code --patch-module} option. 223 * 224 * @see #PATCH_MODULE 225 * @see #patchModule(String) 226 */ 227 public final class Modular implements PathType { 228 /** 229 * Name of the module for which a path is specified. 230 */ 231 @Nonnull 232 private final String moduleName; 233 234 /** 235 * Creates a new path type for the specified module. 236 * 237 * @param moduleName name of the module for which a path is specified 238 */ 239 private Modular(@Nonnull String moduleName) { 240 this.moduleName = Objects.requireNonNull(moduleName); 241 } 242 243 @Override 244 public String id() { 245 return JavaPathType.this.name() + ":" + moduleName; 246 } 247 248 /** 249 * Returns the type of path without indication about the target module. 250 * This is usually {@link #PATCH_MODULE}. 251 * 252 * @return type of path without indication about the target module 253 */ 254 @Nonnull 255 public JavaPathType rawType() { 256 return JavaPathType.this; 257 } 258 259 /** 260 * Returns the name of the tool option for this path, not including the module name. 261 * 262 * @return name of the tool option for this path, not including the module name 263 */ 264 @Nonnull 265 public String name() { 266 return JavaPathType.this.name(); 267 } 268 269 /** 270 * Returns the name of the module for which a path is specified 271 * 272 * @return name of the module for which a path is specified 273 */ 274 @Nonnull 275 public String moduleName() { 276 return moduleName; 277 } 278 279 /** 280 * Returns the name of the tool option for this path. 281 * The option does not include the {@linkplain #moduleName() module name} on which it applies. 282 * 283 * @return the name of the tool option for this path type 284 */ 285 @Nonnull 286 @Override 287 public Optional<String> option() { 288 return JavaPathType.this.option(); 289 } 290 291 /** 292 * Returns the option followed by a string representation of the given path elements. 293 * The path elements are separated by an option-specific or platform-specific separator. 294 * If the given {@code paths} argument contains no element, then this method returns an empty string. 295 * 296 * @param paths the path to format as a string 297 * @return the option associated to this path type followed by the given path elements, 298 * or an empty string if there is no path element. 299 */ 300 @Nonnull 301 @Override 302 public String option(Iterable<? extends Path> paths) { 303 return format(moduleName, paths); 304 } 305 306 /** 307 * Returns the programmatic name of this path type, including the module to patch. 308 * For example, if this type was created by {@code JavaPathType.patchModule("foo.bar")}, 309 * then this method returns {@code "PathType[PATCH_MODULE:foo.bar]")}. 310 * 311 * @return the programmatic name together with the module name on which it applies 312 */ 313 @Nonnull 314 @Override 315 public String toString() { 316 return "PathType[" + id() + "]"; 317 } 318 } 319 }