diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java
index efad1415adf8..d0906581b62f 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Dependency.java
@@ -30,16 +30,8 @@ public interface Dependency extends Artifact {
@Nonnull
Type getType();
- /**
- * The dependency properties.
- *
- * @return the dependency properties, never {@code null}
- */
- @Nonnull
- DependencyProperties getDependencyProperties();
-
@Nonnull
- Scope getScope();
+ DependencyScope getScope();
boolean isOptional();
@@ -49,5 +41,6 @@ public interface Dependency extends Artifact {
* @return a {@link DependencyCoordinate}
*/
@Nonnull
+ @Override
DependencyCoordinate toCoordinate();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinate.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinate.java
index 7ba4bd1ec75e..c0295bfaa0e4 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinate.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyCoordinate.java
@@ -41,7 +41,7 @@ public interface DependencyCoordinate extends ArtifactCoordinate {
Type getType();
@Nonnull
- Scope getScope();
+ DependencyScope getScope();
@Nullable
Boolean getOptional();
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java
new file mode 100644
index 000000000000..2951acb1b44c
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyScope.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Dependency scope.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Immutable
+public enum DependencyScope {
+
+ /**
+ * None. Allows you to declare dependencies (for example to alter reactor build order) but in reality dependencies
+ * in this scope are not part of any build path scope.
+ */
+ NONE("none", false),
+
+ /**
+ * Empty scope.
+ */
+ EMPTY("", false),
+
+ /**
+ * Compile only.
+ */
+ COMPILE_ONLY("compile-only", false),
+
+ /**
+ * Compile.
+ */
+ COMPILE("compile", true),
+
+ /**
+ * Runtime.
+ */
+ RUNTIME("runtime", true),
+
+ /**
+ * Provided.
+ */
+ PROVIDED("provided", false),
+
+ /**
+ * Test compile only.
+ */
+ TEST_ONLY("test-only", false),
+
+ /**
+ * Test.
+ */
+ TEST("test", false),
+
+ /**
+ * Test runtime.
+ */
+ TEST_RUNTIME("test-runtime", true),
+
+ /**
+ * System scope.
+ *
+ * Important: this scope {@code id} MUST BE KEPT in sync with label in
+ * {@code org.eclipse.aether.util.artifact.Scopes#SYSTEM}.
+ */
+ SYSTEM("system", false);
+
+ private static final Map IDS = Collections.unmodifiableMap(
+ Stream.of(DependencyScope.values()).collect(Collectors.toMap(s -> s.id, s -> s)));
+
+ public static DependencyScope forId(String id) {
+ return IDS.get(id);
+ }
+
+ private final String id;
+ private final boolean transitive;
+
+ DependencyScope(String id, boolean transitive) {
+ this.id = id;
+ this.transitive = transitive;
+ }
+
+ /**
+ * The {@code id} uniquely represents a value for this extensible enum.
+ * This id should be used to compute the equality and hash code for the instance.
+ *
+ * @return the id
+ */
+ @Nonnull
+ public String id() {
+ return id;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java
new file mode 100644
index 000000000000..5e29a498c847
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnum.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Interface that defines some kind of enums that can be extended by Maven plugins or extensions.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
+ */
+public interface ExtensibleEnum {
+
+ /**
+ * The {@code id} uniquely represents a value for this extensible enum.
+ * This id should be used to compute the equality and hash code for the instance.
+ *
+ * @return the id
+ */
+ @Nonnull
+ String id();
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java
new file mode 100644
index 000000000000..7843cbef7066
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ExtensibleEnums.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.util.*;
+
+abstract class ExtensibleEnums {
+
+ static Language language(String id) {
+ return new DefaultLanguage(id);
+ }
+
+ static PathScope pathScope(String id, ProjectScope projectScope, DependencyScope... dependencyScopes) {
+ return new DefaultPathScope(id, projectScope, dependencyScopes);
+ }
+
+ static ProjectScope projectScope(String id) {
+ return new DefaultProjectScope(id);
+ }
+
+ private static class DefaultExtensibleEnum implements ExtensibleEnum {
+
+ private final String id;
+
+ DefaultExtensibleEnum(String id) {
+ this.id = Objects.requireNonNull(id);
+ }
+
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public int hashCode() {
+ return id().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj != null && getClass() == obj.getClass() && id().equals(((DefaultExtensibleEnum) obj).id());
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + id() + "]";
+ }
+ }
+
+ private static class DefaultPathScope extends DefaultExtensibleEnum implements PathScope {
+ private final ProjectScope projectScope;
+ private final Set dependencyScopes;
+
+ DefaultPathScope(String id, ProjectScope projectScope, DependencyScope... dependencyScopes) {
+ super(id);
+ this.projectScope = Objects.requireNonNull(projectScope);
+ this.dependencyScopes =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(Objects.requireNonNull(dependencyScopes))));
+ }
+
+ @Override
+ public ProjectScope projectScope() {
+ return projectScope;
+ }
+
+ @Override
+ public Set dependencyScopes() {
+ return dependencyScopes;
+ }
+ }
+
+ private static class DefaultProjectScope extends DefaultExtensibleEnum implements ProjectScope {
+
+ DefaultProjectScope(String id) {
+ super(id);
+ }
+ }
+
+ private static class DefaultLanguage extends DefaultExtensibleEnum implements Language {
+
+ DefaultLanguage(String id) {
+ super(id);
+ }
+ }
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java
new file mode 100644
index 000000000000..a34b686c8c2c
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/JavaPathType.java
@@ -0,0 +1,319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.StringJoiner;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * The option of a Java command-line tool where to place the paths to some dependencies.
+ * A {@code PathType} can identify the class-path, the module-path, the patches for a specific module,
+ * or another kind of path.
+ *
+ * One path type is handled in a special way: unlike other options,
+ * the paths specified in a {@code --patch-module} Java option is effective only for a specified module.
+ * This type is created by calls to {@link #patchModule(String)} and a new instance must be created for
+ * every module to patch.
+ *
+ * Path types are often exclusive. For example, a dependency should not be both on the Java class-path
+ * and on the Java module-path.
+ *
+ * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths()
+ *
+ * @since 4.0.0
+ */
+@Experimental
+public enum JavaPathType implements PathType {
+ /**
+ * The path identified by the Java {@code --class-path} option.
+ * Used for compilation, execution and Javadoc among others.
+ *
+ * Context-sensitive interpretation:
+ * A dependency with this path type will not necessarily be placed on the class-path.
+ * There are two circumstances where the dependency may nevertheless be placed somewhere else:
+ *
+ *
+ * - If {@link #MODULES} path type is also set, then the dependency can be placed either on the
+ * class-path or on the module-path, but only one of those. The choice is up to the plugin,
+ * possibly using heuristic rules (Maven 3 behavior).
+ * - If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path,
+ * then the test dependency will be placed on the Java {@code --patch-module} option instead of the
+ * class-path.
+ *
+ */
+ CLASSES("--class-path"),
+
+ /**
+ * The path identified by the Java {@code --module-path} option.
+ * Used for compilation, execution and Javadoc among others.
+ *
+ * Context-sensitive interpretation:
+ * A dependency with this flag will not necessarily be placed on the module-path.
+ * There are two circumstances where the dependency may nevertheless be placed somewhere else:
+ *
+ *
+ * - If {@link #CLASSES} path type is also set, then the dependency should be placed on the
+ * module-path, but is also compatible with placement on the class-path. Compatibility can
+ * be achieved, for example, by repeating in the {@code META-INF/services/} directory the services
+ * that are declared in the {@code module-info.class} file. In that case, the path type can be chosen
+ * by the plugin.
+ * - If a {@link #patchModule(String)} is also set and the main JAR file is placed on the module-path,
+ * then the test dependency will be placed on the Java {@code --patch-module} option instead of the
+ * {@code --module-path} option.
+ *
+ */
+ MODULES("--module-path"),
+
+ /**
+ * The path identified by the Java {@code --upgrade-module-path} option.
+ */
+ UPGRADE_MODULES("--upgrade-module-path"),
+
+ /**
+ * The path identified by the Java {@code --patch-module} option.
+ * Note that this option is incomplete, because it must be followed by a module name.
+ * Use this type only when the module to patch is unknown.
+ *
+ * @see #patchModule(String)
+ */
+ PATCH_MODULE("--patch-module"),
+
+ /**
+ * The path identified by the Java {@code --processor-path} option.
+ */
+ PROCESSOR_CLASSES("--processor-path"),
+
+ /**
+ * The path identified by the Java {@code --processor-module-path} option.
+ */
+ PROCESSOR_MODULES("--processor-module-path"),
+
+ /**
+ * The path identified by the Java {@code -agentpath} option.
+ */
+ AGENT("-agentpath"),
+
+ /**
+ * The path identified by the Javadoc {@code -doclet} option.
+ */
+ DOCLET("-doclet"),
+
+ /**
+ * The path identified by the Javadoc {@code -tagletpath} option.
+ */
+ TAGLETS("-tagletpath");
+
+ /**
+ * Creates a path identified by the Java {@code --patch-module} option.
+ * Contrarily to the other types of paths, this path is applied to only
+ * one specific module. Used for compilation and execution among others.
+ *
+ * Context-sensitive interpretation:
+ * This path type makes sense only when a main module is added on the module-path by another dependency.
+ * In no main module is found, the patch dependency may be added on the class-path or module-path
+ * depending on whether {@link #CLASSES} or {@link #MODULES} is present.
+ *
+ *
+ * @param moduleName name of the module on which to apply the path
+ * @return an identification of the patch-module path for the given module.
+ *
+ * @see Modular#moduleName()
+ */
+ @Nonnull
+ public static Modular patchModule(@Nonnull String moduleName) {
+ return PATCH_MODULE.new Modular(moduleName);
+ }
+
+ /**
+ * The tools option for this path, or {@code null} if none.
+ *
+ * @see #option()
+ */
+ private final String option;
+
+ /**
+ * Creates a new enumeration value for a path associated to the given tool option.
+ *
+ * @param option the Java tools option for this path, or {@code null} if none
+ */
+ JavaPathType(String option) {
+ this.option = option;
+ }
+
+ @Override
+ public String id() {
+ return name();
+ }
+
+ /**
+ * Returns the name of the tool option for this path. For example, if this path type
+ * is {@link #MODULES}, then this method returns {@code "--module-path"}. The option
+ * does not include the {@linkplain Modular#moduleName() module name} on which it applies.
+ *
+ * @return the name of the tool option for this path type
+ */
+ @Nonnull
+ @Override
+ public Optional option() {
+ return Optional.ofNullable(option);
+ }
+
+ /**
+ * Returns the option followed by a string representation of the given path elements.
+ * For example, if this type is {@link #MODULES}, then the option is {@code "--module-path"}
+ * followed by the specified path elements.
+ *
+ * @param paths the path to format as a tool option
+ * @return the option associated to this path type followed by the given path elements,
+ * or an empty string if there is no path element
+ * @throws IllegalStateException if no option is associated to this path type
+ */
+ @Nonnull
+ @Override
+ public String option(final Iterable extends Path> paths) {
+ return format(null, paths);
+ }
+
+ /**
+ * Implementation shared with {@link Modular}.
+ */
+ final String format(final String moduleName, final Iterable extends Path> paths) {
+ if (option == null) {
+ throw new IllegalStateException("No option is associated to this path type.");
+ }
+ String prefix = (moduleName == null) ? (option + ' ') : (option + ' ' + moduleName + '=');
+ StringJoiner joiner = new StringJoiner(File.pathSeparator, prefix, "");
+ joiner.setEmptyValue("");
+ for (Path p : paths) {
+ joiner.add(p.toString());
+ }
+ return joiner.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "PathType[" + id() + "]";
+ }
+
+ /**
+ * Type of path which is applied to only one specific Java module.
+ * The main case is the Java {@code --patch-module} option.
+ *
+ * @see #PATCH_MODULE
+ * @see #patchModule(String)
+ */
+ public final class Modular implements PathType {
+ /**
+ * Name of the module for which a path is specified.
+ */
+ @Nonnull
+ private final String moduleName;
+
+ /**
+ * Creates a new path type for the specified module.
+ *
+ * @param moduleName name of the module for which a path is specified
+ */
+ private Modular(@Nonnull String moduleName) {
+ this.moduleName = Objects.requireNonNull(moduleName);
+ }
+
+ @Override
+ public String id() {
+ return JavaPathType.this.name() + ":" + moduleName;
+ }
+
+ /**
+ * Returns the type of path without indication about the target module.
+ * This is usually {@link #PATCH_MODULE}.
+ *
+ * @return type of path without indication about the target module
+ */
+ @Nonnull
+ public JavaPathType rawType() {
+ return JavaPathType.this;
+ }
+
+ /**
+ * Returns the name of the tool option for this path, not including the module name.
+ *
+ * @return name of the tool option for this path, not including the module name
+ */
+ @Nonnull
+ public String name() {
+ return JavaPathType.this.name();
+ }
+
+ /**
+ * Returns the name of the module for which a path is specified
+ *
+ * @return name of the module for which a path is specified
+ */
+ @Nonnull
+ public String moduleName() {
+ return moduleName;
+ }
+
+ /**
+ * Returns the name of the tool option for this path.
+ * The option does not include the {@linkplain #moduleName() module name} on which it applies.
+ *
+ * @return the name of the tool option for this path type
+ */
+ @Nonnull
+ @Override
+ public Optional option() {
+ return JavaPathType.this.option();
+ }
+
+ /**
+ * Returns the option followed by a string representation of the given path elements.
+ * The path elements are separated by an option-specific or platform-specific separator.
+ * If the given {@code paths} argument contains no element, then this method returns an empty string.
+ *
+ * @param paths the path to format as a string
+ * @return the option associated to this path type followed by the given path elements,
+ * or an empty string if there is no path element.
+ */
+ @Nonnull
+ @Override
+ public String option(Iterable extends Path> paths) {
+ return format(moduleName, paths);
+ }
+
+ /**
+ * Returns the programmatic name of this path type, including the module to patch.
+ * For example, if this type was created by {@code JavaPathType.patchModule("foo.bar")},
+ * then this method returns {@code "PathType[PATCH_MODULE:foo.bar]")}.
+ *
+ * @return the programmatic name together with the module name on which it applies
+ */
+ @Nonnull
+ @Override
+ public String toString() {
+ return "PathType[" + id() + "]";
+ }
+ }
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java
new file mode 100644
index 000000000000..0fc459287b57
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Language.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+
+import static org.apache.maven.api.ExtensibleEnums.language;
+
+/**
+ * Language.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Immutable
+@SuppressWarnings("checkstyle:InterfaceIsType")
+public interface Language extends ExtensibleEnum {
+
+ /**
+ * The "none" language. It is not versioned, family is same to itself, and compatible with itself only.
+ * In turn, every {@link Language} implementation must be compatible with {@code NONE} language.
+ */
+ Language NONE = language("none");
+
+ // TODO: this should be moved out from here to Java Support (builtin into core)
+ Language JAVA_FAMILY = language("java");
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyProperties.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java
similarity index 54%
rename from api/maven-api-core/src/main/java/org/apache/maven/api/DependencyProperties.java
rename to api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java
index 9f634168480b..ede4c46b073f 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/DependencyProperties.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Packaging.java
@@ -18,42 +18,35 @@
*/
package org.apache.maven.api;
-import java.util.Map;
-
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Immutable;
import org.apache.maven.api.annotations.Nonnull;
/**
- * Dependency properties supported by Maven Core.
+ * Interface representing a Maven project packaging.
*
* @since 4.0.0
*/
@Experimental
@Immutable
-public interface DependencyProperties {
- /**
- * Boolean flag telling that dependency contains all of its dependencies. Value of this key should be parsed with
- * {@link Boolean#parseBoolean(String)} to obtain value.
- *
- * Important: this flag must be kept in sync with resolver! (as is used during collection)
- */
- String FLAG_INCLUDES_DEPENDENCIES = "includesDependencies";
-
+public interface Packaging extends ExtensibleEnum {
/**
- * Boolean flag telling that dependency is meant to be placed on class path. Value of this key should be parsed with
- * {@link Boolean#parseBoolean(String)} to obtain value.
+ * The packaging id.
*/
- String FLAG_CLASS_PATH_CONSTITUENT = "classPathConstituent";
+ @Nonnull
+ String id();
/**
- * Returns immutable "map view" of all the properties.
+ * The language of this packaging.
*/
@Nonnull
- Map asMap();
+ default Language language() {
+ return getType().getLanguage();
+ }
/**
- * Returns {@code true} if given flag is {@code true}.
+ * The type of main artifact produced by this packaging.
*/
- boolean checkFlag(@Nonnull String flag);
+ @Nonnull
+ Type getType();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java
new file mode 100644
index 000000000000..7132dfaa7e54
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathScope.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.util.*;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+import org.apache.maven.api.annotations.Nonnull;
+
+import static org.apache.maven.api.ExtensibleEnums.pathScope;
+
+/**
+ * Build path scope.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Immutable
+public interface PathScope extends ExtensibleEnum {
+
+ @Nonnull
+ ProjectScope projectScope();
+
+ @Nonnull
+ Set dependencyScopes();
+
+ PathScope MAIN_COMPILE = pathScope(
+ "main-compile",
+ ProjectScope.MAIN,
+ DependencyScope.EMPTY,
+ DependencyScope.COMPILE_ONLY,
+ DependencyScope.COMPILE,
+ DependencyScope.PROVIDED);
+
+ PathScope MAIN_RUNTIME = pathScope(
+ "main-runtime", ProjectScope.MAIN, DependencyScope.EMPTY, DependencyScope.COMPILE, DependencyScope.RUNTIME);
+
+ PathScope TEST_COMPILE = pathScope(
+ "test-compile",
+ ProjectScope.TEST,
+ DependencyScope.EMPTY,
+ DependencyScope.COMPILE,
+ DependencyScope.PROVIDED,
+ DependencyScope.TEST_ONLY,
+ DependencyScope.TEST);
+
+ PathScope TEST_RUNTIME = pathScope(
+ "test-runtime",
+ ProjectScope.TEST,
+ DependencyScope.EMPTY,
+ DependencyScope.COMPILE,
+ DependencyScope.RUNTIME,
+ DependencyScope.PROVIDED,
+ DependencyScope.TEST,
+ DependencyScope.TEST_RUNTIME);
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java
new file mode 100644
index 000000000000..7a558081af1d
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/PathType.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.nio.file.Path;
+import java.util.Optional;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * The option of a command-line tool where to place the paths to some dependencies.
+ * A {@code PathType} can identify the Java class-path, the Java module-path,
+ * or another kind of path for another programming language for example.
+ * Path types are often exclusive. For example, a dependency should not be
+ * both on the Java class-path and on the Java module-path.
+ *
+ * @see org.apache.maven.api.services.DependencyResolverResult#getDispatchedPaths()
+ *
+ * @since 4.0.0
+ */
+@Experimental
+public interface PathType {
+ /**
+ * Returns the unique name of this path type, including the module to patch if any.
+ * For example, if this type is {@link JavaPathType#MODULES}, then this method returns {@code "MODULES"}.
+ * But if this type was created by {@code JavaPathType.patchModule("foo.bar")}, then this method returns
+ * {@code "PATCH_MODULE:foo.bar"}.
+ *
+ * @return the programmatic name together with the module name on which it applies
+ * @see #toString()
+ */
+ @Nonnull
+ String id();
+
+ /**
+ * Returns the name of the tool option for this path. For example, if this path type
+ * is {@link JavaPathType#MODULES}, then this method returns {@code "--module-path"}.
+ * The option does not include the {@linkplain JavaPathType.Modular#moduleName() module name}
+ * on which it applies.
+ *
+ * @return the name of the tool option for this path type
+ */
+ @Nonnull
+ Optional option();
+
+ /**
+ * Returns the option followed by a string representation of the given path elements.
+ * The path elements are separated by an option-specific or platform-specific separator.
+ * If the given {@code paths} argument contains no element, then this method returns an empty string.
+ *
+ * Examples:
+ * If {@code paths} is a list containing two elements, {@code path1} and {@code path2}, then:
+ *
+ *
+ * - If this type is {@link JavaPathType#MODULES}, then this method returns
+ * {@code "--module-path path1:path2"} on Unix or {@code "--module-path path1;path2"} on Windows.
+ * - If this type was created by {@code JavaPathType.patchModule("foo.bar")}, then the method returns
+ * {@code "--patch-module foo.bar=path1:path2"} on Unix or {@code "--patch-module foo.bar=path1;path2"}
+ * on Windows.
+ *
+ *
+ * @param paths the path to format as a string
+ * @return the option associated to this path type followed by the given path elements,
+ * or an empty string if there is no path element.
+ */
+ @Nonnull
+ String option(Iterable extends Path> paths);
+
+ /**
+ * Returns the name of this path type. For example, if this path type
+ * is {@link JavaPathType#MODULES}, then this method returns {@code "MODULES"}.
+ *
+ * @return the programmatic name of this path type
+ */
+ @Nonnull
+ String name();
+
+ /**
+ * Returns a string representation for this extensible enum describing a path type.
+ * For example {@code "PathType[PATCH_MODULE:foo.bar]"}.
+ */
+ @Nonnull
+ @Override
+ String toString();
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java
index 1b448f9bc311..948bb2561211 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Project.java
@@ -76,7 +76,17 @@ public interface Project {
* @see #getArtifacts()
*/
@Nonnull
- String getPackaging();
+ Packaging getPackaging();
+
+ /**
+ * Returns the project language. It is by default determined by {@link #getPackaging()}.
+ *
+ * @see #getPackaging()
+ */
+ @Nonnull
+ default Language getLanguage() {
+ return getPackaging().language();
+ }
/**
* Returns the project POM artifact, which is the artifact of the POM of this project. Every project have a POM
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Scope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java
similarity index 53%
rename from api/maven-api-core/src/main/java/org/apache/maven/api/Scope.java
rename to api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java
index 624293c1e92d..e663e61463a0 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Scope.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProjectScope.java
@@ -18,49 +18,31 @@
*/
package org.apache.maven.api;
-import java.util.HashMap;
-import java.util.Map;
-
import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Immutable;
+
+import static org.apache.maven.api.ExtensibleEnums.projectScope;
/**
- * Scope for a dependency
+ * Project scope.
+ *
+ * Implementation must have {@code equals()} and {@code hashCode()} implemented, so implementations of this interface
+ * can be used as keys.
*
* @since 4.0.0
*/
@Experimental
-public enum Scope {
- EMPTY(""),
- COMPILE_ONLY("compile-only"),
- COMPILE("compile"),
- RUNTIME("runtime"),
- PROVIDED("provided"),
- TEST_COMPILE_ONLY("test-compile-only"),
- TEST("test"),
- TEST_RUNTIME("test-runtime"),
- IMPORT("import"); // TODO: v4: remove import scope somehow
-
- private final String id;
-
- private static final Map SCOPES;
-
- static {
- Map scopes = new HashMap<>();
- for (Scope s : Scope.values()) {
- scopes.put(s.id, s);
- }
- SCOPES = scopes;
- }
-
- Scope(String id) {
- this.id = id;
- }
-
- public String id() {
- return this.id;
- }
-
- public static Scope get(String scope) {
- return SCOPES.get(scope);
- }
+@Immutable
+@SuppressWarnings("checkstyle:InterfaceIsType")
+public interface ProjectScope extends ExtensibleEnum {
+
+ /**
+ * Main scope.
+ */
+ ProjectScope MAIN = projectScope("main");
+
+ /**
+ * Test scope.
+ */
+ ProjectScope TEST = projectScope("test");
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/ResolutionScope.java b/api/maven-api-core/src/main/java/org/apache/maven/api/ResolutionScope.java
deleted file mode 100644
index 7d003694d633..000000000000
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/ResolutionScope.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.api;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.maven.api.annotations.Experimental;
-
-/**
- * Dependencies resolution scopes available before
- * mojo execution.
- *
- * Important note: The {@code id} values of this enum correspond to constants of
- * {@code org.apache.maven.artifact.Artifact} class and MUST BE KEPT IN SYNC.
- *
- * @since 4.0.0
- */
-@Experimental
-public enum ResolutionScope {
- /**
- * compile resolution scope
- * = compile-only + compile + provided dependencies
- */
- PROJECT_COMPILE("project-compile", Scope.EMPTY, Scope.COMPILE_ONLY, Scope.COMPILE, Scope.PROVIDED),
- /**
- * runtime resolution scope
- * = compile + runtime dependencies
- */
- PROJECT_RUNTIME("project-runtime", Scope.EMPTY, Scope.COMPILE, Scope.RUNTIME),
- /**
- * test-compile resolution scope
- * = compile-only + compile + provided + test-compile-only + test
- * dependencies
- */
- TEST_COMPILE(
- "test-compile",
- Scope.EMPTY,
- Scope.COMPILE_ONLY,
- Scope.COMPILE,
- Scope.PROVIDED,
- Scope.TEST_COMPILE_ONLY,
- Scope.TEST),
- /**
- * test resolution scope
- * = compile + runtime + provided + test + test-runtime
- * dependencies
- */
- TEST_RUNTIME(
- "test-runtime", Scope.EMPTY, Scope.COMPILE, Scope.RUNTIME, Scope.PROVIDED, Scope.TEST, Scope.TEST_RUNTIME);
-
- private static final Map VALUES =
- Stream.of(ResolutionScope.values()).collect(Collectors.toMap(ResolutionScope::id, s -> s));
-
- public static ResolutionScope fromString(String id) {
- return Optional.ofNullable(VALUES.get(id))
- .orElseThrow(() -> new IllegalArgumentException("Unknown resolution scope " + id));
- }
-
- private final String id;
- private final Set scopes;
-
- ResolutionScope(String id, Scope... scopes) {
- this.id = id;
- this.scopes = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(scopes)));
- }
-
- public String id() {
- return this.id;
- }
-
- public Set scopes() {
- return scopes;
- }
-}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
index 65d4798c7c6d..56731f9f3a99 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
@@ -195,13 +195,21 @@ public interface Session {
/**
* Shortcut for {@code getService(RepositoryFactory.class).createLocal(...)}.
*
+ * @param path location of the local repository to create
+ * @return cache of artifacts downloaded from a remote repository or built locally
+ *
* @see org.apache.maven.api.services.RepositoryFactory#createLocal(Path)
*/
- LocalRepository createLocalRepository(Path path);
+ @Nonnull
+ LocalRepository createLocalRepository(@Nonnull Path path);
/**
* Shortcut for {@code getService(RepositoryFactory.class).createRemote(...)}.
*
+ * @param id identifier of the remote repository to create
+ * @param url location of the remote repository
+ * @return remote repository that can be used to download or upload artifacts
+ *
* @see org.apache.maven.api.services.RepositoryFactory#createRemote(String, String)
*/
@Nonnull
@@ -210,48 +218,76 @@ public interface Session {
/**
* Shortcut for {@code getService(RepositoryFactory.class).createRemote(...)}.
*
+ * @param repository information needed for establishing connections with remote repository
+ * @return remote repository that can be used to download or upload artifacts
+ *
* @see org.apache.maven.api.services.RepositoryFactory#createRemote(Repository)
*/
@Nonnull
RemoteRepository createRemoteRepository(@Nonnull Repository repository);
/**
+ * Creates a coordinate out of string that is formatted like:
+ * {@code :[:[:]]:}.
+ *
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
- * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String)
+ * @param coordString the string having "standard" coordinate.
+ * @return coordinate used to point to the artifact
+ *
+ * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String)
*/
- ArtifactCoordinate createArtifactCoordinate(String groupId, String artifactId, String version, String extension);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(@Nonnull String coordString);
/**
- * Creates a coordinate out of string that is formatted like:
- * {@code :[:[:]]:}
- *
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
- * @param coordString the string having "standard" coordinate.
- * @return an {@code ArtifactCoordinate}, never {@code null}
- * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String)
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @return coordinate used to point to the artifact
+ *
+ * @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String)
*/
- ArtifactCoordinate createArtifactCoordinate(String coordString);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(String groupId, String artifactId, String version, String extension);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param classifier the artifact classifier, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @param type the artifact type, or {@code null} is unspecified
+ * @return coordinate used to point to the artifact
+ *
* @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String, String, String)
*/
+ @Nonnull
ArtifactCoordinate createArtifactCoordinate(
String groupId, String artifactId, String version, String classifier, String extension, String type);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param artifact artifact from which to get coordinates
+ * @return coordinate used to point to the artifact
+ *
* @see org.apache.maven.api.services.ArtifactCoordinateFactory#create(Session, String, String, String, String, String, String)
*/
- ArtifactCoordinate createArtifactCoordinate(Artifact artifact);
+ @Nonnull
+ ArtifactCoordinate createArtifactCoordinate(@Nonnull Artifact artifact);
/**
* Shortcut for {@code getService(DependencyFactory.class).create(...)}.
*
+ * @param coordinate artifact coordinate to get as a dependency coordinate
+ * @return dependency coordinate for the given artifact
+ *
* @see DependencyCoordinateFactory#create(Session, ArtifactCoordinate)
*/
@Nonnull
@@ -260,6 +296,9 @@ ArtifactCoordinate createArtifactCoordinate(
/**
* Shortcut for {@code getService(DependencyFactory.class).create(...)}.
*
+ * @param dependency dependency for which to get the coordinate
+ * @return coordinate for the given dependency
+ *
* @see DependencyCoordinateFactory#create(Session, Dependency)
*/
@Nonnull
@@ -268,85 +307,131 @@ ArtifactCoordinate createArtifactCoordinate(
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @return artifact with the given coordinates
+ *
* @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String)
*/
+ @Nonnull
Artifact createArtifact(String groupId, String artifactId, String version, String extension);
/**
* Shortcut for {@code getService(ArtifactFactory.class).create(...)}.
*
+ * @param groupId the group identifier, or {@code null} is unspecified
+ * @param artifactId the artifact identifier, or {@code null} is unspecified
+ * @param version the artifact version, or {@code null} is unspecified
+ * @param classifier the artifact classifier, or {@code null} is unspecified
+ * @param extension the artifact extension, or {@code null} is unspecified
+ * @param type the artifact type, or {@code null} is unspecified
+ * @return artifact with the given coordinates
+ *
* @see org.apache.maven.api.services.ArtifactFactory#create(Session, String, String, String, String, String, String)
*/
+ @Nonnull
Artifact createArtifact(
String groupId, String artifactId, String version, String classifier, String extension, String type);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinate coordinates of the artifact to resolve
+ * @return requested artifact together with the path to its file
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map.Entry resolveArtifact(ArtifactCoordinate coordinate);
+ @Nonnull
+ Map.Entry resolveArtifact(@Nonnull ArtifactCoordinate coordinate);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinates coordinates of all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(ArtifactCoordinate... coordinates);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull ArtifactCoordinate... coordinates);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param coordinates coordinates of all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(Collection extends ArtifactCoordinate> coordinates);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull Collection extends ArtifactCoordinate> coordinates);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param artifact the artifact to resolve
+ * @return requested artifact together with the path to its file
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map.Entry resolveArtifact(Artifact artifact);
+ @Nonnull
+ Map.Entry resolveArtifact(@Nonnull Artifact artifact);
/**
* Shortcut for {@code getService(ArtifactResolver.class).resolve(...)}.
*
- * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
+ * @param artifacts all artifacts to resolve
+ * @return requested artifacts together with the paths to their files
* @throws org.apache.maven.api.services.ArtifactResolverException if the artifact resolution failed
+ *
+ * @see org.apache.maven.api.services.ArtifactResolver#resolve(Session, Collection)
*/
- Map resolveArtifacts(Artifact... artifacts);
+ @Nonnull
+ Map resolveArtifacts(@Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactInstaller.class).install(...)}.
*
- * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
+ * @param artifacts the artifacts to install
* @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed
+ *
+ * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
*/
- void installArtifacts(Artifact... artifacts);
+ void installArtifacts(@Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactInstaller.class).install(...)}.
*
- * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
+ * @param artifacts the artifacts to install
* @throws org.apache.maven.api.services.ArtifactInstallerException if the artifacts installation failed
+ *
+ * @see org.apache.maven.api.services.ArtifactInstaller#install(Session, Collection)
*/
- void installArtifacts(Collection artifacts);
+ void installArtifacts(@Nonnull Collection artifacts);
/**
* Shortcut for {@code getService(ArtifactDeployer.class).deploy(...)}.
*
- * @see org.apache.maven.api.services.ArtifactDeployer#deploy(Session, RemoteRepository, Collection)
+ * @param repository the repository where to deploy artifacts
+ * @param artifacts the artifacts to deploy
* @throws org.apache.maven.api.services.ArtifactDeployerException if the artifacts deployment failed
+ *
+ * @see org.apache.maven.api.services.ArtifactDeployer#deploy(Session, RemoteRepository, Collection)
*/
- void deployArtifact(RemoteRepository repository, Artifact... artifacts);
+ void deployArtifact(@Nonnull RemoteRepository repository, @Nonnull Artifact... artifacts);
/**
* Shortcut for {@code getService(ArtifactManager.class).setPath(...)}.
*
+ * @param artifact the artifact for which to associate a path
+ * @param path path to associate to the given artifact
+ *
* @see org.apache.maven.api.services.ArtifactManager#setPath(Artifact, Path)
*/
void setArtifactPath(@Nonnull Artifact artifact, @Nonnull Path path);
@@ -354,6 +439,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(ArtifactManager.class).getPath(...)}.
*
+ * @param artifact the artifact for which to get a path
+ * @return path associated to the given artifact
+ *
* @see org.apache.maven.api.services.ArtifactManager#getPath(Artifact)
*/
@Nonnull
@@ -365,6 +453,9 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(LocalArtifactManager.class).getPathForLocalArtitact(...)}.
*
+ * @param artifact the artifact for which to get a local path
+ * @return local path associated to the given artifact, or {@code null} if none
+ *
* @see org.apache.maven.api.services.LocalRepositoryManager#getPathForLocalArtifact(Session, LocalRepository, Artifact)
*/
Path getPathForLocalArtifact(@Nonnull Artifact artifact);
@@ -376,6 +467,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(LocalArtifactManager.class).getPathForRemoteArtifact(...)}.
*
+ * @param remote the repository from where artifacts are downloaded
+ * @param artifact the artifact for which to get a path
+ * @return path associated to the given artifact
+ *
* @see org.apache.maven.api.services.LocalRepositoryManager#getPathForRemoteArtifact(Session, LocalRepository, RemoteRepository, Artifact)
*/
@Nonnull
@@ -385,6 +480,12 @@ Artifact createArtifact(
* Checks whether a given artifact version is considered a {@code SNAPSHOT} or not.
*
* Shortcut for {@code getService(ArtifactManager.class).isSnapshot(...)}.
+ *
+ * In case there is {@link Artifact} in scope, the recommended way to perform this check is
+ * use of {@link Artifact#isSnapshot()} instead.
+ *
+ * @param version artifact version
+ * @return whether the given version is a snapshot
*
* @see org.apache.maven.api.services.VersionParser#isSnapshot(String)
*/
@@ -393,6 +494,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)}
*
+ * @param artifact artifact for which to get the dependencies, including transitive ones
+ * @return root node of the dependency graph for the given artifact
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Artifact)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -402,6 +506,9 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)}
*
+ * @param project project for which to get the dependencies, including transitive ones
+ * @return root node of the dependency graph for the given project
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Project)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -415,6 +522,9 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(DependencyCollector.class).resolve(...)}
*
+ * @param dependency dependency for which to get transitive dependencies
+ * @return root node of the dependency graph for the given artifact
+ *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, DependencyCoordinate)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed
*/
@@ -424,20 +534,79 @@ Artifact createArtifact(
/**
* Shortcut for {@code getService(DependencyResolver.class).flatten(...)}.
*
- * @see org.apache.maven.api.services.DependencyResolver#flatten(Session, Node, ResolutionScope)
+ * @param node node for which to get a flattened list
+ * @param scope build path scope (main compile, test compile, etc.) of desired nodes
+ * @return flattened list of node with the given build path scope
* @throws org.apache.maven.api.services.DependencyResolverException if the dependency flattening failed
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#flatten(Session, Node, PathScope)
*/
@Nonnull
- List flattenDependencies(@Nonnull Node node, @Nonnull ResolutionScope scope);
+ List flattenDependencies(@Nonnull Node node, @Nonnull PathScope scope);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param dependencyCoordinate coordinate of the dependency for which to get the paths
+ * @return paths to the transitive dependencies of the given dependency
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, DependencyCoordinate)
+ */
@Nonnull
List resolveDependencies(@Nonnull DependencyCoordinate dependencyCoordinate);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param dependencyCoordinates coordinates of all dependency for which to get the paths
+ * @return paths to the transitive dependencies of the given dependencies
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, List)
+ */
@Nonnull
List resolveDependencies(@Nonnull List dependencyCoordinates);
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getPaths()}.
+ *
+ * @param project the project for which to get dependencies
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
@Nonnull
- List resolveDependencies(@Nonnull Project project, @Nonnull ResolutionScope scope);
+ List resolveDependencies(@Nonnull Project project, @Nonnull PathScope scope);
+
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}.
+ *
+ * @param dependencyCoordinate coordinate of the dependency for which to get the paths
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @param desiredTypes the type of paths to include in the result
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
+ @Nonnull
+ Map> resolveDependencies(
+ @Nonnull DependencyCoordinate dependencyCoordinate,
+ @Nonnull PathScope scope,
+ @Nonnull Collection desiredTypes);
+
+ /**
+ * Shortcut for {@code getService(DependencyResolver.class).resolve(...).getDispatchedPaths()}.
+ *
+ * @param project the project for which to get dependencies
+ * @param scope build path scope (main compile, test compile, etc.) of desired paths
+ * @param desiredTypes the type of paths to include in the result
+ * @return paths to the transitive dependencies of the given project
+ *
+ * @see org.apache.maven.api.services.DependencyResolver#resolve(Session, Project, PathScope)
+ */
+ @Nonnull
+ Map> resolveDependencies(
+ @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection desiredTypes);
/**
* Resolves an artifact's meta version (if any) to a concrete version. For example, resolves "1.0-SNAPSHOT"
@@ -445,8 +614,11 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionResolver.class).resolve(...)}
*
- * @see org.apache.maven.api.services.VersionResolver#resolve(Session, ArtifactCoordinate) (String)
+ * @param artifact the artifact for which to resolve the version
+ * @return resolved version of the given artifact
* @throws org.apache.maven.api.services.VersionResolverException if the resolution failed
+ *
+ * @see org.apache.maven.api.services.VersionResolver#resolve(Session, ArtifactCoordinate) (String)
*/
@Nonnull
Version resolveVersion(@Nonnull ArtifactCoordinate artifact);
@@ -459,9 +631,10 @@ Artifact createArtifact(
* In this case though, the result contains simply the (parsed) input version, regardless of the
* repositories and their contents.
*
+ * @param artifact the artifact for which to resolve the versions
* @return a list of resolved {@code Version}s.
- * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinate) (String)
* @throws org.apache.maven.api.services.VersionRangeResolverException if the resolution failed
+ * @see org.apache.maven.api.services.VersionRangeResolver#resolve(Session, ArtifactCoordinate) (String)
*/
@Nonnull
List resolveVersionRange(@Nonnull ArtifactCoordinate artifact);
@@ -471,8 +644,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersion(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersion(String)
+ * @param version the version string to parse
+ * @return the version parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersion(String)
*/
@Nonnull
Version parseVersion(@Nonnull String version);
@@ -482,8 +657,10 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersionRange(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String)
+ * @param versionRange the version string to parse
+ * @return the version range parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersionRange(String)
*/
@Nonnull
VersionRange parseVersionRange(@Nonnull String versionRange);
@@ -493,9 +670,23 @@ Artifact createArtifact(
*
* Shortcut for {@code getService(VersionParser.class).parseVersionConstraint(...)}.
*
- * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String)
+ * @param versionConstraint the version string to parse
+ * @return the version constraint parsed from the given string
* @throws org.apache.maven.api.services.VersionParserException if the parsing failed
+ * @see org.apache.maven.api.services.VersionParser#parseVersionConstraint(String)
*/
@Nonnull
VersionConstraint parseVersionConstraint(@Nonnull String versionConstraint);
+
+ Type requireType(String id);
+
+ Language requireLanguage(String id);
+
+ Packaging requirePackaging(String id);
+
+ ProjectScope requireProjectScope(String id);
+
+ DependencyScope requireDependencyScope(String id);
+
+ PathScope requirePathScope(String id);
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
index 15ffc0b59a69..4842ae5d46a8 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Type.java
@@ -18,6 +18,8 @@
*/
package org.apache.maven.api;
+import java.util.Set;
+
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Immutable;
import org.apache.maven.api.annotations.Nonnull;
@@ -30,20 +32,65 @@
*
* It provides information about the file type (or extension) of the associated artifact,
* its default classifier, and how the artifact will be used in the build when creating
- * classpaths.
+ * class-paths or module-paths.
*
* For example, the type {@code java-source} has a {@code jar} extension and a
* {@code sources} classifier. The artifact and its dependencies should be added
- * to the classpath.
+ * to the build path.
*
* @since 4.0.0
*/
@Experimental
@Immutable
-public interface Type {
+public interface Type extends ExtensibleEnum {
+ /**
+ * Artifact type name for a POM file.
+ */
+ String POM = "pom";
+
+ /**
+ * Artifact type name for a JAR file that can be placed either on the class-path or on the module-path.
+ * The path (classes or modules) is chosen by the plugin, possibly using heuristic rules.
+ * This is the behavior of Maven 3.
+ */
+ String JAR = "jar";
+
+ /**
+ * Artifact type name for a JAR file to unconditionally place on the class-path.
+ * If the JAR is modular, its module information are ignored.
+ * This type is new in Maven 4.
+ */
+ String CLASSPATH_JAR = "classpath-jar";
+
+ /**
+ * Artifact type name for a JAR file to unconditionally place on the module-path.
+ * If the JAR is not modular, then it is loaded by Java as an unnamed module.
+ * This type is new in Maven 4.
+ */
+ String MODULAR_JAR = "modular-jar";
+
+ /**
+ * Artifact type name for source code packaged in a JAR file.
+ */
+ String JAVA_SOURCE = "java-source";
+
+ /**
+ * Artifact type name for javadoc packaged in a JAR file.
+ */
+ String JAVADOC = "javadoc";
+
+ /**
+ * Artifact type name for a Maven plugin.
+ */
+ String MAVEN_PLUGIN = "maven-plugin";
- String LANGUAGE_NONE = "none";
- String LANGUAGE_JAVA = "java";
+ /**
+ * Artifact type name for a JAR file containing test classes. If the main artifact is placed on the class-path
+ * ({@value #JAR} or {@value #CLASSPATH_JAR} types), then the test artifact will also be placed on the class-path.
+ * Otherwise, if the main artifact is placed on the module-path ({@value #JAR} or {@value #MODULAR_JAR} types),
+ * then the test artifact will be added using {@code --patch-module} option.
+ */
+ String TEST_JAR = "test-jar";
/**
* Returns the dependency type id.
@@ -52,14 +99,15 @@ public interface Type {
* @return the id of this type, never {@code null}.
*/
@Nonnull
- String getId();
+ String id();
/**
* Returns the dependency type language.
*
* @return the language of this type, never {@code null}.
*/
- String getLanguage();
+ @Nonnull
+ Language getLanguage();
/**
* Get the file extension of artifacts of this type.
@@ -79,16 +127,6 @@ public interface Type {
@Nullable
String getClassifier();
- /**
- * Specifies if the artifact contains java classes and should be
- * added to the classpath.
- *
- * @return if the artifact should be added to the class path
- */
- default boolean isAddedToClassPath() {
- return getDependencyProperties().checkFlag(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT);
- }
-
/**
* Specifies if the artifact already embeds its own dependencies.
* This is the case for JEE packages or similar artifacts such as
@@ -96,15 +134,20 @@ default boolean isAddedToClassPath() {
*
* @return if the artifact's dependencies are included in the artifact
*/
- default boolean isIncludesDependencies() {
- return getDependencyProperties().checkFlag(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES);
- }
+ boolean isIncludesDependencies();
/**
- * Gets the default properties associated with this dependency type.
+ * Types of path (class-path, module-path, …) where the dependency can be placed.
+ * For most deterministic builds, the array length should be 1. In such case,
+ * the dependency will be unconditionally placed on the specified type of path
+ * and no heuristic rule will be involved.
*
- * @return the default properties, never {@code null}.
+ *
It is nevertheless common to specify two or more types of path. For example,
+ * a Java library may be compatible with either the class-path or the module-path,
+ * and the user may have provided no instruction about which type to use. In such
+ * case, the plugin may apply rules for choosing a path. See for example
+ * {@link JavaPathType#CLASSES} and {@link JavaPathType#MODULES}.
*/
@Nonnull
- DependencyProperties getDependencyProperties();
+ Set getPathTypes();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinateFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinateFactoryRequest.java
index 6db92dbe652a..34483be0de67 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinateFactoryRequest.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinateFactoryRequest.java
@@ -89,7 +89,7 @@ static DependencyCoordinateFactoryRequest build(@Nonnull Session session, @Nonnu
.version(dependency.getVersion().asString())
.classifier(dependency.getClassifier())
.extension(dependency.getExtension())
- .type(dependency.getType().getId())
+ .type(dependency.getType().id())
.scope(dependency.getScope().id())
.optional(dependency.isOptional())
.build();
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java
index 3da46b1f5bde..9fefbc4cda7c 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolver.java
@@ -22,8 +22,8 @@
import org.apache.maven.api.DependencyCoordinate;
import org.apache.maven.api.Node;
+import org.apache.maven.api.PathScope;
import org.apache.maven.api.Project;
-import org.apache.maven.api.ResolutionScope;
import org.apache.maven.api.Service;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Experimental;
@@ -35,7 +35,7 @@
@Experimental
public interface DependencyResolver extends Service {
- List flatten(Session session, Node node, ResolutionScope scope) throws DependencyResolverException;
+ List flatten(Session session, Node node, PathScope scope) throws DependencyResolverException;
/**
* This method collects, flattens and resolves the dependencies.
@@ -47,7 +47,7 @@ public interface DependencyResolver extends Service {
* @throws ArtifactResolverException
*
* @see DependencyCollector#collect(DependencyCollectorRequest)
- * @see #flatten(Session, Node, ResolutionScope)
+ * @see #flatten(Session, Node, PathScope)
* @see ArtifactResolver#resolve(ArtifactResolverRequest)
*/
DependencyResolverResult resolve(DependencyResolverRequest request)
@@ -60,7 +60,7 @@ default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull Proj
@Nonnull
default DependencyResolverResult resolve(
- @Nonnull Session session, @Nonnull Project project, @Nonnull ResolutionScope scope) {
+ @Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, project, scope));
}
@@ -71,7 +71,7 @@ default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull Depe
@Nonnull
default DependencyResolverResult resolve(
- @Nonnull Session session, @Nonnull DependencyCoordinate dependency, @Nonnull ResolutionScope scope) {
+ @Nonnull Session session, @Nonnull DependencyCoordinate dependency, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, dependency, scope));
}
@@ -83,9 +83,7 @@ default DependencyResolverResult resolve(
@Nonnull
default DependencyResolverResult resolve(
- @Nonnull Session session,
- @Nonnull List dependencies,
- @Nonnull ResolutionScope scope) {
+ @Nonnull Session session, @Nonnull List dependencies, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, dependencies, scope));
}
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
index 0519ececf1d7..2f410a8a303f 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java
@@ -20,11 +20,14 @@
import java.util.Collection;
import java.util.List;
+import java.util.function.Predicate;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.PathScope;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Project;
-import org.apache.maven.api.ResolutionScope;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@@ -35,7 +38,17 @@
public interface DependencyResolverRequest extends DependencyCollectorRequest {
@Nonnull
- ResolutionScope getResolutionScope();
+ PathScope getPathScope();
+
+ /**
+ * Returns a filter for the types of path (class-path, module-path, …) accepted by the tool.
+ * For example, if a Java tools accepts only class-path elements, then the filter should return
+ * {@code true} for {@link JavaPathType#CLASSES} and {@code false} for {@link JavaPathType#MODULES}.
+ * If no filter is explicitly set, then the default is a filter accepting everything.
+ *
+ * @return a filter for the types of path (class-path, module-path, …) accepted by the tool
+ */
+ Predicate getPathTypeFilter();
@Nonnull
static DependencyResolverRequestBuilder builder() {
@@ -44,50 +57,51 @@ static DependencyResolverRequestBuilder builder() {
@Nonnull
static DependencyResolverRequest build(Session session, Project project) {
- return build(session, project, ResolutionScope.PROJECT_RUNTIME);
+ return build(session, project, PathScope.MAIN_RUNTIME);
}
@Nonnull
- static DependencyResolverRequest build(Session session, Project project, ResolutionScope scope) {
+ static DependencyResolverRequest build(Session session, Project project, PathScope scope) {
return new DependencyResolverRequestBuilder()
.session(session)
.project(project)
- .resolutionScope(scope)
+ .pathScope(scope)
.build();
}
@Nonnull
static DependencyResolverRequest build(Session session, DependencyCoordinate dependency) {
- return build(session, dependency, ResolutionScope.PROJECT_RUNTIME);
+ return build(session, dependency, PathScope.MAIN_RUNTIME);
}
@Nonnull
- static DependencyResolverRequest build(Session session, DependencyCoordinate dependency, ResolutionScope scope) {
+ static DependencyResolverRequest build(Session session, DependencyCoordinate dependency, PathScope scope) {
return new DependencyResolverRequestBuilder()
.session(session)
.dependency(dependency)
- .resolutionScope(scope)
+ .pathScope(scope)
.build();
}
@Nonnull
static DependencyResolverRequest build(Session session, List dependencies) {
- return build(session, dependencies, ResolutionScope.PROJECT_RUNTIME);
+ return build(session, dependencies, PathScope.MAIN_RUNTIME);
}
@Nonnull
- static DependencyResolverRequest build(
- Session session, List dependencies, ResolutionScope scope) {
+ static DependencyResolverRequest build(Session session, List dependencies, PathScope scope) {
return new DependencyResolverRequestBuilder()
.session(session)
.dependencies(dependencies)
- .resolutionScope(scope)
+ .pathScope(scope)
.build();
}
@NotThreadSafe
class DependencyResolverRequestBuilder extends DependencyCollectorRequestBuilder {
- ResolutionScope resolutionScope;
+ PathScope pathScope;
+
+ Predicate pathTypeFilter;
@Nonnull
@Override
@@ -154,20 +168,57 @@ public DependencyResolverRequestBuilder verbose(boolean verbose) {
}
@Nonnull
- public DependencyResolverRequestBuilder resolutionScope(@Nonnull ResolutionScope resolutionScope) {
- this.resolutionScope = resolutionScope;
+ public DependencyResolverRequestBuilder pathScope(@Nonnull PathScope pathScope) {
+ this.pathScope = pathScope;
+ return this;
+ }
+
+ /**
+ * Filters the types of paths to include in the result.
+ * The result will contain only the paths of types for which the predicate returned {@code true}.
+ * It is recommended to apply a filter for retaining only the types of paths of interest,
+ * because it can resolve ambiguities when a path could be of many types.
+ *
+ * @param pathTypeFilter predicate evaluating whether a path type should be included in the result
+ * @return {@code this} for method call chaining
+ */
+ @Nonnull
+ public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Predicate pathTypeFilter) {
+ this.pathTypeFilter = pathTypeFilter;
return this;
}
+ /**
+ * Specifies the type of paths to include in the result. This is a convenience method for
+ * {@link #pathTypeFilter(Predicate)} using {@link Collection#contains(Object)} as the filter.
+ *
+ * @param desiredTypes the type of paths to include in the result
+ * @return {@code this} for method call chaining
+ */
+ @Nonnull
+ public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Collection desiredTypes) {
+ return pathTypeFilter(desiredTypes::contains);
+ }
+
@Override
public DependencyResolverRequest build() {
return new DefaultDependencyResolverRequest(
- session, project, rootArtifact, root, dependencies, managedDependencies, verbose, resolutionScope);
+ session,
+ project,
+ rootArtifact,
+ root,
+ dependencies,
+ managedDependencies,
+ verbose,
+ pathScope,
+ pathTypeFilter);
}
static class DefaultDependencyResolverRequest extends DefaultDependencyCollectorRequest
implements DependencyResolverRequest {
- private final ResolutionScope resolutionScope;
+ private final PathScope pathScope;
+
+ private final Predicate pathTypeFilter;
DefaultDependencyResolverRequest(
Session session,
@@ -177,9 +228,11 @@ static class DefaultDependencyResolverRequest extends DefaultDependencyCollector
Collection dependencies,
Collection managedDependencies,
boolean verbose,
- ResolutionScope resolutionScope) {
+ PathScope pathScope,
+ Predicate pathTypeFilter) {
super(session, project, rootArtifact, root, dependencies, managedDependencies, verbose);
- this.resolutionScope = nonNull(resolutionScope, "resolutionScope cannot be null");
+ this.pathScope = nonNull(pathScope, "pathScope cannot be null");
+ this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : (t) -> true;
if (verbose) {
throw new IllegalArgumentException("verbose cannot be true for resolving dependencies");
}
@@ -187,8 +240,13 @@ static class DefaultDependencyResolverRequest extends DefaultDependencyCollector
@Nonnull
@Override
- public ResolutionScope getResolutionScope() {
- return resolutionScope;
+ public PathScope getPathScope() {
+ return pathScope;
+ }
+
+ @Override
+ public Predicate getPathTypeFilter() {
+ return pathTypeFilter;
}
}
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
index 793243850068..60468046145e 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java
@@ -21,9 +21,11 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@@ -32,13 +34,57 @@ public interface DependencyResolverResult extends DependencyCollectorResult {
/**
* The ordered list of the flattened dependency nodes.
+ *
+ * @return the ordered list of the flattened dependency nodes
*/
@Nonnull
List getNodes();
+ /**
+ * Returns the file paths of all dependencies, regardless on which tool option those paths should be placed.
+ * The returned list may contain a mix of Java class-path, Java module-path, and other types of path elements.
+ *
+ * @return the paths of all dependencies
+ */
@Nonnull
List getPaths();
+ /**
+ * Returns the file paths of all dependencies, dispatched according the tool options where to place them.
+ * The {@link PathType} keys identify, for example, {@code --class-path} or {@code --module-path} options.
+ * In the case of Java tools, the map may also contain {@code --patch-module} options, which are
+ * {@linkplain org.apache.maven.api.JavaPathType#patchModule(String) handled in a special way}.
+ *
+ * Design note:
+ * All types of path are determined together because they are sometime mutually exclusive.
+ * For example, an artifact of type {@value org.apache.maven.api.Type#JAR} can be placed
+ * either on the class-path or on the module-path. The project needs to make a choice
+ * (possibly using heuristic rules), then to add the dependency in only one of the options
+ * identified by {@link PathType}.
+ *
+ * @return file paths to place on the different tool options
+ */
+ @Nonnull
+ Map> getDispatchedPaths();
+
@Nonnull
Map getDependencies();
+
+ /**
+ * Formats the command-line option for the path of the specified type.
+ * The option are documented in {@link org.apache.maven.api.JavaPathType} enumeration values.
+ *
+ * @param type the desired type of path (class-path, module-path, …)
+ * @return the option to pass to Java tools
+ */
+ default Optional formatOption(final PathType type) {
+ List paths = getDispatchedPaths().get(type);
+ if (paths != null) {
+ String option = type.option(paths);
+ if (!option.isEmpty()) {
+ return Optional.of(option);
+ }
+ }
+ return Optional.empty();
+ }
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java
new file mode 100644
index 000000000000..3a5673c583e2
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ExtensibleEnumRegistry.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.services;
+
+import java.util.Optional;
+
+import org.apache.maven.api.ExtensibleEnum;
+import org.apache.maven.api.Service;
+import org.apache.maven.api.annotations.Nonnull;
+
+public interface ExtensibleEnumRegistry extends Service {
+ @Nonnull
+ Optional lookup(String id);
+
+ default T require(String id) {
+ return lookup(id).orElseThrow(() -> new IllegalArgumentException("Unknown extensible enum value '" + id + "'"));
+ }
+}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java
new file mode 100644
index 000000000000..d2f0f0933157
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/LanguageRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.services;
+
+import org.apache.maven.api.Language;
+
+public interface LanguageRegistry extends ExtensibleEnumRegistry {}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java
new file mode 100644
index 000000000000..e114e2664058
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PackagingRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.services;
+
+import org.apache.maven.api.Packaging;
+
+public interface PackagingRegistry extends ExtensibleEnumRegistry {}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java
new file mode 100644
index 000000000000..06b5d5964733
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/PathScopeRegistry.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.services;
+
+import org.apache.maven.api.PathScope;
+
+public interface PathScopeRegistry extends ExtensibleEnumRegistry {}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java
new file mode 100644
index 000000000000..a3b4f24a5d58
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectScopeRegistry.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.services;
+
+import org.apache.maven.api.ProjectScope;
+
+/**
+ * Manager for {@link ProjectScope}.
+ */
+public interface ProjectScopeRegistry extends ExtensibleEnumRegistry {}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java
index c484df40f5db..ca4c40d48a19 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/TypeRegistry.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.api.services;
-import org.apache.maven.api.Service;
import org.apache.maven.api.Type;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
@@ -29,7 +28,7 @@
* @since 4.0.0
*/
@Experimental
-public interface TypeRegistry extends Service {
+public interface TypeRegistry extends ExtensibleEnumRegistry {
/**
* Obtain the {@link Type} from the specified {@code id}.
@@ -40,5 +39,5 @@ public interface TypeRegistry extends Service {
* @return the type
*/
@Nonnull
- Type getType(@Nonnull String id);
+ Type require(@Nonnull String id);
}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java
new file mode 100644
index 000000000000..514651088ac3
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ExtensibleEnumProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.spi;
+
+import java.util.Collection;
+
+import org.apache.maven.api.ExtensibleEnum;
+
+public interface ExtensibleEnumProvider {
+
+ Collection provides();
+}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java
new file mode 100644
index 000000000000..9757d04f2005
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/LanguageProvider.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.spi;
+
+import org.apache.maven.api.Language;
+
+public interface LanguageProvider extends ExtensibleEnumProvider {}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java
new file mode 100644
index 000000000000..bfcf3a219100
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PackagingProvider.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.spi;
+
+import org.apache.maven.api.Packaging;
+
+public interface PackagingProvider extends ExtensibleEnumProvider {}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java
new file mode 100644
index 000000000000..2e2597b839d0
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/PathScopeProvider.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.spi;
+
+import org.apache.maven.api.PathScope;
+
+public interface PathScopeProvider extends ExtensibleEnumProvider {}
diff --git a/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java
new file mode 100644
index 000000000000..c4a9a1f992a3
--- /dev/null
+++ b/api/maven-api-spi/src/main/java/org/apache/maven/api/spi/ProjectScopeProvider.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.spi;
+
+import org.apache.maven.api.ProjectScope;
+
+public interface ProjectScopeProvider extends ExtensibleEnumProvider {}
diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java b/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java
index 9a1ca1f4336b..44f2615790ca 100644
--- a/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java
+++ b/maven-artifact/src/main/java/org/apache/maven/artifact/DefaultArtifact.java
@@ -181,52 +181,64 @@ private boolean empty(String value) {
return (value == null) || (value.trim().length() < 1);
}
+ @Override
public String getClassifier() {
return classifier;
}
+ @Override
public boolean hasClassifier() {
return classifier != null && !classifier.isEmpty();
}
+ @Override
public String getScope() {
return scope;
}
+ @Override
public String getGroupId() {
return groupId;
}
+ @Override
public String getArtifactId() {
return artifactId;
}
+ @Override
public String getVersion() {
return version;
}
+ @Override
public void setVersion(String version) {
this.version = version;
setBaseVersionInternal(version);
versionRange = null;
}
+ @Override
public String getType() {
return type;
}
+ @Override
public void setFile(File file) {
this.file = file;
}
+ @Override
public File getFile() {
return file;
}
+ @Override
public ArtifactRepository getRepository() {
return repository;
}
+ @Override
public void setRepository(ArtifactRepository repository) {
this.repository = repository;
}
@@ -235,10 +247,12 @@ public void setRepository(ArtifactRepository repository) {
//
// ----------------------------------------------------------------------
+ @Override
public String getId() {
return getDependencyConflictId() + ":" + getBaseVersion();
}
+ @Override
public String getDependencyConflictId() {
StringBuilder sb = new StringBuilder(128);
sb.append(getGroupId());
@@ -257,6 +271,7 @@ private void appendArtifactTypeClassifierString(StringBuilder sb) {
}
}
+ @Override
public void addMetadata(ArtifactMetadata metadata) {
if (metadataMap == null) {
metadataMap = new HashMap<>();
@@ -270,6 +285,7 @@ public void addMetadata(ArtifactMetadata metadata) {
}
}
+ @Override
public Collection getMetadataList() {
if (metadataMap == null) {
return Collections.emptyList();
@@ -282,6 +298,7 @@ public Collection getMetadataList() {
// Object overrides
// ----------------------------------------------------------------------
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (getGroupId() != null) {
@@ -323,6 +340,7 @@ public int hashCode() {
return Objects.hash(groupId, artifactId, type, classifier, version);
}
+ @Override
public String getBaseVersion() {
if (baseVersion == null && version != null) {
setBaseVersionInternal(version);
@@ -339,6 +357,7 @@ protected String getBaseVersionInternal() {
return baseVersion;
}
+ @Override
public void setBaseVersion(String baseVersion) {
setBaseVersionInternal(baseVersion);
}
@@ -347,6 +366,7 @@ protected void setBaseVersionInternal(String baseVersion) {
this.baseVersion = ArtifactUtils.toSnapshotVersion(baseVersion);
}
+ @Override
public int compareTo(Artifact a) {
int result = groupId.compareTo(a.getGroupId());
if (result == 0) {
@@ -376,47 +396,58 @@ public int compareTo(Artifact a) {
return result;
}
+ @Override
public void updateVersion(String version, ArtifactRepository localRepository) {
setResolvedVersion(version);
setFile(new File(localRepository.getBasedir(), localRepository.pathOf(this)));
}
+ @Override
public String getDownloadUrl() {
return downloadUrl;
}
+ @Override
public void setDownloadUrl(String downloadUrl) {
this.downloadUrl = downloadUrl;
}
+ @Override
public ArtifactFilter getDependencyFilter() {
return dependencyFilter;
}
+ @Override
public void setDependencyFilter(ArtifactFilter artifactFilter) {
dependencyFilter = artifactFilter;
}
+ @Override
public ArtifactHandler getArtifactHandler() {
return artifactHandler;
}
+ @Override
public List getDependencyTrail() {
return dependencyTrail;
}
+ @Override
public void setDependencyTrail(List dependencyTrail) {
this.dependencyTrail = dependencyTrail;
}
+ @Override
public void setScope(String scope) {
this.scope = scope;
}
+ @Override
public VersionRange getVersionRange() {
return versionRange;
}
+ @Override
public void setVersionRange(VersionRange versionRange) {
this.versionRange = versionRange;
selectVersionFromNewRangeIfAvailable();
@@ -431,70 +462,86 @@ private void selectVersionFromNewRangeIfAvailable() {
}
}
+ @Override
public void selectVersion(String version) {
this.version = version;
setBaseVersionInternal(version);
}
+ @Override
public void setGroupId(String groupId) {
this.groupId = groupId;
}
+ @Override
public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
+ @Override
public boolean isSnapshot() {
return getBaseVersion() != null
&& (getBaseVersion().endsWith(SNAPSHOT_VERSION)
|| getBaseVersion().equals(LATEST_VERSION));
}
+ @Override
public void setResolved(boolean resolved) {
this.resolved = resolved;
}
+ @Override
public boolean isResolved() {
return resolved;
}
+ @Override
public void setResolvedVersion(String version) {
this.version = version;
// retain baseVersion
}
+ @Override
public void setArtifactHandler(ArtifactHandler artifactHandler) {
this.artifactHandler = artifactHandler;
}
+ @Override
public void setRelease(boolean release) {
this.release = release;
}
+ @Override
public boolean isRelease() {
return release;
}
+ @Override
public List getAvailableVersions() {
return availableVersions;
}
+ @Override
public void setAvailableVersions(List availableVersions) {
this.availableVersions = availableVersions;
}
+ @Override
public boolean isOptional() {
return optional;
}
+ @Override
public ArtifactVersion getSelectedVersion() throws OverConstrainedVersionException {
return versionRange.getSelectedVersion(this);
}
+ @Override
public boolean isSelectedVersionKnown() throws OverConstrainedVersionException {
return versionRange.isSelectedVersionKnown(this);
}
+ @Override
public void setOptional(boolean optional) {
this.optional = optional;
}
diff --git a/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java b/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
index 92cca2a069dd..12f50ef31a76 100644
--- a/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
+++ b/maven-artifact/src/main/java/org/apache/maven/artifact/handler/ArtifactHandler.java
@@ -53,5 +53,16 @@ public interface ArtifactHandler {
String getLanguage();
+ /**
+ * Specifies if the artifact contains java classes and can be added to the classpath.
+ * Whether the artifact should be added to the classpath depends on other
+ * dependency properties.
+ *
+ * @return if the artifact can be added to the class path
+ *
+ * @deprecated A value of {@code true} does not mean that the dependency should
+ * be placed on the classpath. See {@code JavaPathType} instead for better analysis.
+ */
+ @Deprecated
boolean isAddedToClasspath();
}
diff --git a/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java b/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
index deb3e77c5456..f70db5bcbd32 100644
--- a/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
+++ b/maven-artifact/src/test/java/org/apache/maven/artifact/handler/ArtifactHandlerMock.java
@@ -27,6 +27,7 @@ public void setExtension(String extension) {
this.extension = extension;
}
+ @Override
public String getExtension() {
return extension;
}
@@ -35,6 +36,7 @@ public void setDirectory(String directory) {
this.directory = directory;
}
+ @Override
public String getDirectory() {
return directory;
}
@@ -43,6 +45,7 @@ public void setClassifier(String classifier) {
this.classifier = classifier;
}
+ @Override
public String getClassifier() {
return classifier;
}
@@ -51,6 +54,7 @@ public void setPackaging(String packaging) {
this.packaging = packaging;
}
+ @Override
public String getPackaging() {
return packaging;
}
@@ -59,6 +63,7 @@ public void setIncludesDependencies(boolean includesDependencies) {
this.includesDependencies = includesDependencies;
}
+ @Override
public boolean isIncludesDependencies() {
return includesDependencies;
}
@@ -67,14 +72,18 @@ public void setLanguage(String language) {
this.language = language;
}
+ @Override
public String getLanguage() {
return language;
}
+ @Deprecated
public void setAddedToClasspath(boolean addedToClasspath) {
this.addedToClasspath = addedToClasspath;
}
+ @Override
+ @Deprecated
public boolean isAddedToClasspath() {
return addedToClasspath;
}
diff --git a/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java b/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
index 860fbe3fb528..93c90bd889fa 100644
--- a/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
+++ b/maven-compat/src/test/java/org/apache/maven/repository/TestArtifactHandler.java
@@ -39,30 +39,38 @@ public TestArtifactHandler(String type, String extension) {
this.extension = extension;
}
+ @Override
public String getClassifier() {
return null;
}
+ @Override
public String getDirectory() {
return getPackaging() + "s";
}
+ @Override
public String getExtension() {
return extension;
}
+ @Override
public String getLanguage() {
return "java";
}
+ @Override
public String getPackaging() {
return type;
}
+ @Override
+ @Deprecated
public boolean isAddedToClasspath() {
return true;
}
+ @Override
public boolean isIncludesDependencies() {
return false;
}
diff --git a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java b/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
index 0a083732c05d..198fe6fb6aa3 100644
--- a/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
+++ b/maven-core/src/main/java/org/apache/maven/artifact/handler/DefaultArtifactHandler.java
@@ -37,6 +37,7 @@ public class DefaultArtifactHandler implements ArtifactHandler {
private String language;
+ @Deprecated
private boolean addedToClasspath;
/**
@@ -146,10 +147,12 @@ public void setLanguage(final String language) {
}
@Override
+ @Deprecated
public boolean isAddedToClasspath() {
return addedToClasspath;
}
+ @Deprecated
public void setAddedToClasspath(final boolean addedToClasspath) {
this.addedToClasspath = addedToClasspath;
}
diff --git a/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java b/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
index 87166eb29a75..2ad0541914dc 100644
--- a/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
+++ b/maven-core/src/main/java/org/apache/maven/artifact/handler/manager/DefaultArtifactHandlerManager.java
@@ -26,6 +26,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.maven.api.JavaPathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.artifact.handler.ArtifactHandler;
@@ -60,9 +61,10 @@ public void onEvent(Object event) {
}
}
+ @Override
public ArtifactHandler getArtifactHandler(String id) {
return allHandlers.computeIfAbsent(id, k -> {
- Type type = typeRegistry.getType(id);
+ Type type = typeRegistry.require(id);
return new DefaultArtifactHandler(
id,
type.getExtension(),
@@ -70,11 +72,17 @@ public ArtifactHandler getArtifactHandler(String id) {
null,
null,
type.isIncludesDependencies(),
- type.getLanguage(),
- type.isAddedToClassPath()); // TODO: watch out for module path
+ type.getLanguage().id(),
+ type.getPathTypes().contains(JavaPathType.CLASSES));
+ // TODO: watch out for module path
});
+
+ // Note: here, type decides is artifact added to "build path" (for example during resolution)
+ // and "build path" is intermediate data that is used to create actual Java classpath/modulepath
+ // but to create those, proper filtering should happen via Type properties.
}
+ @Override
public void addHandlers(Map handlers) {
throw new UnsupportedOperationException("Adding handlers programmatically is not supported anymore");
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java b/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
index b5f3f9c61437..d3861f2c571c 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/aether/TypeRegistryAdapter.java
@@ -18,6 +18,7 @@
*/
package org.apache.maven.internal.aether;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Type;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.internal.impl.DefaultType;
@@ -35,17 +36,18 @@ class TypeRegistryAdapter implements ArtifactTypeRegistry {
@Override
public ArtifactType get(String typeId) {
- Type type = typeRegistry.getType(typeId);
+ Type type = typeRegistry.require(typeId);
if (type instanceof ArtifactType) {
return (ArtifactType) type;
}
if (type != null) {
return new DefaultType(
- type.getId(),
+ type.id(),
type.getLanguage(),
type.getExtension(),
type.getClassifier(),
- type.getDependencyProperties());
+ type.isIncludesDependencies(),
+ type.getPathTypes().toArray(new PathType[0]));
}
return null;
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
index ce90e19be732..bc65ac91d881 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
@@ -29,6 +29,7 @@
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
import org.apache.maven.api.*;
import org.apache.maven.api.annotations.Nonnull;
@@ -53,40 +54,49 @@ public abstract class AbstractSession implements InternalSession {
private final Map allDependencies =
Collections.synchronizedMap(new WeakHashMap<>());
+ @Override
public RemoteRepository getRemoteRepository(org.eclipse.aether.repository.RemoteRepository repository) {
return allRepositories.computeIfAbsent(repository, DefaultRemoteRepository::new);
}
+ @Override
public Node getNode(org.eclipse.aether.graph.DependencyNode node) {
return getNode(node, false);
}
+ @Override
public Node getNode(org.eclipse.aether.graph.DependencyNode node, boolean verbose) {
return allNodes.computeIfAbsent(node, n -> new DefaultNode(this, n, verbose));
}
@Nonnull
+ @Override
public Artifact getArtifact(@Nonnull org.eclipse.aether.artifact.Artifact artifact) {
return allArtifacts.computeIfAbsent(artifact, a -> new DefaultArtifact(this, a));
}
@Nonnull
+ @Override
public Dependency getDependency(@Nonnull org.eclipse.aether.graph.Dependency dependency) {
return allDependencies.computeIfAbsent(dependency, d -> new DefaultDependency(this, d));
}
+ @Override
public List getProjects(List projects) {
return projects == null ? null : map(projects, this::getProject);
}
+ @Override
public Project getProject(MavenProject project) {
return allProjects.computeIfAbsent(project.getId(), id -> new DefaultProject(this, project));
}
+ @Override
public List toRepositories(List repositories) {
return repositories == null ? null : map(repositories, this::toRepository);
}
+ @Override
public org.eclipse.aether.repository.RemoteRepository toRepository(RemoteRepository repository) {
if (repository instanceof DefaultRemoteRepository) {
return ((DefaultRemoteRepository) repository).getRepository();
@@ -96,6 +106,7 @@ public org.eclipse.aether.repository.RemoteRepository toRepository(RemoteReposit
}
}
+ @Override
public org.eclipse.aether.repository.LocalRepository toRepository(LocalRepository repository) {
if (repository instanceof DefaultLocalRepository) {
return ((DefaultLocalRepository) repository).getRepository();
@@ -105,22 +116,28 @@ public org.eclipse.aether.repository.LocalRepository toRepository(LocalRepositor
}
}
+ @Override
public List toArtifactRepositories(List repositories) {
return repositories == null ? null : map(repositories, this::toArtifactRepository);
}
+ @Override
public abstract ArtifactRepository toArtifactRepository(RemoteRepository repository);
+ @Override
public List toDependencies(Collection dependencies) {
return dependencies == null ? null : map(dependencies, this::toDependency);
}
+ @Override
public abstract org.eclipse.aether.graph.Dependency toDependency(DependencyCoordinate dependency);
+ @Override
public List toArtifacts(Collection artifacts) {
return artifacts == null ? null : map(artifacts, this::toArtifact);
}
+ @Override
public org.eclipse.aether.artifact.Artifact toArtifact(Artifact artifact) {
File file = getService(ArtifactManager.class)
.getPath(artifact)
@@ -142,6 +159,7 @@ public org.eclipse.aether.artifact.Artifact toArtifact(Artifact artifact) {
file);
}
+ @Override
public org.eclipse.aether.artifact.Artifact toArtifact(ArtifactCoordinate coord) {
if (coord instanceof DefaultArtifactCoordinate) {
return ((DefaultArtifactCoordinate) coord).getCoordinate();
@@ -335,7 +353,8 @@ public Map.Entry resolveArtifact(Artifact artifact) {
@Override
public Map resolveArtifacts(Artifact... artifacts) {
ArtifactCoordinateFactory acf = getService(ArtifactCoordinateFactory.class);
- List coords = map(Arrays.asList(artifacts), a -> acf.create(this, a));
+ List coords =
+ Arrays.stream(artifacts).map(a -> acf.create(this, a)).collect(Collectors.toList());
return resolveArtifacts(coords);
}
@@ -420,6 +439,7 @@ public DependencyCoordinate createDependencyCoordinate(@Nonnull ArtifactCoordina
* @see DependencyCoordinateFactory#create(Session, ArtifactCoordinate)
*/
@Nonnull
+ @Override
public DependencyCoordinate createDependencyCoordinate(@Nonnull Dependency dependency) {
return getService(DependencyCoordinateFactory.class).create(this, dependency);
}
@@ -462,7 +482,7 @@ public Node collectDependencies(@Nonnull DependencyCoordinate dependency) {
@Nonnull
@Override
- public List flattenDependencies(@Nonnull Node node, @Nonnull ResolutionScope scope) {
+ public List flattenDependencies(@Nonnull Node node, @Nonnull PathScope scope) {
return getService(DependencyResolver.class).flatten(this, node, scope);
}
@@ -477,12 +497,40 @@ public List resolveDependencies(List dependencies) {
}
@Override
- public List resolveDependencies(Project project, ResolutionScope scope) {
+ public List resolveDependencies(Project project, PathScope scope) {
return getService(DependencyResolver.class)
.resolve(this, project, scope)
.getPaths();
}
+ @Override
+ public Map> resolveDependencies(
+ @Nonnull DependencyCoordinate dependency,
+ @Nonnull PathScope scope,
+ @Nonnull Collection desiredTypes) {
+ return getService(DependencyResolver.class)
+ .resolve(DependencyResolverRequest.builder()
+ .session(this)
+ .dependency(dependency)
+ .pathScope(scope)
+ .pathTypeFilter(desiredTypes)
+ .build())
+ .getDispatchedPaths();
+ }
+
+ @Override
+ public Map> resolveDependencies(
+ @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection desiredTypes) {
+ return getService(DependencyResolver.class)
+ .resolve(DependencyResolverRequest.builder()
+ .session(this)
+ .project(project)
+ .pathScope(scope)
+ .pathTypeFilter(desiredTypes)
+ .build())
+ .getDispatchedPaths();
+ }
+
@Override
public Path getPathForLocalArtifact(@Nonnull Artifact artifact) {
return getService(LocalRepositoryManager.class).getPathForLocalArtifact(this, getLocalRepository(), artifact);
@@ -518,4 +566,34 @@ public Version resolveVersion(ArtifactCoordinate artifact) {
public List resolveVersionRange(ArtifactCoordinate artifact) {
return getService(VersionRangeResolver.class).resolve(this, artifact).getVersions();
}
+
+ @Override
+ public Type requireType(String id) {
+ return getService(TypeRegistry.class).require(id);
+ }
+
+ @Override
+ public Language requireLanguage(String id) {
+ return getService(LanguageRegistry.class).require(id);
+ }
+
+ @Override
+ public Packaging requirePackaging(String id) {
+ return getService(PackagingRegistry.class).require(id);
+ }
+
+ @Override
+ public ProjectScope requireProjectScope(String id) {
+ return getService(ProjectScopeRegistry.class).require(id);
+ }
+
+ @Override
+ public DependencyScope requireDependencyScope(String id) {
+ return DependencyScope.forId(id);
+ }
+
+ @Override
+ public PathScope requirePathScope(String id) {
+ return getService(PathScopeRegistry.class).require(id);
+ }
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
index 3f2f06893ebf..8137a2e2c968 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependency.java
@@ -23,30 +23,26 @@
import org.apache.maven.api.Artifact;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.DependencyCoordinate;
-import org.apache.maven.api.DependencyProperties;
-import org.apache.maven.api.Scope;
+import org.apache.maven.api.DependencyScope;
import org.apache.maven.api.Type;
import org.apache.maven.api.Version;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
-import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.repository.internal.DefaultModelVersionParser;
import org.eclipse.aether.artifact.ArtifactProperties;
import static org.apache.maven.internal.impl.Utils.nonNull;
public class DefaultDependency implements Dependency {
+
private final InternalSession session;
private final org.eclipse.aether.graph.Dependency dependency;
- private final DependencyProperties dependencyProperties;
private final String key;
public DefaultDependency(
@Nonnull InternalSession session, @Nonnull org.eclipse.aether.graph.Dependency dependency) {
this.session = nonNull(session, "session");
this.dependency = nonNull(dependency, "dependency");
- this.dependencyProperties =
- new DefaultDependencyProperties(dependency.getArtifact().getProperties());
this.key = getGroupId()
+ ':'
+ getArtifactId()
@@ -102,12 +98,7 @@ public Type getType() {
String type = dependency
.getArtifact()
.getProperty(ArtifactProperties.TYPE, dependency.getArtifact().getExtension());
- return session.getService(TypeRegistry.class).getType(type);
- }
-
- @Override
- public DependencyProperties getDependencyProperties() {
- return dependencyProperties;
+ return session.requireType(type);
}
@Override
@@ -117,8 +108,8 @@ public boolean isSnapshot() {
@Nonnull
@Override
- public Scope getScope() {
- return Scope.get(dependency.getScope());
+ public DependencyScope getScope() {
+ return session.requireDependencyScope(dependency.getScope());
}
@Nullable
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
index 54121e8da6d8..c0420f5fbba4 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
@@ -23,7 +23,6 @@
import org.apache.maven.api.*;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
-import org.apache.maven.api.services.TypeRegistry;
import org.eclipse.aether.artifact.ArtifactProperties;
import static org.apache.maven.internal.impl.Utils.nonNull;
@@ -73,13 +72,13 @@ public Type getType() {
String type = dependency
.getArtifact()
.getProperty(ArtifactProperties.TYPE, dependency.getArtifact().getExtension());
- return session.getService(TypeRegistry.class).getType(type);
+ return session.requireType(type);
}
@Nonnull
@Override
- public Scope getScope() {
- return Scope.get(dependency.getScope());
+ public DependencyScope getScope() {
+ return session.requireDependencyScope(dependency.getScope());
}
@Nullable
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyProperties.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyProperties.java
deleted file mode 100644
index 2ee780713034..000000000000
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyProperties.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.internal.impl;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.maven.api.DependencyProperties;
-import org.apache.maven.api.annotations.Nonnull;
-
-import static org.apache.maven.internal.impl.Utils.nonNull;
-
-/**
- * Default implementation of artifact properties.
- */
-public class DefaultDependencyProperties implements DependencyProperties {
- private final Map properties;
-
- public DefaultDependencyProperties(String... flags) {
- this(Arrays.asList(flags));
- }
-
- public DefaultDependencyProperties(@Nonnull Collection flags) {
- nonNull(flags, "flags");
- HashMap map = new HashMap<>();
- for (String flag : flags) {
- map.put(flag, Boolean.TRUE.toString());
- }
- this.properties = Collections.unmodifiableMap(map);
- }
-
- public DefaultDependencyProperties(@Nonnull Map properties) {
- this.properties = Collections.unmodifiableMap(nonNull(properties, "properties"));
- }
-
- @Nonnull
- @Override
- public Map asMap() {
- return properties;
- }
-
- @Override
- public boolean checkFlag(@Nonnull String flag) {
- nonNull(flag, "flag");
- return Boolean.parseBoolean(properties.getOrDefault(flag, ""));
- }
-}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
index c5ba1a32b02d..65a1a18ea05f 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
@@ -21,25 +21,25 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.maven.api.*;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.ArtifactCoordinate;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Project;
-import org.apache.maven.api.ResolutionScope;
-import org.apache.maven.api.Scope;
import org.apache.maven.api.Session;
import org.apache.maven.api.services.*;
import org.apache.maven.lifecycle.LifecycleExecutionException;
@@ -58,7 +58,7 @@
public class DefaultDependencyResolver implements DependencyResolver {
@Override
- public List flatten(Session s, Node node, ResolutionScope scope) throws DependencyResolverException {
+ public List flatten(Session s, Node node, PathScope scope) throws DependencyResolverException {
InternalSession session = InternalSession.from(s);
DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
List dependencies = session.getRepositorySystem()
@@ -67,70 +67,81 @@ public List flatten(Session s, Node node, ResolutionScope scope) throws De
return map(dependencies, session::getNode);
}
- private static DependencyFilter getScopeDependencyFilter(ResolutionScope scope) {
- Set scopes = scope.scopes().stream().map(Scope::id).collect(Collectors.toSet());
+ private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
+ Set scopes =
+ scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
return (n, p) -> {
org.eclipse.aether.graph.Dependency d = n.getDependency();
return d == null || scopes.contains(d.getScope());
};
}
+ /**
+ * Collects, flattens and resolves the dependencies.
+ *
+ * @param request the request to resolve
+ * @return the result of the resolution
+ */
@Override
- public DependencyResolverResult resolve(DependencyResolverRequest request)
+ public DependencyResolverResult resolve(final DependencyResolverRequest request)
throws DependencyCollectorException, DependencyResolverException, ArtifactResolverException {
- nonNull(request, "request can not be null");
- InternalSession session = InternalSession.from(request.getSession());
-
+ nonNull(request, "request");
+ final InternalSession session = InternalSession.from(request.getSession());
+ final Predicate filter = request.getPathTypeFilter();
+ final PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache.
if (request.getProject().isPresent()) {
- DependencyResolutionResult result = resolveDependencies(
- request.getSession(), request.getProject().get(), request.getResolutionScope());
+ final DependencyResolutionResult resolved = resolveDependencies(
+ request.getSession(), request.getProject().get(), request.getPathScope());
- Map nodes = stream(
- result.getDependencyGraph())
+ final Map nodes = stream(
+ resolved.getDependencyGraph())
.filter(n -> n.getDependency() != null)
.collect(Collectors.toMap(DependencyNode::getDependency, n -> n));
- Node root = session.getNode(result.getDependencyGraph());
- List dependencies = new ArrayList<>();
- Map artifacts = new LinkedHashMap<>();
- List paths = new ArrayList<>();
- for (org.eclipse.aether.graph.Dependency dep : result.getResolvedDependencies()) {
- dependencies.add(session.getNode(nodes.get(dep)));
+ final Node root = session.getNode(resolved.getDependencyGraph());
+ final List deprendencies = resolved.getResolvedDependencies();
+ final DefaultDependencyResolverResult result =
+ new DefaultDependencyResolverResult(resolved.getCollectionErrors(), root, deprendencies.size());
+ for (org.eclipse.aether.graph.Dependency dep : deprendencies) {
+ Node node = session.getNode(nodes.get(dep));
Path path = dep.getArtifact().getFile().toPath();
- artifacts.put(session.getDependency(dep), path);
- paths.add(path);
+ try {
+ result.addDependency(node, session.getDependency(dep), filter, path, cache);
+ } catch (IOException e) {
+ throw cannotReadModuleInfo(path, e);
+ }
}
- return new DefaultDependencyResolverResult(
- result.getCollectionErrors(), root, dependencies, paths, artifacts);
+ return result;
}
- DependencyCollectorResult collectorResult =
+ final DependencyCollectorResult collectorResult =
session.getService(DependencyCollector.class).collect(request);
- List nodes = flatten(session, collectorResult.getRoot(), request.getResolutionScope());
- List deps =
- nodes.stream().map(Node::getDependency).filter(Objects::nonNull).collect(Collectors.toList());
- List coordinates =
- deps.stream().map(Artifact::toCoordinate).collect(Collectors.toList());
- Map artifacts = session.resolveArtifacts(coordinates);
- Map dependencies = new LinkedHashMap<>();
- List paths = new ArrayList<>();
- for (Dependency d : deps) {
- Path path = artifacts.get(d);
- if (dependencies.put(d, path) != null) {
- throw new IllegalStateException("Duplicate key");
+ final List nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
+ final List coordinates = nodes.stream()
+ .map(Node::getDependency)
+ .filter(Objects::nonNull)
+ .map(Artifact::toCoordinate)
+ .collect(Collectors.toList());
+ final Map artifacts = session.resolveArtifacts(coordinates);
+ final DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(
+ collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
+ for (Node node : nodes) {
+ Dependency d = node.getDependency();
+ Path path = (d != null) ? artifacts.get(d) : null;
+ try {
+ result.addDependency(node, d, filter, path, cache);
+ } catch (IOException e) {
+ throw cannotReadModuleInfo(path, e);
}
- paths.add(path);
}
-
- return new DefaultDependencyResolverResult(
- collectorResult.getExceptions(), collectorResult.getRoot(), nodes, paths, dependencies);
+ return result;
}
- private Stream stream(DependencyNode node) {
- return Stream.concat(Stream.of(node), node.getChildren().stream().flatMap(this::stream));
+ private static Stream stream(final DependencyNode node) {
+ return Stream.concat(Stream.of(node), node.getChildren().stream().flatMap(DefaultDependencyResolver::stream));
}
- private DependencyResolutionResult resolveDependencies(Session session, Project project, ResolutionScope scope) {
+ private DependencyResolutionResult resolveDependencies(Session session, Project project, PathScope scope) {
Collection toResolve = toScopes(scope);
try {
LifecycleDependencyResolver lifecycleDependencyResolver =
@@ -147,57 +158,15 @@ private DependencyResolutionResult resolveDependencies(Session session, Project
}
}
- private MavenProject getMavenProject(Project project) {
+ private static MavenProject getMavenProject(final Project project) {
return ((DefaultProject) project).getProject();
}
- private Collection toScopes(ResolutionScope scope) {
- return map(scope.scopes(), Scope::id);
+ private Collection toScopes(PathScope scope) {
+ return map(scope.dependencyScopes(), DependencyScope::id);
}
- static class DefaultDependencyResolverResult implements DependencyResolverResult {
- private final List exceptions;
- private final Node root;
- private final List nodes;
- private final List paths;
- private final Map dependencies;
-
- DefaultDependencyResolverResult(
- List exceptions,
- Node root,
- List nodes,
- List paths,
- Map dependencies) {
- this.exceptions = exceptions;
- this.root = root;
- this.nodes = nodes;
- this.paths = paths;
- this.dependencies = dependencies;
- }
-
- @Override
- public List getExceptions() {
- return exceptions;
- }
-
- @Override
- public Node getRoot() {
- return root;
- }
-
- @Override
- public List getNodes() {
- return nodes;
- }
-
- @Override
- public List getPaths() {
- return paths;
- }
-
- @Override
- public Map getDependencies() {
- return dependencies;
- }
+ private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
+ return new DependencyResolverException("Cannot read module information of " + path, cause);
}
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java
new file mode 100644
index 000000000000..b41d307ab363
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolverResult.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.maven.api.Dependency;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Node;
+import org.apache.maven.api.PathType;
+import org.apache.maven.api.services.DependencyResolverRequest;
+import org.apache.maven.api.services.DependencyResolverResult;
+
+/**
+ * The result of collecting dependencies with a dependency resolver.
+ * New instances are initially empty. Callers must populate with calls
+ * to the following methods, in that order:
+ *
+ *
+ * - {@link #addOutputDirectory(Path, Path, PathModularizationCache)} (optional)
+ * - {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}
+ *
+ *
+ * @see DefaultDependencyResolver#resolve(DependencyResolverRequest)
+ */
+final class DefaultDependencyResolverResult implements DependencyResolverResult {
+ /**
+ * The exceptions that occurred while building the dependency graph.
+ */
+ private final List exceptions;
+
+ /**
+ * The root node of the dependency graph.
+ */
+ private final Node root;
+
+ /**
+ * The ordered list of the flattened dependency nodes.
+ */
+ private final List nodes;
+
+ /**
+ * The file paths of all dependencies, regardless on which Java tool option those paths should be placed.
+ */
+ private final List paths;
+
+ /**
+ * The file paths of all dependencies, dispatched according the Java options where to place them.
+ */
+ private final Map> dispatchedPaths;
+
+ /**
+ * The dependencies together with the path to each dependency.
+ */
+ private final Map dependencies;
+
+ /**
+ * Information about modules in the main output. This field is initially null and is set to a non-null
+ * value when the output directories have been set, or when it is too late for setting them.
+ */
+ private PathModularization outputModules;
+
+ /**
+ * Creates an initially empty result. Callers should add path elements by calls
+ * to {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}.
+ *
+ * @param exceptions the exceptions that occurred while building the dependency graph
+ * @param root the root node of the dependency graph
+ * @param count estimated number of dependencies
+ */
+ DefaultDependencyResolverResult(final List exceptions, final Node root, final int count) {
+ this.exceptions = exceptions;
+ this.root = root;
+ nodes = new ArrayList<>(count);
+ paths = new ArrayList<>(count);
+ dispatchedPaths = new LinkedHashMap<>();
+ dependencies = new LinkedHashMap<>(count + count / 3);
+ }
+
+ /**
+ * Adds the given path element to the specified type of path.
+ *
+ * @param type the type of path (class-path, module-path, …)
+ * @param path the path element to add
+ */
+ private void addPathElement(final PathType type, final Path path) {
+ dispatchedPaths.computeIfAbsent(type, (t) -> new ArrayList<>()).add(path);
+ }
+
+ /**
+ * Adds main and test output directories to the result. This method adds the main output directory
+ * to the module-path if it contains a {@code module-info.class}, or to the class-path otherwise.
+ * For the test output directory, the rules are more complex and are governed by the fact that
+ * Java does not accept the placement of two modules of the same name on the module-path.
+ * So the modular test output directory usually needs to be placed in a {@code --path-module} option.
+ *
+ *
+ * - If the test output directory is modular, then:
+ *
+ * - If a test module name is identical to a main module name,
+ * place the test directory in a {@code --patch-module} option.
+ * - Otherwise, place the test directory on the module-path. However, this case
+ * (a module existing only in test output, not in main output) should be uncommon.
+ *
+ *
+ * - Otherwise (test output contains no module information), then:
+ *
+ * - If the main output is on the module-path, place the test output
+ * on a {@code --patch-module} option.
+ * - Otherwise (main output on the class-path), place the test output on the class-path too.
+ *
+ *
+ *
+ *
+ * This method must be invoked before {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}
+ * if output directories are desired on the class-path or module-path.
+ * This method can be invoked at most once.
+ *
+ * @param main the main output directory, or {@code null} if none
+ * @param test the test output directory, or {@code null} if none
+ * @param cache cache of module information about each dependency
+ * @throws IOException if an error occurred while reading module information
+ *
+ * TODO: this is currently not called
+ */
+ void addOutputDirectory(final Path main, final Path test, final PathModularizationCache cache) throws IOException {
+ if (outputModules != null) {
+ throw new IllegalStateException("Output directories must be set first and only once.");
+ }
+ if (main != null) {
+ outputModules = cache.getModuleInfo(main);
+ addPathElement(outputModules.getPathType(), main);
+ } else {
+ outputModules = PathModularization.NONE;
+ }
+ if (test != null) {
+ boolean addToClasspath = true;
+ final PathModularization testModules = cache.getModuleInfo(test);
+ final boolean isModuleHierarchy = outputModules.isModuleHierarchy() || testModules.isModuleHierarchy();
+ for (final String moduleName : outputModules.getModuleNames().values()) {
+ Path subdir = test;
+ if (isModuleHierarchy) {
+ // If module hierarchy is used, the directory names shall be the module names.
+ final Path path = test.resolve(moduleName);
+ if (!Files.isDirectory(path)) {
+ // Main module without tests. It is okay.
+ continue;
+ }
+ subdir = path;
+ }
+ // When the same module is found in main and test output, the latter is patching the former.
+ addPathElement(JavaPathType.patchModule(moduleName), subdir);
+ addToClasspath = false;
+ }
+ /*
+ * If the test output directory provides some modules of its own, add them.
+ * Except for this unusual case, tests should never be added to the module-path.
+ */
+ for (final Map.Entry entry :
+ testModules.getModuleNames().entrySet()) {
+ if (!outputModules.containsModule(entry.getValue())) {
+ addPathElement(JavaPathType.MODULES, entry.getKey());
+ addToClasspath = false;
+ }
+ }
+ if (addToClasspath) {
+ addPathElement(JavaPathType.CLASSES, test);
+ }
+ }
+ }
+
+ /**
+ * Adds a dependency to the result. This method populates the {@link #nodes}, {@link #paths},
+ * {@link #dispatchedPaths} and {@link #dependencies} collections with the given arguments.
+ *
+ * @param node the dependency node
+ * @param dep the dependency for the given node, or {@code null} if none
+ * @param filter filter the paths accepted by the tool which will consume the path.
+ * @param path the path to the dependency, or {@code null} if the dependency was null
+ * @param cache cache of module information about each dependency
+ * @throws IOException if an error occurred while reading module information
+ */
+ void addDependency(
+ final Node node,
+ final Dependency dep,
+ final Predicate filter,
+ final Path path,
+ final PathModularizationCache cache)
+ throws IOException {
+ nodes.add(node);
+ if (dep == null) {
+ return;
+ }
+ if (dependencies.put(dep, path) != null) {
+ throw new IllegalStateException("Duplicated key: " + dep);
+ }
+ if (path == null) {
+ return;
+ }
+ paths.add(path);
+ /*
+ * Dispatch the dependency to class-path, module-path, patch-module path, etc.
+ * according the dependency properties. We need to process patch-module first,
+ * because this type depends on whether a module of the same name has already
+ * been added on the module-type.
+ */
+ // final DependencyProperties properties = dep.getDependencyProperties();
+ final Set pathTypes = dep.getType().getPathTypes();
+ if (containsPatches(pathTypes)) {
+ if (outputModules == null) {
+ // For telling users that it is too late for setting the output directory.
+ outputModules = PathModularization.NONE;
+ }
+ PathType type = null;
+ for (Map.Entry info :
+ cache.getModuleInfo(path).getModuleNames().entrySet()) {
+ String moduleName = info.getValue();
+ type = JavaPathType.patchModule(moduleName);
+ if (!containsModule(moduleName, cache)) {
+ /*
+ * Not patching an existing module. This case should be unusual. If it nevertheless
+ * happens, add on class-path or module-path if allowed, or keep patching otherwise.
+ * The latter case (keep patching) is okay if the main module will be defined later.
+ */
+ type = cache.selectPathType(pathTypes, filter, path).orElse(type);
+ }
+ addPathElement(type, info.getKey());
+ // There is usually no more than one element, but nevertheless allow multi-modules.
+ }
+ /*
+ * If the dependency has no module information, search for an artifact of the same groupId
+ * and artifactId. If one is found, we are patching that module. If none is found, add the
+ * dependency as a normal dependency.
+ */
+ if (type == null) {
+ Path main = findArtifactPath(dep.getGroupId(), dep.getArtifactId());
+ if (main != null) {
+ for (Map.Entry info :
+ cache.getModuleInfo(main).getModuleNames().entrySet()) {
+ type = JavaPathType.patchModule(info.getValue());
+ addPathElement(type, info.getKey());
+ // There is usually no more than one element, but nevertheless allow multi-modules.
+ }
+ }
+ }
+ if (type != null) {
+ return; // Dependency added, we are done.
+ }
+ }
+ cache.selectPathType(pathTypes, filter, path).ifPresent((type) -> addPathElement(type, path));
+ }
+
+ /**
+ * Returns whether the given set of path types contains at least one patch for a module.
+ */
+ private boolean containsPatches(final Set types) {
+ for (PathType type : types) {
+ if (type instanceof JavaPathType.Modular) {
+ type = ((JavaPathType.Modular) type).rawType();
+ }
+ if (JavaPathType.PATCH_MODULE.equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether at least one previously added modular dependency contains a module of the given name.
+ *
+ * @param moduleName name of the module to search
+ * @param cache cache of module information about each dependency
+ */
+ private boolean containsModule(final String moduleName, final PathModularizationCache cache) throws IOException {
+ for (Path path : dispatchedPaths.getOrDefault(JavaPathType.MODULES, Collections.emptyList())) {
+ if (cache.getModuleInfo(path).containsModule(moduleName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Searches an artifact of the given group and artifact identifiers, and returns its path
+ *
+ * @param group the group identifier to search
+ * @param artifact the artifact identifier to search
+ * @return path to the desired artifact, or {@code null} if not found
+ */
+ private Path findArtifactPath(final String group, final String artifact) throws IOException {
+ for (final Map.Entry entry : dependencies.entrySet()) {
+ Dependency dep = entry.getKey();
+ if (group.equals(dep.getGroupId()) && artifact.equals(dep.getArtifactId())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List getExceptions() {
+ return exceptions;
+ }
+
+ @Override
+ public Node getRoot() {
+ return root;
+ }
+
+ @Override
+ public List getNodes() {
+ return nodes;
+ }
+
+ @Override
+ public List getPaths() {
+ return paths;
+ }
+
+ @Override
+ public Map> getDispatchedPaths() {
+ return dispatchedPaths;
+ }
+
+ @Override
+ public Map getDependencies() {
+ return dependencies;
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java
new file mode 100644
index 000000000000..2a5b8a63b53e
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultPackagingRegistry.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.maven.api.Packaging;
+import org.apache.maven.api.Type;
+import org.apache.maven.api.services.PackagingRegistry;
+import org.apache.maven.api.services.TypeRegistry;
+import org.apache.maven.lifecycle.mapping.LifecycleMapping;
+
+/**
+ * TODO: this is session scoped as SPI can contribute.
+ */
+@Named
+@Singleton
+public class DefaultPackagingRegistry implements PackagingRegistry {
+ private final Map lifecycleMappings;
+
+ private final TypeRegistry typeRegistry;
+
+ @Inject
+ public DefaultPackagingRegistry(Map lifecycleMappings, TypeRegistry typeRegistry) {
+ this.lifecycleMappings = lifecycleMappings;
+ this.typeRegistry = typeRegistry;
+ }
+
+ @Override
+ public Optional lookup(String id) {
+ LifecycleMapping lifecycleMapping = lifecycleMappings.get(id);
+ if (lifecycleMapping == null) {
+ return Optional.empty();
+ }
+ Type type = typeRegistry.lookup(id).orElse(null);
+ if (type == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(new Packaging() {
+ @Override
+ public String id() {
+ return id;
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+ });
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
index f438261aee5d..e2f59668eca8 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
@@ -27,7 +27,6 @@
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.model.DependencyManagement;
import org.apache.maven.api.model.Model;
-import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.ProjectArtifact;
import org.eclipse.aether.util.artifact.ArtifactIdUtils;
@@ -38,10 +37,12 @@ public class DefaultProject implements Project {
private final InternalSession session;
private final MavenProject project;
+ private final Packaging packaging;
public DefaultProject(InternalSession session, MavenProject project) {
this.session = session;
this.project = project;
+ this.packaging = session.requirePackaging(project.getPackaging());
}
public InternalSession getSession() {
@@ -86,8 +87,8 @@ public List getArtifacts() {
@Nonnull
@Override
- public String getPackaging() {
- return project.getPackaging();
+ public Packaging getPackaging() {
+ return packaging;
}
@Nonnull
@@ -175,13 +176,13 @@ public String getExtension() {
@Override
public Type getType() {
String type = dependency.getType();
- return session.getService(TypeRegistry.class).getType(type);
+ return session.requireType(type);
}
@Nonnull
@Override
- public Scope getScope() {
- return Scope.get(dependency.getScope());
+ public DependencyScope getScope() {
+ return session.requireDependencyScope(dependency.getScope());
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultType.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultType.java
index 6297f5c4dde0..cc5534f4cdb1 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultType.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultType.java
@@ -18,10 +18,12 @@
*/
package org.apache.maven.internal.impl;
-import java.util.HashMap;
+import java.util.*;
import java.util.Map;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Type;
import org.eclipse.aether.artifact.ArtifactProperties;
import org.eclipse.aether.artifact.ArtifactType;
@@ -29,37 +31,44 @@
import static org.apache.maven.internal.impl.Utils.nonNull;
public class DefaultType implements Type, ArtifactType {
+ private final String id;
+
+ private final Language language;
+
private final String extension;
private final String classifier;
-
- private final DependencyProperties dependencyProperties;
+ private final boolean includesDependencies;
+ private final Set pathTypes;
public DefaultType(
String id,
- String language,
+ Language language,
String extension,
String classifier,
- DependencyProperties dependencyProperties) {
- nonNull(id, "id");
- nonNull(language, "language");
+ boolean includesDependencies,
+ PathType... pathTypes) {
+ this.id = nonNull(id, "id");
+ this.language = nonNull(language, "language");
this.extension = nonNull(extension, "extension");
this.classifier = classifier;
- nonNull(dependencyProperties, "dependencyProperties");
- HashMap props = new HashMap<>(dependencyProperties.asMap());
- props.put(ArtifactProperties.TYPE, id);
- props.put(ArtifactProperties.LANGUAGE, language);
- this.dependencyProperties = new DefaultDependencyProperties(props);
+ this.includesDependencies = includesDependencies;
+ this.pathTypes = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(pathTypes)));
+ }
+
+ @Override
+ public String id() {
+ return id;
}
@Override
public String getId() {
- return dependencyProperties.asMap().get(ArtifactProperties.TYPE);
+ return id();
}
@Override
- public String getLanguage() {
- return dependencyProperties.asMap().get(ArtifactProperties.LANGUAGE);
+ public Language getLanguage() {
+ return language;
}
@Override
@@ -73,12 +82,22 @@ public String getClassifier() {
}
@Override
- public DependencyProperties getDependencyProperties() {
- return dependencyProperties;
+ public boolean isIncludesDependencies() {
+ return this.includesDependencies;
+ }
+
+ public Set getPathTypes() {
+ return this.pathTypes;
}
@Override
public Map getProperties() {
- return getDependencyProperties().asMap();
+ Map properties = new HashMap<>();
+ properties.put(ArtifactProperties.TYPE, this.id);
+ properties.put(ArtifactProperties.LANGUAGE, this.language.id());
+ properties.put(ArtifactProperties.INCLUDES_DEPENDENCIES, String.valueOf(includesDependencies));
+ properties.put(
+ ArtifactProperties.CONSTITUTES_BUILD_PATH, String.valueOf(pathTypes.contains(JavaPathType.CLASSES)));
+ return Collections.unmodifiableMap(properties);
}
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
index cebd30cc666a..0a74fa7f0489 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultTypeRegistry.java
@@ -22,13 +22,13 @@
import javax.inject.Named;
import javax.inject.Singleton;
-import java.util.ArrayList;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.maven.api.DependencyProperties;
import org.apache.maven.api.Type;
import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.services.LanguageRegistry;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.handler.manager.LegacyArtifactHandlerManager;
@@ -42,6 +42,8 @@
public class DefaultTypeRegistry extends AbstractEventSpy implements TypeRegistry {
private final Map types;
+ private final LanguageRegistry languageRegistry;
+
private final ConcurrentHashMap usedTypes;
private final ConcurrentHashMap legacyTypes;
@@ -49,8 +51,10 @@ public class DefaultTypeRegistry extends AbstractEventSpy implements TypeRegistr
private final LegacyArtifactHandlerManager manager;
@Inject
- public DefaultTypeRegistry(Map types, LegacyArtifactHandlerManager manager) {
+ public DefaultTypeRegistry(
+ Map types, LanguageRegistry languageRegistry, LegacyArtifactHandlerManager manager) {
this.types = nonNull(types, "types");
+ this.languageRegistry = nonNull(languageRegistry, "languageRegistry");
this.usedTypes = new ConcurrentHashMap<>();
this.legacyTypes = new ConcurrentHashMap<>();
this.manager = nonNull(manager, "artifactHandlerManager");
@@ -67,9 +71,14 @@ public void onEvent(Object event) {
}
}
+ @Override
+ public Optional lookup(String id) {
+ return Optional.of(require(id));
+ }
+
@Override
@Nonnull
- public Type getType(String id) {
+ public Type require(String id) {
nonNull(id, "id");
return usedTypes.computeIfAbsent(id, i -> {
Type type = types.get(id);
@@ -78,19 +87,14 @@ public Type getType(String id) {
type = legacyTypes.computeIfAbsent(id, k -> {
// Copy data as the ArtifactHandler is not immutable, but Type should be.
ArtifactHandler handler = manager.getArtifactHandler(id);
- ArrayList flags = new ArrayList<>();
- if (handler.isAddedToClasspath()) {
- flags.add(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT);
- }
- if (handler.isIncludesDependencies()) {
- flags.add(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES);
- }
return new DefaultType(
id,
- handler.getLanguage(),
+ languageRegistry.require(handler.getLanguage()),
handler.getExtension(),
handler.getClassifier(),
- new DefaultDependencyProperties(flags));
+ handler.isIncludesDependencies()
+ // TODO: add path types
+ );
});
}
return type;
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java b/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java
new file mode 100644
index 000000000000..d512e74b4635
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/ExtensibleEnumRegistries.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.SessionScoped;
+import org.apache.maven.api.*;
+import org.apache.maven.api.services.*;
+import org.apache.maven.api.spi.*;
+
+public class ExtensibleEnumRegistries {
+
+ @Named
+ @SessionScoped
+ public static class DefaultPathScopeRegistry extends DefaultExtensibleEnumRegistry
+ implements PathScopeRegistry {
+
+ @Inject
+ public DefaultPathScopeRegistry(List providers) {
+ super(
+ providers,
+ PathScope.MAIN_COMPILE,
+ PathScope.MAIN_RUNTIME,
+ PathScope.TEST_COMPILE,
+ PathScope.TEST_RUNTIME);
+ }
+ }
+
+ @Named
+ @SessionScoped
+ public static class DefaultProjectScopeRegistry
+ extends DefaultExtensibleEnumRegistry implements ProjectScopeRegistry {
+
+ @Inject
+ public DefaultProjectScopeRegistry(List providers) {
+ super(providers, ProjectScope.MAIN, ProjectScope.TEST);
+ }
+ }
+
+ @Named
+ @Singleton
+ public static class DefaultLanguageRegistry extends DefaultExtensibleEnumRegistry
+ implements LanguageRegistry {
+
+ @Inject
+ public DefaultLanguageRegistry(List providers) {
+ super(providers, Language.NONE, Language.JAVA_FAMILY);
+ }
+ }
+
+ static class DefaultExtensibleEnumRegistry>
+ implements ExtensibleEnumRegistry {
+
+ private final Map values;
+
+ DefaultExtensibleEnumRegistry(List providers, T... builtinValues) {
+ values = Stream.concat(
+ Stream.of(builtinValues), providers.stream().flatMap(p -> p.provides().stream()))
+ .collect(Collectors.toMap(t -> t.id(), t -> t));
+ }
+
+ @Override
+ public Optional lookup(String id) {
+ return Optional.ofNullable(values.get(id));
+ }
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java
new file mode 100644
index 000000000000..631f8344c254
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularization.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Information about the modules contained in a path element.
+ * The path element may be a JAR file or a directory. Directories may use either package hierarchy
+ * or module hierarchy, but not module source hierarchy. The latter is excluded because this class
+ * is for path elements of compiled codes.
+ */
+final class PathModularization {
+ /**
+ * A unique constant for all non-modular dependencies.
+ */
+ public static final PathModularization NONE = new PathModularization();
+
+ /**
+ * Name of the file to use as a sentinel value for deciding if a directory or a JAR is modular.
+ */
+ private static final String MODULE_INFO = "module-info.class";
+
+ /**
+ * The attribute for automatic module name in {@code META-INF/MANIFEST.MF} files.
+ */
+ private static final Attributes.Name AUTO_MODULE_NAME = new Attributes.Name("Automatic-Module-Name");
+
+ /**
+ * Module information for the path specified at construction time.
+ * This map is usually either empty if no module was found, or a singleton map.
+ * It may however contain more than one entry if module hierarchy was detected,
+ * in which case there is one key per sub-directory.
+ *
+ * This map may contain null values if the constructor was invoked with {@code resolve}
+ * parameter set to false. This is more efficient when only the module existence needs to
+ * be tested, and module descriptors are not needed.
+ *
+ * @see #getModuleNames()
+ */
+ private final Map descriptors;
+
+ /**
+ * Whether module hierarchy was detected. If false, then package hierarchy is assumed.
+ * In a package hierarchy, the {@linkplain #descriptors} map has either zero or one entry.
+ * In a module hierarchy, the descriptors map may have an arbitrary number of entries,
+ * including one (so the map size cannot be used as a criterion).
+ *
+ * @see #isModuleHierarchy()
+ */
+ private final boolean isModuleHierarchy;
+
+ /**
+ * Constructs an empty instance for non-modular dependencies.
+ *
+ * @see #NONE
+ */
+ private PathModularization() {
+ descriptors = Collections.emptyMap();
+ isModuleHierarchy = false;
+ }
+
+ /**
+ * Finds module information in the given JAR file, output directory, or test output directory.
+ * If no module is found, or if module information cannot be extracted, then this constructor
+ * builds an empty map.
+ *
+ * If the {@code resolve} parameter value is {@code false}, then some or all map values may
+ * be null instead of the actual module name. This option can avoid the cost of reading module
+ * descriptors when only the modules existence needs to be verified.
+ *
+ * Algorithm:
+ * If the given path is a directory, then there is a choice:
+ *
+ *
+ * - Package hierarchy: if a {@code module-info.class} file is found at the root,
+ * then builds a singleton map with the module name declared in that descriptor.
+ * - Module hierarchy: if {@code module-info.class} files are found in sub-directories,
+ * at a deep intentionally restricted to one level, then builds a map of module names found
+ * in the descriptor of each sub-directory.
+ *
+ *
+ * Otherwise if the given path is a JAR file, then there is a choice:
+ *
+ * - If a {@code module-info.class} file is found in the root directory or in a
+ * {@code "META-INF/versions/{n}/"} subdirectory, builds a singleton map with
+ * the module name declared in that descriptor.
+ * - Otherwise if an {@code "Automatic-Module-Name"} attribute is declared in the
+ * {@code META-INF/MANIFEST.MF} file, builds a singleton map with the value of that attribute.
+ *
+ *
+ * Otherwise builds an empty map.
+ *
+ * @param path directory or JAR file to test
+ * @param resolve whether the module names are requested. If false, null values may be used instead
+ * @throws IOException if an error occurred while reading the JAR file or the module descriptor
+ */
+ PathModularization(final Path path, final boolean resolve) throws IOException {
+ if (Files.isDirectory(path)) {
+ /*
+ * Package hierarchy: only one module with descriptor at the root.
+ * This is the layout of output directories in projects using the
+ * classical (Java 8 and before) way to organize source files.
+ */
+ Path file = path.resolve(MODULE_INFO);
+ if (Files.isRegularFile(file)) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = Files.newInputStream(file)) {
+ name = getModuleName(in);
+ }
+ }
+ descriptors = Collections.singletonMap(file, name);
+ isModuleHierarchy = false;
+ return;
+ }
+ /*
+ * Module hierarchy: many modules, one per directory, with descriptor at the root of the sub-directory.
+ * This is the layout of output directories in projects using the new (Java 9 and later) way to organize
+ * source files.
+ */
+ if (Files.isDirectory(file)) {
+ final Map names = new HashMap<>();
+ try (Stream subdirs = Files.list(file)) {
+ subdirs.filter(Files::isDirectory).forEach((subdir) -> {
+ Path mf = subdir.resolve(MODULE_INFO);
+ if (Files.isRegularFile(mf)) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = Files.newInputStream(mf)) {
+ name = getModuleName(in);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ names.put(mf, name);
+ }
+ });
+ } catch (UncheckedIOException e) {
+ throw e.getCause();
+ }
+ if (!names.isEmpty()) {
+ descriptors = Collections.unmodifiableMap(names);
+ isModuleHierarchy = true;
+ return;
+ }
+ }
+ } else if (Files.isRegularFile(path)) {
+ /*
+ * JAR file: can contain only one module, with descriptor at the root.
+ * If no descriptor, the "Automatic-Module-Name" manifest attribute is
+ * taken as a fallback.
+ */
+ try (JarFile jar = new JarFile(path.toFile())) {
+ final ZipEntry entry = jar.getEntry(MODULE_INFO);
+ if (entry != null) {
+ String name = null;
+ if (resolve) {
+ try (InputStream in = jar.getInputStream(entry)) {
+ name = getModuleName(in);
+ }
+ }
+ descriptors = Collections.singletonMap(path, name);
+ isModuleHierarchy = false;
+ return;
+ }
+ // No module descriptor, check manifest file.
+ final Manifest mf = jar.getManifest();
+ if (mf != null) {
+ final Object name = mf.getMainAttributes().get(AUTO_MODULE_NAME);
+ if (name instanceof String) {
+ descriptors = Collections.singletonMap(path, (String) name);
+ isModuleHierarchy = false;
+ return;
+ }
+ }
+ }
+ }
+ descriptors = Collections.emptyMap();
+ isModuleHierarchy = false;
+ }
+
+ /**
+ * Returns the module name declared in the given {@code module-info} descriptor.
+ * The input stream may be for a file or for an entry in a JAR file.
+ */
+ @Nonnull
+ private static String getModuleName(final InputStream in) throws IOException {
+ return ModuleDescriptor.read(in).name();
+ }
+
+ /**
+ * Returns the type of path detected. The return value is {@link JavaPathType#MODULES}
+ * if the dependency is a modular JAR file or a directory containing module descriptor(s),
+ * or {@link JavaPathType#CLASSES} otherwise. A JAR file without module descriptor but with
+ * an "Automatic-Module-Name" manifest attribute is considered modular.
+ */
+ public JavaPathType getPathType() {
+ return descriptors.isEmpty() ? JavaPathType.CLASSES : JavaPathType.MODULES;
+ }
+
+ /**
+ * Returns whether module hierarchy was detected. If false, then package hierarchy is assumed.
+ * In a package hierarchy, the {@linkplain #getModuleNames()} map of modules has either zero or one entry.
+ * In a module hierarchy, the descriptors map may have an arbitrary number of entries,
+ * including one (so the map size cannot be used as a criterion).
+ */
+ public boolean isModuleHierarchy() {
+ return isModuleHierarchy;
+ }
+
+ /**
+ * Returns the module names for the path specified at construction time.
+ * This map is usually either empty if no module was found, or a singleton map.
+ * It may however contain more than one entry if module hierarchy was detected,
+ * in which case there is one key per sub-directory.
+ *
+ * This map may contain null values if the constructor was invoked with {@code resolve}
+ * parameter set to false. This is more efficient when only the module existence needs to
+ * be tested, and module descriptors are not needed.
+ */
+ @Nonnull
+ public Map getModuleNames() {
+ return descriptors;
+ }
+
+ /**
+ * Returns whether the dependency contains a module of the given name.
+ */
+ public boolean containsModule(final String name) {
+ return descriptors.containsValue(name);
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java
new file mode 100644
index 000000000000..d123205bb02e
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/PathModularizationCache.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.PathType;
+
+/**
+ * Cache of {@link PathModularization} instances computed for given {@link Path} elements.
+ * The cache is used for avoiding the need to reopen the same files many times when the
+ * same dependency is used for different scope. For example a path used for compilation
+ * is typically also used for tests.
+ */
+final class PathModularizationCache {
+ /**
+ * Module information for each JAR file or output directories.
+ * Cached when first requested to avoid decoding the module descriptors multiple times.
+ *
+ * @see #getModuleInfo(Path)
+ */
+ private final Map moduleInfo;
+
+ /**
+ * Whether JAR files are modular. This map is redundant with {@link #moduleInfo},
+ * but cheaper to compute when the module names are not needed.
+ *
+ * @see #getPathType(Path)
+ */
+ private final Map pathTypes;
+
+ /**
+ * Creates an initially empty cache.
+ */
+ PathModularizationCache() {
+ moduleInfo = new HashMap<>();
+ pathTypes = new HashMap<>();
+ }
+
+ /**
+ * Gets module information for the given JAR file or output directory.
+ * Module descriptors are read when first requested, then cached.
+ */
+ PathModularization getModuleInfo(final Path path) throws IOException {
+ PathModularization info = moduleInfo.get(path);
+ if (info == null) {
+ info = new PathModularization(path, true);
+ moduleInfo.put(path, info);
+ pathTypes.put(path, info.getPathType());
+ }
+ return info;
+ }
+
+ /**
+ * Returns {@link JavaPathType#MODULES} if the given JAR file or output directory is modular.
+ * This is used in heuristic rules for deciding whether to place a dependency on the class-path
+ * or on the module-path when the {@code "jar"} artifact type is used.
+ */
+ private PathType getPathType(final Path path) throws IOException {
+ PathType type = pathTypes.get(path);
+ if (type == null) {
+ type = new PathModularization(path, false).getPathType();
+ pathTypes.put(path, type);
+ }
+ return type;
+ }
+
+ /**
+ * Selects the type of path where to place the given dependency.
+ * This method returns one of the values specified in the given array.
+ * This method does not handle the patch-module paths, because the patches
+ * depend on which modules have been previously added on the module-paths.
+ *
+ * If the dependency can be a constituent of both the class-path and the module-path,
+ * then the path type is determined by checking if the dependency is modular.
+ *
+ * @param types types of path where a dependency can be placed
+ * @param filter filter the paths accepted by the tool which will consume the path
+ * @param path path to the JAR file or output directory of the dependency
+ * @return where to place the dependency, or an empty value if the placement cannot be determined
+ * @throws IOException if an error occurred while reading module information
+ */
+ Optional selectPathType(final Set types, final Predicate filter, final Path path)
+ throws IOException {
+ PathType selected = null;
+ boolean classes = false;
+ boolean modules = false;
+ boolean unknown = false;
+ for (PathType type : types) {
+ if (filter.test(type)) {
+ if (JavaPathType.CLASSES.equals(type)) {
+ classes = true;
+ } else if (JavaPathType.MODULES.equals(type)) {
+ modules = true;
+ } else {
+ unknown = true;
+ }
+ if (selected == null) {
+ selected = type;
+ } else if (unknown) {
+ // More than one filtered value, and we don't know how to handle at least one of them.
+ // TODO: add a plugin mechanism for allowing plugin to specify their selection algorithm.
+ return Optional.empty();
+ }
+ }
+ }
+ if (classes & modules) {
+ selected = getPathType(path);
+ }
+ return Optional.ofNullable(selected);
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/BomTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/BomTypeProvider.java
index d7387a1780b2..fd03cf821918 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/BomTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/BomTypeProvider.java
@@ -22,8 +22,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(BomTypeProvider.NAME)
@@ -34,7 +34,7 @@ public class BomTypeProvider implements Provider {
private final Type type;
public BomTypeProvider() {
- this.type = new DefaultType(NAME, Type.LANGUAGE_NONE, "pom", null, new DefaultDependencyProperties());
+ this.type = new DefaultType(NAME, Language.NONE, "pom", null, false);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/ClasspathJarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ClasspathJarTypeProvider.java
new file mode 100644
index 000000000000..e8836978249a
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ClasspathJarTypeProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl.types;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
+import org.apache.maven.api.Type;
+import org.apache.maven.internal.impl.DefaultType;
+
+/**
+ * Type provider for a JAR file to unconditionally place on the class-path.
+ * Dependencies of this type are class-path constituents only.
+ *
+ * @see Type#CLASSPATH_JAR
+ */
+@Named(ClasspathJarTypeProvider.NAME)
+@Singleton
+public class ClasspathJarTypeProvider implements Provider {
+ public static final String NAME = Type.CLASSPATH_JAR;
+
+ private final Type type;
+
+ public ClasspathJarTypeProvider() {
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES);
+ }
+
+ @Override
+ public Type get() {
+ return type;
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EarTypeProvider.java
index 489ed638bb53..d8c319b04b6a 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EarTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EarTypeProvider.java
@@ -22,9 +22,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(EarTypeProvider.NAME)
@@ -35,12 +34,7 @@ public class EarTypeProvider implements Provider {
private final Type type;
public EarTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "ear",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "ear", null, true);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbClientTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbClientTypeProvider.java
index efbfe05a1cc0..57e678cea91a 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbClientTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbClientTypeProvider.java
@@ -22,9 +22,9 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(EjbClientTypeProvider.NAME)
@@ -35,12 +35,7 @@ public class EjbClientTypeProvider implements Provider {
private final Type type;
public EjbClientTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- "client",
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", "client", false, JavaPathType.CLASSES);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbTypeProvider.java
index e6f8fbdb4b5e..d58890bd76f3 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/EjbTypeProvider.java
@@ -22,9 +22,9 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(EjbTypeProvider.NAME)
@@ -35,12 +35,7 @@ public class EjbTypeProvider implements Provider {
private final Type type;
public EjbTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JarTypeProvider.java
index 974c34e0ed97..a0737f403fe6 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JarTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JarTypeProvider.java
@@ -22,25 +22,29 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for a JAR file that can be placed either on the class-path or on the module-path.
+ * Dependencies of this type are class-path constituents and module-path constituents.
+ * Only one of those constituents shall be effective for any given dependency.
+ * The choice may depend on heuristic rules.
+ *
+ * @see Type#JAR
+ */
@Named(JarTypeProvider.NAME)
@Singleton
public class JarTypeProvider implements Provider {
- public static final String NAME = "jar";
+ public static final String NAME = Type.JAR;
private final Type type;
public JarTypeProvider() {
this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ NAME, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES, JavaPathType.MODULES);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavaSourceTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavaSourceTypeProvider.java
index bef8acf716fd..10dfee272183 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavaSourceTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavaSourceTypeProvider.java
@@ -22,19 +22,24 @@
import javax.inject.Provider;
import javax.inject.Singleton;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for source code packaged in a JAR file.
+ *
+ * @see Type#JAVA_SOURCE
+ */
@Named(JavaSourceTypeProvider.NAME)
@Singleton
public class JavaSourceTypeProvider implements Provider {
- public static final String NAME = "java-source";
+ public static final String NAME = Type.JAVA_SOURCE;
private final Type type;
public JavaSourceTypeProvider() {
- this.type = new DefaultType(NAME, Type.LANGUAGE_JAVA, "jar", "sources", new DefaultDependencyProperties());
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", "sources", false);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavadocTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavadocTypeProvider.java
index 1ccac4f04b49..99f4fb179400 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavadocTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/JavadocTypeProvider.java
@@ -22,25 +22,25 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for javadoc packaged in a JAR file.
+ *
+ * @see Type#JAVADOC
+ */
@Named(JavadocTypeProvider.NAME)
@Singleton
public class JavadocTypeProvider implements Provider {
- public static final String NAME = "javadoc";
+ public static final String NAME = Type.JAVADOC;
private final Type type;
public JavadocTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- "javadoc",
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", "javadoc", false, JavaPathType.CLASSES);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/MavenPluginTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/MavenPluginTypeProvider.java
index bfdb424c2786..bef3837cc3ef 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/MavenPluginTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/MavenPluginTypeProvider.java
@@ -22,25 +22,25 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for a Maven plugin.
+ *
+ * @see Type#MAVEN_PLUGIN
+ */
@Named(MavenPluginTypeProvider.NAME)
@Singleton
public class MavenPluginTypeProvider implements Provider {
- public static final String NAME = "maven-plugin";
+ public static final String NAME = Type.MAVEN_PLUGIN;
private final Type type;
public MavenPluginTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.CLASSES);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/ModularJarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ModularJarTypeProvider.java
new file mode 100644
index 000000000000..9dfa5a0df54a
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ModularJarTypeProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.internal.impl.types;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
+import org.apache.maven.api.Type;
+import org.apache.maven.internal.impl.DefaultType;
+
+/**
+ * Type provider for a JAR file to unconditionally place on the module-path.
+ * Dependencies of this type are module-path constituents only.
+ *
+ * @see Type#MODULAR_JAR
+ */
+@Named(ModularJarTypeProvider.NAME)
+@Singleton
+public class ModularJarTypeProvider implements Provider {
+ public static final String NAME = Type.MODULAR_JAR;
+
+ private final Type type;
+
+ public ModularJarTypeProvider() {
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "jar", null, false, JavaPathType.MODULES);
+ }
+
+ @Override
+ public Type get() {
+ return type;
+ }
+}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/ParTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ParTypeProvider.java
index 91db9ba182da..1f51178991a5 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/ParTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/ParTypeProvider.java
@@ -22,9 +22,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(ParTypeProvider.NAME)
@@ -35,12 +34,7 @@ public class ParTypeProvider implements Provider {
private final Type type;
public ParTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "par",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "par", null, true);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/PomTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/PomTypeProvider.java
index fe0f9705d0ea..7b52f299a2f9 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/PomTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/PomTypeProvider.java
@@ -22,19 +22,24 @@
import javax.inject.Provider;
import javax.inject.Singleton;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for a POM file.
+ *
+ * @see Type#POM
+ */
@Named(PomTypeProvider.NAME)
@Singleton
public class PomTypeProvider implements Provider {
- public static final String NAME = "pom";
+ public static final String NAME = Type.POM;
private final Type type;
public PomTypeProvider() {
- this.type = new DefaultType(NAME, Type.LANGUAGE_NONE, "pom", null, new DefaultDependencyProperties());
+ this.type = new DefaultType(NAME, Language.NONE, "pom", null, false);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/RarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/RarTypeProvider.java
index ad5accd3cd0c..3dd81a68bb4a 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/RarTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/RarTypeProvider.java
@@ -22,9 +22,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(RarTypeProvider.NAME)
@@ -35,12 +34,7 @@ public class RarTypeProvider implements Provider {
private final Type type;
public RarTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "rar",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "rar", null, true);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/TestJarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/TestJarTypeProvider.java
index 6bd7ea2da727..00bb4c20fdfd 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/TestJarTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/TestJarTypeProvider.java
@@ -22,25 +22,28 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.JavaPathType;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
+/**
+ * Type provider for a JAR file containing test classes. Dependencies of this type are class-path constituents
+ * or {@code --patch-module} option values. For any dependency, the choice depends on whether the main JAR file
+ * was placed on the class-path or module-path respectively.
+ *
+ * @see Type#TEST_JAR
+ */
@Named(TestJarTypeProvider.NAME)
@Singleton
public class TestJarTypeProvider implements Provider {
- public static final String NAME = "test-jar";
+ public static final String NAME = Type.TEST_JAR;
private final Type type;
public TestJarTypeProvider() {
this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "jar",
- "tests",
- new DefaultDependencyProperties(DependencyProperties.FLAG_CLASS_PATH_CONSTITUENT));
+ NAME, Language.JAVA_FAMILY, "jar", "tests", false, JavaPathType.CLASSES, JavaPathType.PATCH_MODULE);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/types/WarTypeProvider.java b/maven-core/src/main/java/org/apache/maven/internal/impl/types/WarTypeProvider.java
index a2b339001d46..cdde4a512833 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/types/WarTypeProvider.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/types/WarTypeProvider.java
@@ -22,9 +22,8 @@
import javax.inject.Provider;
import javax.inject.Singleton;
-import org.apache.maven.api.DependencyProperties;
+import org.apache.maven.api.Language;
import org.apache.maven.api.Type;
-import org.apache.maven.internal.impl.DefaultDependencyProperties;
import org.apache.maven.internal.impl.DefaultType;
@Named(WarTypeProvider.NAME)
@@ -35,12 +34,7 @@ public class WarTypeProvider implements Provider {
private final Type type;
public WarTypeProvider() {
- this.type = new DefaultType(
- NAME,
- Type.LANGUAGE_JAVA,
- "war",
- null,
- new DefaultDependencyProperties(DependencyProperties.FLAG_INCLUDES_DEPENDENCIES));
+ this.type = new DefaultType(NAME, Language.JAVA_FAMILY, "war", null, true);
}
@Override
diff --git a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
index 80ba4e7cdf2e..64087c262d47 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/TransformedArtifactHandler.java
@@ -36,30 +36,38 @@ class TransformedArtifactHandler implements ArtifactHandler {
this.packaging = requireNonNull(packaging);
}
+ @Override
public String getClassifier() {
return classifier;
}
+ @Override
public String getDirectory() {
return null;
}
+ @Override
public String getExtension() {
return extension;
}
+ @Override
public String getLanguage() {
return "none";
}
+ @Override
public String getPackaging() {
return packaging;
}
+ @Override
+ @Deprecated
public boolean isAddedToClasspath() {
return false;
}
+ @Override
public boolean isIncludesDependencies() {
return false;
}
diff --git a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
index 0e165e04cb30..9b58e0212646 100644
--- a/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
+++ b/maven-core/src/main/java/org/apache/maven/project/MavenProject.java
@@ -32,12 +32,14 @@
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.function.Predicate;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.lifecycle.internal.DefaultProjectArtifactFactory;
@@ -320,68 +322,97 @@ public List getTestCompileSourceRoots() {
return testCompileSourceRoots;
}
- public List getCompileClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 1);
-
- String d = getBuild().getOutputDirectory();
- if (d != null) {
- list.add(d);
- }
-
- for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()) {
- // TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope())
- || Artifact.SCOPE_PROVIDED.equals(a.getScope())
- || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
- addArtifactPath(a, list);
- }
- }
- }
+ // TODO let the scope handler deal with this
+ private static boolean isCompilePathElement(final String scope) {
+ return Artifact.SCOPE_COMPILE.equals(scope)
+ || Artifact.SCOPE_PROVIDED.equals(scope)
+ || Artifact.SCOPE_SYSTEM.equals(scope);
+ }
- return list;
+ // TODO let the scope handler deal with this
+ private static boolean isRuntimePathElement(final String scope) {
+ return Artifact.SCOPE_COMPILE.equals(scope) || Artifact.SCOPE_RUNTIME.equals(scope);
}
- // TODO this checking for file == null happens because the resolver has been confused about the root
- // artifact or not. things like the stupid dummy artifact coming from surefire.
- public List getTestClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 2);
+ // TODO let the scope handler deal with this
+ private static boolean isTestPathElement(final String scope) {
+ return true;
+ }
- String d = getBuild().getTestOutputDirectory();
- if (d != null) {
- list.add(d);
+ /**
+ * Returns a filtered list of classpath elements. This method is invoked when the caller
+ * requested that all dependencies are placed on the classpath, with no module-path element.
+ *
+ * @param scopeFilter a filter returning {@code true} for the artifact scopes to accept.
+ * @param includeTestDir whether to include the test directory in the classpath elements.
+ * @return paths of all artifacts placed on the classpath.
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ */
+ private List getClasspathElements(final Predicate scopeFilter, final boolean includeTestDir)
+ throws DependencyResolutionRequiredException {
+ final List list = new ArrayList<>(getArtifacts().size() + 2);
+ if (includeTestDir) {
+ String d = getBuild().getTestOutputDirectory();
+ if (d != null) {
+ list.add(d);
+ }
}
-
- d = getBuild().getOutputDirectory();
+ String d = getBuild().getOutputDirectory();
if (d != null) {
list.add(d);
}
-
for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()) {
- addArtifactPath(a, list);
+ final File f = a.getFile();
+ if (f != null && scopeFilter.test(a.getScope())) {
+ final ArtifactHandler h = a.getArtifactHandler();
+ if (h.isAddedToClasspath()) {
+ list.add(f.getPath());
+ }
}
}
-
return list;
}
- public List getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
- List list = new ArrayList<>(getArtifacts().size() + 1);
+ /**
+ * Returns the elements placed on the classpath for compilation.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getCompileClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isCompilePathElement, false);
+ }
- String d = getBuild().getOutputDirectory();
- if (d != null) {
- list.add(d);
- }
+ /**
+ * Returns the elements placed on the classpath for tests.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getTestClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isTestPathElement, true);
+ }
- for (Artifact a : getArtifacts()) {
- if (a.getArtifactHandler().isAddedToClasspath()
- // TODO let the scope handler deal with this
- && (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope()))) {
- addArtifactPath(a, list);
- }
- }
- return list;
+ /**
+ * Returns the elements placed on the classpath for runtime.
+ * This method can be invoked when the caller does not support module-path.
+ *
+ * @throws DependencyResolutionRequiredException if an artifact file is used, but has not been resolved
+ *
+ * @deprecated This method is unreliable because it does not consider other dependency properties.
+ * See {@link org.apache.maven.api.JavaPathType} instead for better analysis.
+ */
+ @Deprecated
+ public List getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
+ return getClasspathElements(MavenProject::isRuntimePathElement, false);
}
// ----------------------------------------------------------------------
@@ -1111,13 +1142,6 @@ private void deepCopy(MavenProject project) {
lifecyclePhases.addAll(project.lifecyclePhases);
}
- private void addArtifactPath(Artifact artifact, List classpath) {
- File file = artifact.getFile();
- if (file != null) {
- classpath.add(file.getPath());
- }
- }
-
private static String getProjectReferenceId(String groupId, String artifactId, String version) {
StringBuilder buffer = new StringBuilder(128);
buffer.append(groupId).append(':').append(artifactId).append(':').append(version);
@@ -1347,9 +1371,7 @@ public List getCompileArtifacts() {
// TODO classpath check doesn't belong here - that's the other method
if (a.getArtifactHandler().isAddedToClasspath()) {
// TODO let the scope handler deal with this
- if (Artifact.SCOPE_COMPILE.equals(a.getScope())
- || Artifact.SCOPE_PROVIDED.equals(a.getScope())
- || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
+ if (isCompilePathElement(a.getScope())) {
list.add(a);
}
}
@@ -1369,9 +1391,7 @@ public List