[ https://issues.apache.org/jira/browse/MNG-8015?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17808677#comment-17808677 ]
ASF GitHub Bot commented on MNG-8015: ------------------------------------- gnodet commented on code in PR #1378: URL: https://github.com/apache/maven/pull/1378#discussion_r1459081636 ########## api/maven-api-core/src/main/java/org/apache/maven/api/DependencyProperties.java: ########## @@ -33,27 +36,226 @@ @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. + * Keys in the dependency properties map. + * Each key can be associated to values of a specific class. + * + * @param <V> type of value associated to the key + */ + class Key<V> { + /** + * The keys that are defined in this {@code DependencyProperties} map. + * Accesses to this map shall be synchronized on the map. + * + * @see #intern() + */ + private static final Map<String, Key<?>> INTERNS = new HashMap<>(); + + /** + * Value returned by {@link #name()}. + */ + @Nonnull + private final String name; + + /** + * Value returned by {@link #valueType()}. + */ + @Nonnull + private final Class<V> valueType; + + /** + * Creates a new key. + * + * @param name name of the key + * @param valueType type of value associated to the key + */ + public Key(@Nonnull final String name, @Nonnull final Class<V> valueType) { + this.name = Objects.requireNonNull(name); + this.valueType = Objects.requireNonNull(valueType); + } + + /** + * If a key exists in the {@linkplain #intern() intern pool} for the given name, returns that key. + * Otherwise, if the {@code defaultType} is non-null, creates a key for values of the specified type. + * Otherwise, returns {@code null}. + * + * @param name name of the key to search or create + * @param defaultType value type of the key to create if none exist for the given name, or {@code null} + * @return key found or created, or {@code null} if no key was found and {@code defaultType} is null + * + * @see #intern() + */ + public static Key<?> forName(String name, Class<?> defaultType) { + Key<?> key; + synchronized (INTERNS) { + key = INTERNS.get(name); + } + if (key == null && defaultType != null) { + key = new Key<>(name, defaultType); + } + return key; + } + + /** + * {@return the name of the key}. + */ + @Nonnull + public String name() { + return name; + } + + /** + * {@return the type of value associated to the key}. + */ + @Nonnull + public Class<V> valueType() { + return valueType; + } + + /** + * {@return a canonical representation of this key}. A pool of keys, initially empty, is maintained privately. + * When the {@code intern()} method is invoked, if the pool already contains a key equal to this {@code Key} + * as determined by the {@link #equals(Object)} method, then the key from the pool is returned. Otherwise, + * if no key exist in the pool for this key {@linkplain #name() name}, then this {@code Key} object is added + * to the pool and {@code this} is returned. Otherwise an {@link IllegalStateException} is thrown. + * + * @throws IllegalStateException if a key exists in the pool for the same name but a different class of values. + * + * @see String#intern() + */ + @SuppressWarnings("unchecked") + public Key<V> intern() { + Key<?> previous; + synchronized (INTERNS) { + previous = INTERNS.putIfAbsent(name, this); + } + if (previous == null) { + return this; + } + if (equals(previous)) { + return (Key<V>) previous; + } + throw new IllegalStateException("Key " + name + " already exists for a different class of values."); + } + + /** + * {@return a string representation of this key}. + * By default, this is the name of this key. + */ + @Override + public String toString() { + return name; + } + + /** + * {@return an hash code value for this key}. + */ + @Override + public int hashCode() { + return 7 + name.hashCode() + 31 * valueType.hashCode(); + } + + /** + * Compares this key with the given object for equality. + * Two keys are considered equal if they have the same name + * and are associated to values of the same class. + * + * @param obj the object to compare with this key + * @return whether the given object is equal to this key + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && getClass() == obj.getClass()) { + final Key<?> other = (Key<?>) obj; + return name.equals(other.name) && valueType.equals(other.valueType); + } + return false; + } + } + + /** + * The dependency type. The {@linkplain Key#name() name} of this property + * is equal to the {@code ArtifactProperties.TYPE} value. + */ + Key<String> TYPE = new Key<>("type", String.class).intern(); + + /** + * The dependency language. The {@linkplain Key#name() name} of this property + * is equal to the {@code ArtifactProperties.LANGUAGE} value. + */ + Key<String> LANGUAGE = new Key<>("language", String.class).intern(); + + /** + * 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. + * + * <p>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}.</p> + */ + Key<PathType[]> PATH_TYPES = new Key<>("pathTypes", PathType[].class).intern(); Review Comment: What about using `java.lang.reflect.Type` instead of `Class<?>` here and in the value converters ? > Control the type of path where each dependency can be placed > ------------------------------------------------------------ > > Key: MNG-8015 > URL: https://issues.apache.org/jira/browse/MNG-8015 > Project: Maven > Issue Type: Improvement > Components: Core > Affects Versions: 4.0.0-alpha-12 > Reporter: Martin Desruisseaux > Priority: Major > > Make possible to declare where each dependency can be placed: on the > module-path, class-path, agent path, doclet path, taglet path, annotation > processing path, _etc._ The proposed improvement consists in adding a new > {{PATH_TYPES}} property that can be associated to dependencies. The property > value is an array of {{PathType}}, a new enumeration-like class with values > such as {{CLASSES}}, {{MODULES}}, {{DOCLET}}, _etc._ Contrarily to real Java > enumerations, this enumeration-like class is extensible: plugins can add > their own enumeration values. This is required at least for the > {{--patch-module}} option, where a new {{PathType}} enumeration value need to > be created for each module to patch. > Users can control indirectly the {{PathType}} of a dependency by specifying > the dependency type. Note that there is no direct mapping between the > dependency type and where the dependency will be placed, but only an indirect > mapping caused by the fact that using a dependency type implies implicit > values of some properties such as classifier, and (with this proposal) path > types: > * {{<type>jar</type>}} implies {{PathType.CLASSES}} and {{PathType.MODULES}}. > * {{<type>modular-jar</type>}} implies {{PathType.MODULES}} only. > * {{<type>classpath-jar</type>}} implies {{PathType.CLASSES}} only. > * _etc._ > When a plugin requests the paths of dependencies, the plugin specifies the > types of path it is interested in. For example, a Java compiler plugin can > specify that it is interested in {{PathType.CLASSES}} and > {{PathType.MODULES}}, but not {{PathType.DOCLET}}. If a dependency declared > that it can be placed on the class-path or the doclet-path, only the > class-path is left after intersection with plugin's request. This is > important for the next step. > If, after all filtering such as above paragraph are applied, a dependency has > only one {{PathType}} left, then there is no ambiguity and we are done. > Combined with above-cited dependency types like {{modular-jar}} or > {{classpath-jar}}, this rule allows users to control where the dependency > will be placed. But if there are two or more {{PathType}} left after > filtering, then a choice needs to be done. For example if there are both > {{PathType.CLASSES}} and {{PathType.MODULES}} (which may happen when > {{<type>jar</type>}} is used), then an heuristic rule similar to Maven 3 can > be applied: check if a {{module-info.class}} file or an {{Automatic-Name}} > manifest attribute is present, and base the decision on that. > This proposal aims to fix MNG-7855. -- This message was sent by Atlassian Jira (v8.20.10#820010)