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