View Javadoc
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 }