mirror of https://github.com/apache/maven.git
[MNG-8286] Add a condition profile based on a simple expressions (#1771)
This commit is contained in:
parent
fcd9c0f018
commit
986f59683e
|
@ -2742,10 +2742,79 @@
|
|||
<class>
|
||||
<name>Activation</name>
|
||||
<version>4.0.0+</version>
|
||||
<description>The conditions within the build runtime environment which will trigger the
|
||||
<description><![CDATA[
|
||||
The conditions within the build runtime environment which will trigger the
|
||||
automatic inclusion of the build profile. Multiple conditions can be defined, which must
|
||||
be all satisfied to activate the profile.
|
||||
</description>
|
||||
|
||||
<p>In addition to the traditional activation mechanisms (JDK version, OS properties,
|
||||
file existence, etc.), Maven now supports a powerful condition-based activation
|
||||
through the {@code condition} field. This new mechanism allows for more flexible
|
||||
and expressive profile activation rules.</p>
|
||||
|
||||
<h2>Condition Syntax</h2>
|
||||
|
||||
<p>The condition is specified as a string expression that can include various
|
||||
functions, comparisons, and logical operators. Some key features include:</p>
|
||||
|
||||
<ul>
|
||||
<li>Property access: {@code ${property.name}}</li>
|
||||
<li>Comparison operators: {@code ==}, {@code !=}, {@code <}, {@code >}, {@code <=}, {@code >=}</li>
|
||||
<li>Logical operators: {@code &&} (AND), {@code ||} (OR), {@code not(...)}</li>
|
||||
<li>Functions: {@code exists(...)}, {@code missing(...)}, {@code matches(...)}, {@code inrange(...)}, and more</li>
|
||||
</ul>
|
||||
|
||||
<h2>Supported Functions</h2>
|
||||
|
||||
<p>The following functions are supported in condition expressions:</p>
|
||||
|
||||
<ul>
|
||||
<li>{@code length(string)}: Returns the length of the given string.</li>
|
||||
<li>{@code upper(string)}: Converts the string to uppercase.</li>
|
||||
<li>{@code lower(string)}: Converts the string to lowercase.</li>
|
||||
<li>{@code substring(string, start, [end])}: Returns a substring of the given string.</li>
|
||||
<li>{@code indexOf(string, substring)}: Returns the index of the first occurrence of substring in string, or -1 if not found.</li>
|
||||
<li>{@code contains(string, substring)}: Checks if the string contains the substring.</li>
|
||||
<li>{@code matches(string, regex)}: Checks if the string matches the given regular expression.</li>
|
||||
<li>{@code not(condition)}: Negates the given condition.</li>
|
||||
<li>{@code if(condition, trueValue, falseValue)}: Returns trueValue if the condition is true, falseValue otherwise.</li>
|
||||
<li>{@code exists(path)}: Checks if a file matching the given glob pattern exists.</li>
|
||||
<li>{@code missing(path)}: Checks if a file matching the given glob pattern does not exist.</li>
|
||||
<li>{@code inrange(version, range)}: Checks if the given version is within the specified version range.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Supported properties</h2>
|
||||
|
||||
<p>The following properties are supported in expressions:</p>
|
||||
|
||||
<ul>
|
||||
<li>`project.basedir`: The project directory</li>
|
||||
<li>`project.rootDirectory`: The root directory of the project</li>
|
||||
<li>`project.artifactId`: The artifactId of the project</li>
|
||||
<li>`project.packaging`: The packaging of the project</li>
|
||||
<li>user properties</li>
|
||||
<li>system properties (including environment variables prefixed with `env.`)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Examples</h2>
|
||||
|
||||
<ul>
|
||||
<li>JDK version range: {@code inrange(${java.version}, '[11,)')} (JDK 11 or higher)</li>
|
||||
<li>OS check: {@code ${os.name} == 'windows'}</li>
|
||||
<li>File existence: {@code exists('${project.basedir}/src/**}{@code /*.xsd')}</li>
|
||||
<li>Property check: {@code ${my.property} != 'some-value'}</li>
|
||||
<li>Regex matching: {@code matches(${os.version}, '.*aws')}</li>
|
||||
<li>Complex condition: {@code ${os.name} == 'windows' && ${os.arch} != 'amd64' && inrange(${os.version}, '[10,)')}</li>
|
||||
<li>String length check: {@code length(${user.name}) > 5}</li>
|
||||
<li>Substring with version: {@code substring(${java.version}, 0, 3) == '1.8'}</li>
|
||||
<li>Using indexOf: {@code indexOf(${java.version}, '-') > 0}</li>
|
||||
<li>Conditional logic: {@code if(contains(${java.version}, '-'), substring(${java.version}, 0, indexOf(${java.version}, '-')), ${java.version})}</li>
|
||||
</ul>
|
||||
|
||||
<p>This flexible condition mechanism allows for more precise control over profile
|
||||
activation, enabling developers to create profiles that respond to a wide range of
|
||||
environmental factors and project states.</p>
|
||||
]]></description>
|
||||
<fields>
|
||||
<field>
|
||||
<name>activeByDefault</name>
|
||||
|
@ -2798,6 +2867,12 @@
|
|||
<type>String</type>
|
||||
<description>Specifies that this profile will be activated based on the project's packaging.</description>
|
||||
</field>
|
||||
<field>
|
||||
<name>condition</name>
|
||||
<version>4.1.0+</version>
|
||||
<type>String</type>
|
||||
<description>The condition which must be satisfied to activate the profile.</description>
|
||||
</field>
|
||||
<!--
|
||||
This could be included once we teach Maven to deal with multiple versions of the model
|
||||
<field>
|
||||
|
|
|
@ -744,6 +744,14 @@
|
|||
Specifies that this profile will be activated based on the project's packaging.
|
||||
</description>
|
||||
</field>
|
||||
<field>
|
||||
<name>condition</name>
|
||||
<version>2.0.0+</version>
|
||||
<type>String</type>
|
||||
<description>
|
||||
The condition which must be satisfied to activate the profile.
|
||||
</description>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,27 +18,27 @@
|
|||
*/
|
||||
package org.apache.maven.api.services.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.maven.api.annotations.Nonnull;
|
||||
import org.apache.maven.api.model.Model;
|
||||
|
||||
/**
|
||||
* Describes the environmental context used to determine the activation status of profiles.
|
||||
*
|
||||
* The {@link Model} is available from the activation context, but only static parts of it
|
||||
* are allowed to be used, i.e. those that do not change between file model and effective model.
|
||||
*
|
||||
*/
|
||||
public interface ProfileActivationContext {
|
||||
/**
|
||||
* Key of the property containing the project's packaging.
|
||||
* Available in {@link #getUserProperties()}.
|
||||
* @since 4.0.0
|
||||
*/
|
||||
String PROPERTY_NAME_PACKAGING = "packaging";
|
||||
|
||||
/**
|
||||
* Gets the identifiers of those profiles that should be activated by explicit demand.
|
||||
*
|
||||
* @return The identifiers of those profiles to activate, never {@code null}.
|
||||
*/
|
||||
@Nonnull
|
||||
List<String> getActiveProfileIds();
|
||||
|
||||
/**
|
||||
|
@ -46,6 +46,7 @@ public interface ProfileActivationContext {
|
|||
*
|
||||
* @return The identifiers of those profiles to deactivate, never {@code null}.
|
||||
*/
|
||||
@Nonnull
|
||||
List<String> getInactiveProfileIds();
|
||||
|
||||
/**
|
||||
|
@ -54,6 +55,7 @@ public interface ProfileActivationContext {
|
|||
*
|
||||
* @return The execution properties, never {@code null}.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, String> getSystemProperties();
|
||||
|
||||
/**
|
||||
|
@ -63,19 +65,14 @@ public interface ProfileActivationContext {
|
|||
*
|
||||
* @return The user properties, never {@code null}.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, String> getUserProperties();
|
||||
|
||||
/**
|
||||
* Gets the base directory of the current project (if any).
|
||||
* Gets the model which is being activated.
|
||||
*
|
||||
* @return The base directory of the current project or {@code null} if none.
|
||||
* @return The project model, never {@code null}.
|
||||
*/
|
||||
Path getProjectDirectory();
|
||||
|
||||
/**
|
||||
* Gets current calculated project properties
|
||||
*
|
||||
* @return The project properties, never {@code null}.
|
||||
*/
|
||||
Map<String, String> getProjectProperties();
|
||||
@Nonnull
|
||||
Model getModel();
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.maven.api.di.Inject;
|
||||
import org.apache.maven.api.di.Named;
|
||||
|
@ -43,6 +44,7 @@ import org.apache.maven.api.services.Source;
|
|||
import org.apache.maven.api.services.xml.SettingsXmlFactory;
|
||||
import org.apache.maven.api.services.xml.XmlReaderException;
|
||||
import org.apache.maven.api.services.xml.XmlReaderRequest;
|
||||
import org.apache.maven.api.settings.Activation;
|
||||
import org.apache.maven.api.settings.Profile;
|
||||
import org.apache.maven.api.settings.Repository;
|
||||
import org.apache.maven.api.settings.RepositoryPolicy;
|
||||
|
@ -245,10 +247,23 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
|
|||
Map<String, String> systemProperties = request.getSession().getSystemProperties();
|
||||
src = Interpolator.chain(userProperties::get, systemProperties::get);
|
||||
}
|
||||
return new SettingsTransformer(value -> value != null ? interpolator.interpolate(value, src) : null)
|
||||
return new DefSettingsTransformer(value -> value != null ? interpolator.interpolate(value, src) : null)
|
||||
.visit(settings);
|
||||
}
|
||||
|
||||
static class DefSettingsTransformer extends SettingsTransformer {
|
||||
DefSettingsTransformer(Function<String, String> transformer) {
|
||||
super(transformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Activation.Builder transformActivation_Condition(
|
||||
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||
// do not interpolate the condition activation
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
private Settings decrypt(
|
||||
Source settingsSource, Settings settings, SettingsBuilderRequest request, List<BuilderProblem> problems) {
|
||||
if (secDispatcher == null) {
|
||||
|
|
|
@ -127,6 +127,8 @@ public final class SettingsUtilsV4 {
|
|||
|
||||
activation.packaging(modelActivation.getPackaging());
|
||||
|
||||
activation.condition(modelActivation.getCondition());
|
||||
|
||||
profile.activation(activation.build());
|
||||
}
|
||||
|
||||
|
@ -212,6 +214,8 @@ public final class SettingsUtilsV4 {
|
|||
|
||||
activation.packaging(settingsActivation.getPackaging());
|
||||
|
||||
activation.condition(settingsActivation.getCondition());
|
||||
|
||||
profile.activation(activation.build());
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,6 @@ import org.apache.maven.api.services.model.ModelValidator;
|
|||
import org.apache.maven.api.services.model.ModelVersionParser;
|
||||
import org.apache.maven.api.services.model.PluginConfigurationExpander;
|
||||
import org.apache.maven.api.services.model.PluginManagementInjector;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.api.services.model.ProfileInjector;
|
||||
import org.apache.maven.api.services.model.ProfileSelector;
|
||||
import org.apache.maven.api.services.model.RootLocator;
|
||||
|
@ -1125,7 +1124,7 @@ public class DefaultModelBuilder implements ModelBuilder {
|
|||
profileActivationContext.setUserProperties(profileProps);
|
||||
}
|
||||
|
||||
profileActivationContext.setProjectProperties(inputModel.getProperties());
|
||||
profileActivationContext.setModel(inputModel);
|
||||
setSource(inputModel);
|
||||
List<Profile> activePomProfiles = getActiveProfiles(inputModel.getProfiles(), profileActivationContext);
|
||||
|
||||
|
@ -1203,7 +1202,7 @@ public class DefaultModelBuilder implements ModelBuilder {
|
|||
model = modelNormalizer.mergeDuplicates(model, request, this);
|
||||
|
||||
// profile activation
|
||||
profileActivationContext.setProjectProperties(model.getProperties());
|
||||
profileActivationContext.setModel(model);
|
||||
|
||||
List<Profile> interpolatedProfiles =
|
||||
interpolateActivations(model.getProfiles(), profileActivationContext, this);
|
||||
|
@ -1782,6 +1781,13 @@ public class DefaultModelBuilder implements ModelBuilder {
|
|||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Activation.Builder transformActivation_Condition(
|
||||
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||
// do not interpolate the condition activation
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivationFile.Builder transformActivationFile_Missing(
|
||||
Supplier<? extends ActivationFile.Builder> creator,
|
||||
|
@ -1860,13 +1866,8 @@ public class DefaultModelBuilder implements ModelBuilder {
|
|||
context.setActiveProfileIds(request.getActiveProfileIds());
|
||||
context.setInactiveProfileIds(request.getInactiveProfileIds());
|
||||
context.setSystemProperties(request.getSystemProperties());
|
||||
// enrich user properties with project packaging
|
||||
Map<String, String> userProperties = new HashMap<>(request.getUserProperties());
|
||||
if (!userProperties.containsKey(ProfileActivationContext.PROPERTY_NAME_PACKAGING)) {
|
||||
userProperties.put(ProfileActivationContext.PROPERTY_NAME_PACKAGING, model.getPackaging());
|
||||
}
|
||||
context.setUserProperties(userProperties);
|
||||
context.setProjectDirectory(model.getProjectDirectory());
|
||||
context.setUserProperties(request.getUserProperties());
|
||||
context.setModel(model);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
|
@ -282,6 +282,17 @@ public class DefaultModelValidator implements ModelValidator {
|
|||
stk.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Activation.Builder transformActivation_Condition(
|
||||
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
|
||||
stk.push(nextFrame("condition"));
|
||||
try {
|
||||
return super.transformActivation_Condition(creator, builder, target);
|
||||
} finally {
|
||||
stk.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Set<String> validCoordinatesIds = ConcurrentHashMap.newKeySet();
|
||||
|
@ -470,6 +481,8 @@ public class DefaultModelValidator implements ModelValidator {
|
|||
}
|
||||
}
|
||||
|
||||
validateStringNoExpression("packaging", problems, Severity.WARNING, Version.V20, m.getPackaging(), m);
|
||||
|
||||
validate20RawDependencies(
|
||||
problems,
|
||||
m.getDependencies(),
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
*/
|
||||
package org.apache.maven.internal.impl.model;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
|
||||
/**
|
||||
|
@ -41,9 +41,7 @@ public class DefaultProfileActivationContext implements ProfileActivationContext
|
|||
|
||||
private Map<String, String> userProperties = Collections.emptyMap();
|
||||
|
||||
private Map<String, String> projectProperties = Collections.emptyMap();
|
||||
|
||||
private Path projectDirectory;
|
||||
private Model model;
|
||||
|
||||
@Override
|
||||
public List<String> getActiveProfileIds() {
|
||||
|
@ -138,34 +136,12 @@ public class DefaultProfileActivationContext implements ProfileActivationContext
|
|||
}
|
||||
|
||||
@Override
|
||||
public Path getProjectDirectory() {
|
||||
return projectDirectory;
|
||||
public Model getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base directory of the current project.
|
||||
*
|
||||
* @param projectDirectory The base directory of the current project, may be {@code null} if profile activation
|
||||
* happens in the context of metadata retrieval rather than project building.
|
||||
* @return This context, never {@code null}.
|
||||
*/
|
||||
public DefaultProfileActivationContext setProjectDirectory(Path projectDirectory) {
|
||||
this.projectDirectory = projectDirectory;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProjectProperties() {
|
||||
return projectProperties;
|
||||
}
|
||||
|
||||
public DefaultProfileActivationContext setProjectProperties(Properties projectProperties) {
|
||||
return setProjectProperties(toMap(projectProperties));
|
||||
}
|
||||
|
||||
public DefaultProfileActivationContext setProjectProperties(Map<String, String> projectProperties) {
|
||||
this.projectProperties = unmodifiable(projectProperties);
|
||||
public DefaultProfileActivationContext setModel(Model model) {
|
||||
this.model = model;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class ProfileActivationFilePathInterpolator {
|
|||
return null;
|
||||
}
|
||||
|
||||
Path basedir = context.getProjectDirectory();
|
||||
Path basedir = context.getModel().getProjectDirectory();
|
||||
|
||||
String absolutePath = interpolator.interpolate(path, s -> {
|
||||
if ("basedir".equals(s) || "project.basedir".equals(s)) {
|
||||
|
@ -71,7 +71,7 @@ public class ProfileActivationFilePathInterpolator {
|
|||
Path root = rootLocator.findMandatoryRoot(basedir);
|
||||
return root.toFile().getAbsolutePath();
|
||||
}
|
||||
String r = context.getProjectProperties().get(s);
|
||||
String r = context.getModel().getProperties().get(s);
|
||||
if (r == null) {
|
||||
r = context.getUserProperties().get(s);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.maven.api.services.VersionParser;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||
|
||||
import static org.apache.maven.internal.impl.model.profile.ConditionParser.toInt;
|
||||
|
||||
/**
|
||||
* Provides a set of functions for evaluating profile activation conditions.
|
||||
* These functions can be used in profile activation expressions to determine
|
||||
* whether a profile should be activated based on various criteria.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ConditionFunctions {
|
||||
private final ProfileActivationContext context;
|
||||
private final VersionParser versionParser;
|
||||
private final ProfileActivationFilePathInterpolator interpolator;
|
||||
|
||||
/**
|
||||
* Constructs a new ConditionFunctions instance.
|
||||
*
|
||||
* @param context The profile activation context
|
||||
* @param versionParser The version parser for comparing versions
|
||||
* @param interpolator The interpolator for resolving file paths
|
||||
*/
|
||||
public ConditionFunctions(
|
||||
ProfileActivationContext context,
|
||||
VersionParser versionParser,
|
||||
ProfileActivationFilePathInterpolator interpolator) {
|
||||
this.context = context;
|
||||
this.versionParser = versionParser;
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given string.
|
||||
*
|
||||
* @param args A list containing a single string argument
|
||||
* @return The length of the string
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
*/
|
||||
public Object length(List<Object> args) {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("length function requires exactly one argument");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
return s.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given string to uppercase.
|
||||
*
|
||||
* @param args A list containing a single string argument
|
||||
* @return The uppercase version of the input string
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
*/
|
||||
public Object upper(List<Object> args) {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("upper function requires exactly one argument");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
return s.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given string to lowercase.
|
||||
*
|
||||
* @param args A list containing a single string argument
|
||||
* @return The lowercase version of the input string
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
*/
|
||||
public Object lower(List<Object> args) {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("lower function requires exactly one argument");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
return s.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a substring of the given string.
|
||||
*
|
||||
* @param args A list containing 2 or 3 arguments: the string, start index, and optionally end index
|
||||
* @return The substring
|
||||
* @throws IllegalArgumentException if the number of arguments is not 2 or 3
|
||||
*/
|
||||
public Object substring(List<Object> args) {
|
||||
if (args.size() < 2 || args.size() > 3) {
|
||||
throw new IllegalArgumentException("substring function requires 2 or 3 arguments");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
int start = toInt(args.get(1));
|
||||
int end = args.size() == 3 ? toInt(args.get(2)) : s.length();
|
||||
return s.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index of a substring within a string.
|
||||
*
|
||||
* @param args A list containing two strings: the main string and the substring to find
|
||||
* @return The index of the substring, or -1 if not found
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly two
|
||||
*/
|
||||
public Object indexOf(List<Object> args) {
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalArgumentException("indexOf function requires exactly two arguments");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
String substring = ConditionParser.toString(args.get(1));
|
||||
return s.indexOf(substring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string contains a given substring.
|
||||
*
|
||||
* @param args A list containing two strings: the main string and the substring to check
|
||||
* @return true if the main string contains the substring, false otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly two
|
||||
*/
|
||||
public Object contains(List<Object> args) {
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalArgumentException("contains function requires exactly two arguments");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
String substring = ConditionParser.toString(args.get(1));
|
||||
return s.contains(substring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string matches a given regular expression.
|
||||
*
|
||||
* @param args A list containing two strings: the string to check and the regex pattern
|
||||
* @return true if the string matches the regex, false otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly two
|
||||
*/
|
||||
public Object matches(List<Object> args) {
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalArgumentException("matches function requires exactly two arguments");
|
||||
}
|
||||
String s = ConditionParser.toString(args.get(0));
|
||||
String regex = ConditionParser.toString(args.get(1));
|
||||
return s.matches(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates a boolean value.
|
||||
*
|
||||
* @param args A list containing a single boolean argument
|
||||
* @return The negation of the input boolean
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
*/
|
||||
public Object not(List<Object> args) {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("not function requires exactly one argument");
|
||||
}
|
||||
return !ConditionParser.toBoolean(args.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an if-then-else operation.
|
||||
*
|
||||
* @param args A list containing three arguments: condition, value if true, value if false
|
||||
* @return The second argument if the condition is true, the third argument otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly three
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:MethodName")
|
||||
public Object if_(List<Object> args) {
|
||||
if (args.size() != 3) {
|
||||
throw new IllegalArgumentException("if function requires exactly three arguments");
|
||||
}
|
||||
boolean condition = ConditionParser.toBoolean(args.get(0));
|
||||
return condition ? args.get(1) : args.get(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file or directory exists at the given path.
|
||||
*
|
||||
* @param args A list containing a single string argument representing the path
|
||||
* @return true if the file or directory exists, false otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
* @throws IOException if a problem occurs while walking the file system
|
||||
*/
|
||||
public Object exists(List<Object> args) throws IOException {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("exists function requires exactly one argument");
|
||||
}
|
||||
String path = ConditionParser.toString(args.get(0));
|
||||
return fileExists(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file or directory is missing at the given path.
|
||||
*
|
||||
* @param args A list containing a single string argument representing the path
|
||||
* @return true if the file or directory does not exist, false otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
* @throws IOException if a problem occurs while walking the file system
|
||||
*/
|
||||
public Object missing(List<Object> args) throws IOException {
|
||||
if (args.size() != 1) {
|
||||
throw new IllegalArgumentException("missing function requires exactly one argument");
|
||||
}
|
||||
String path = ConditionParser.toString(args.get(0));
|
||||
return !fileExists(path);
|
||||
}
|
||||
|
||||
private boolean fileExists(String path) throws IOException {
|
||||
String pattern = interpolator.interpolate(path, context);
|
||||
int asteriskIndex = pattern.indexOf('*');
|
||||
int questionMarkIndex = pattern.indexOf('?');
|
||||
int firstWildcardIndex = questionMarkIndex < 0
|
||||
? asteriskIndex
|
||||
: asteriskIndex < 0 ? questionMarkIndex : Math.min(asteriskIndex, questionMarkIndex);
|
||||
String fixed, glob;
|
||||
if (firstWildcardIndex < 0) {
|
||||
fixed = pattern;
|
||||
glob = "";
|
||||
} else {
|
||||
int lastSep = pattern.substring(0, firstWildcardIndex).lastIndexOf(File.separatorChar);
|
||||
if (lastSep < 0) {
|
||||
fixed = "";
|
||||
glob = pattern;
|
||||
} else {
|
||||
fixed = pattern.substring(0, lastSep);
|
||||
glob = pattern.substring(lastSep + 1);
|
||||
}
|
||||
}
|
||||
Path fixedPath = Paths.get(fixed);
|
||||
if (!Files.exists(fixedPath)) {
|
||||
return false;
|
||||
}
|
||||
if (!glob.isEmpty()) {
|
||||
PathMatcher matcher = fixedPath.getFileSystem().getPathMatcher("glob:" + glob);
|
||||
AtomicBoolean found = new AtomicBoolean(false);
|
||||
Files.walkFileTree(fixedPath, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
if (found.get() || matcher.matches(fixedPath.relativize(file))) {
|
||||
found.set(true);
|
||||
return FileVisitResult.TERMINATE;
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return found.get();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a version is within a specified version range.
|
||||
*
|
||||
* @param args A list containing two strings: the version to check and the version range
|
||||
* @return true if the version is within the range, false otherwise
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly two
|
||||
*/
|
||||
public Object inrange(List<Object> args) {
|
||||
if (args.size() != 2) {
|
||||
throw new IllegalArgumentException("inrange function requires exactly two arguments");
|
||||
}
|
||||
String version = ConditionParser.toString(args.get(0));
|
||||
String range = ConditionParser.toString(args.get(1));
|
||||
return versionParser.parseVersionRange(range).contains(versionParser.parseVersion(version));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,636 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The {@code ConditionParser} class is responsible for parsing and evaluating expressions.
|
||||
* It supports tokenizing the input expression and resolving custom functions passed in a map.
|
||||
* This class implements a recursive descent parser to handle various operations including
|
||||
* arithmetic, logical, and comparison operations, as well as function calls.
|
||||
*/
|
||||
public class ConditionParser {
|
||||
|
||||
/**
|
||||
* A functional interface that represents an expression function to be applied
|
||||
* to a list of arguments. Implementers can define custom functions.
|
||||
*/
|
||||
public interface ExpressionFunction {
|
||||
/**
|
||||
* Applies the function to the given list of arguments.
|
||||
*
|
||||
* @param args the list of arguments passed to the function
|
||||
* @return the result of applying the function
|
||||
*/
|
||||
Object apply(List<Object> args);
|
||||
}
|
||||
|
||||
private final Map<String, ExpressionFunction> functions; // Map to store functions by their names
|
||||
private final Function<String, String> propertyResolver; // Property resolver
|
||||
private List<String> tokens; // List of tokens derived from the expression
|
||||
private int current; // Keeps track of the current token index
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ConditionParser} with the given function mappings.
|
||||
*
|
||||
* @param functions a map of function names to their corresponding {@code ExpressionFunction} implementations
|
||||
* @param propertyResolver the property resolver
|
||||
*/
|
||||
public ConditionParser(Map<String, ExpressionFunction> functions, Function<String, String> propertyResolver) {
|
||||
this.functions = functions;
|
||||
this.propertyResolver = propertyResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given expression and returns the result of the evaluation.
|
||||
*
|
||||
* @param expression the expression to be parsed
|
||||
* @return the result of parsing and evaluating the expression
|
||||
*/
|
||||
public Object parse(String expression) {
|
||||
this.tokens = tokenize(expression);
|
||||
this.current = 0;
|
||||
return parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes the input expression into a list of string tokens for further parsing.
|
||||
* This method handles quoted strings, property aliases, and various operators.
|
||||
*
|
||||
* @param expression the expression to tokenize
|
||||
* @return a list of tokens
|
||||
*/
|
||||
private List<String> tokenize(String expression) {
|
||||
List<String> tokens = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char quoteType = 0;
|
||||
boolean inPropertyReference = false;
|
||||
|
||||
for (int i = 0; i < expression.length(); i++) {
|
||||
char c = expression.charAt(i);
|
||||
|
||||
if (quoteType != 0) {
|
||||
if (c == quoteType) {
|
||||
quoteType = 0;
|
||||
sb.append(c);
|
||||
tokens.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inPropertyReference) {
|
||||
if (c == '}') {
|
||||
inPropertyReference = false;
|
||||
tokens.add("${" + sb + "}");
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '$' && i + 1 < expression.length() && expression.charAt(i + 1) == '{') {
|
||||
if (!sb.isEmpty()) {
|
||||
tokens.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
}
|
||||
inPropertyReference = true;
|
||||
i++; // Skip the '{'
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '"' || c == '\'') {
|
||||
if (!sb.isEmpty()) {
|
||||
tokens.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
}
|
||||
quoteType = c;
|
||||
sb.append(c);
|
||||
} else if (c == ' ' || c == '(' || c == ')' || c == ',' || c == '+' || c == '>' || c == '<' || c == '='
|
||||
|| c == '!') {
|
||||
if (!sb.isEmpty()) {
|
||||
tokens.add(sb.toString());
|
||||
sb.setLength(0);
|
||||
}
|
||||
if (c != ' ') {
|
||||
if ((c == '>' || c == '<' || c == '=' || c == '!')
|
||||
&& i + 1 < expression.length()
|
||||
&& expression.charAt(i + 1) == '=') {
|
||||
tokens.add(c + "=");
|
||||
i++; // Skip the next character
|
||||
} else {
|
||||
tokens.add(String.valueOf(c));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (inPropertyReference) {
|
||||
throw new RuntimeException("Unclosed property reference: ${");
|
||||
}
|
||||
|
||||
if (!sb.isEmpty()) {
|
||||
tokens.add(sb.toString());
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the next expression from the list of tokens.
|
||||
*
|
||||
* @return the parsed expression as an object
|
||||
* @throws RuntimeException if there are unexpected tokens after the end of the expression
|
||||
*/
|
||||
private Object parseExpression() {
|
||||
Object result = parseLogicalOr();
|
||||
if (current < tokens.size()) {
|
||||
throw new RuntimeException("Unexpected tokens after end of expression");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses logical OR operations.
|
||||
*
|
||||
* @return the result of parsing logical OR operations
|
||||
*/
|
||||
private Object parseLogicalOr() {
|
||||
Object left = parseLogicalAnd();
|
||||
while (current < tokens.size() && tokens.get(current).equals("||")) {
|
||||
current++;
|
||||
Object right = parseLogicalAnd();
|
||||
left = (boolean) left || (boolean) right;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses logical AND operations.
|
||||
*
|
||||
* @return the result of parsing logical AND operations
|
||||
*/
|
||||
private Object parseLogicalAnd() {
|
||||
Object left = parseComparison();
|
||||
while (current < tokens.size() && tokens.get(current).equals("&&")) {
|
||||
current++;
|
||||
Object right = parseComparison();
|
||||
left = (boolean) left && (boolean) right;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses comparison operations.
|
||||
*
|
||||
* @return the result of parsing comparison operations
|
||||
*/
|
||||
private Object parseComparison() {
|
||||
Object left = parseAddSubtract();
|
||||
while (current < tokens.size()
|
||||
&& (tokens.get(current).equals(">")
|
||||
|| tokens.get(current).equals("<")
|
||||
|| tokens.get(current).equals(">=")
|
||||
|| tokens.get(current).equals("<=")
|
||||
|| tokens.get(current).equals("==")
|
||||
|| tokens.get(current).equals("!="))) {
|
||||
String operator = tokens.get(current);
|
||||
current++;
|
||||
Object right = parseAddSubtract();
|
||||
left = compare(left, operator, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses addition and subtraction operations.
|
||||
*
|
||||
* @return the result of parsing addition and subtraction operations
|
||||
*/
|
||||
private Object parseAddSubtract() {
|
||||
Object left = parseMultiplyDivide();
|
||||
while (current < tokens.size()
|
||||
&& (tokens.get(current).equals("+") || tokens.get(current).equals("-"))) {
|
||||
String operator = tokens.get(current);
|
||||
current++;
|
||||
Object right = parseMultiplyDivide();
|
||||
if (operator.equals("+")) {
|
||||
left = add(left, right);
|
||||
} else {
|
||||
left = subtract(left, right);
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses multiplication and division operations.
|
||||
*
|
||||
* @return the result of parsing multiplication and division operations
|
||||
*/
|
||||
private Object parseMultiplyDivide() {
|
||||
Object left = parseUnary();
|
||||
while (current < tokens.size()
|
||||
&& (tokens.get(current).equals("*") || tokens.get(current).equals("/"))) {
|
||||
String operator = tokens.get(current);
|
||||
current++;
|
||||
Object right = parseUnary();
|
||||
if (operator.equals("*")) {
|
||||
left = multiply(left, right);
|
||||
} else {
|
||||
left = divide(left, right);
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses unary operations (negation).
|
||||
*
|
||||
* @return the result of parsing unary operations
|
||||
*/
|
||||
private Object parseUnary() {
|
||||
if (current < tokens.size() && tokens.get(current).equals("-")) {
|
||||
current++;
|
||||
Object value = parseUnary();
|
||||
return negate(value);
|
||||
}
|
||||
return parseTerm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses individual terms (numbers, strings, booleans, parentheses, functions).
|
||||
*
|
||||
* @return the parsed term
|
||||
* @throws RuntimeException if the expression ends unexpectedly or contains unknown tokens
|
||||
*/
|
||||
private Object parseTerm() {
|
||||
if (current >= tokens.size()) {
|
||||
throw new RuntimeException("Unexpected end of expression");
|
||||
}
|
||||
|
||||
String token = tokens.get(current);
|
||||
if (token.equals("(")) {
|
||||
return parseParentheses();
|
||||
} else if (functions.containsKey(token)) {
|
||||
return parseFunction();
|
||||
} else if ((token.startsWith("\"") && token.endsWith("\"")) || (token.startsWith("'") && token.endsWith("'"))) {
|
||||
current++;
|
||||
return token.length() > 1 ? token.substring(1, token.length() - 1) : "";
|
||||
} else if (token.equalsIgnoreCase("true") || token.equalsIgnoreCase("false")) {
|
||||
current++;
|
||||
return Boolean.parseBoolean(token);
|
||||
} else if (token.startsWith("${") && token.endsWith("}")) {
|
||||
current++;
|
||||
String propertyName = token.substring(2, token.length() - 1);
|
||||
return propertyResolver.apply(propertyName);
|
||||
} else {
|
||||
try {
|
||||
current++;
|
||||
return Double.parseDouble(token);
|
||||
} catch (NumberFormatException e) {
|
||||
// If it's not a number, treat it as a variable or unknown function
|
||||
return parseVariableOrUnknownFunction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a token that could be either a variable or an unknown function.
|
||||
*
|
||||
* @return the result of parsing a variable or unknown function
|
||||
* @throws RuntimeException if an unknown function is encountered
|
||||
*/
|
||||
private Object parseVariableOrUnknownFunction() {
|
||||
current--; // Move back to the token we couldn't parse as a number
|
||||
String name = tokens.get(current);
|
||||
current++;
|
||||
|
||||
// Check if it's followed by an opening parenthesis, indicating a function call
|
||||
if (current < tokens.size() && tokens.get(current).equals("(")) {
|
||||
// It's a function call, parse it as such
|
||||
List<Object> args = parseArgumentList();
|
||||
if (functions.containsKey(name)) {
|
||||
return functions.get(name).apply(args);
|
||||
} else {
|
||||
throw new RuntimeException("Unknown function: " + name);
|
||||
}
|
||||
} else {
|
||||
// It's a variable
|
||||
// Here you might want to handle variables differently
|
||||
// For now, we'll throw an exception
|
||||
throw new RuntimeException("Unknown variable: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of arguments for a function call.
|
||||
*
|
||||
* @return a list of parsed arguments
|
||||
* @throws RuntimeException if there's a mismatch in parentheses
|
||||
*/
|
||||
private List<Object> parseArgumentList() {
|
||||
List<Object> args = new ArrayList<>();
|
||||
current++; // Skip the opening parenthesis
|
||||
while (current < tokens.size() && !tokens.get(current).equals(")")) {
|
||||
args.add(parseLogicalOr());
|
||||
if (current < tokens.size() && tokens.get(current).equals(",")) {
|
||||
current++;
|
||||
}
|
||||
}
|
||||
if (current >= tokens.size() || !tokens.get(current).equals(")")) {
|
||||
throw new RuntimeException("Mismatched parentheses: missing closing parenthesis in function call");
|
||||
}
|
||||
current++; // Skip the closing parenthesis
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a function call.
|
||||
*
|
||||
* @return the result of the function call
|
||||
*/
|
||||
private Object parseFunction() {
|
||||
String functionName = tokens.get(current);
|
||||
current++;
|
||||
List<Object> args = parseArgumentList();
|
||||
return functions.get(functionName).apply(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an expression within parentheses.
|
||||
*
|
||||
* @return the result of parsing the expression within parentheses
|
||||
* @throws RuntimeException if there's a mismatch in parentheses
|
||||
*/
|
||||
private Object parseParentheses() {
|
||||
current++; // Skip the opening parenthesis
|
||||
Object result = parseLogicalOr();
|
||||
if (current >= tokens.size() || !tokens.get(current).equals(")")) {
|
||||
throw new RuntimeException("Mismatched parentheses: missing closing parenthesis");
|
||||
}
|
||||
current++; // Skip the closing parenthesis
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two objects, handling string concatenation and numeric addition.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
* @return the result of the addition
|
||||
* @throws RuntimeException if the operands cannot be added
|
||||
*/
|
||||
private static Object add(Object left, Object right) {
|
||||
if (left instanceof String || right instanceof String) {
|
||||
return toString(left) + toString(right);
|
||||
} else if (left instanceof Number && right instanceof Number) {
|
||||
return ((Number) left).doubleValue() + ((Number) right).doubleValue();
|
||||
} else {
|
||||
throw new RuntimeException("Cannot add " + left + " and " + right);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates a numeric value.
|
||||
*
|
||||
* @param value the value to negate
|
||||
* @return the negated value
|
||||
* @throws RuntimeException if the value cannot be negated
|
||||
*/
|
||||
private Object negate(Object value) {
|
||||
if (value instanceof Number) {
|
||||
return -((Number) value).doubleValue();
|
||||
}
|
||||
throw new RuntimeException("Cannot negate non-numeric value: " + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts the right operand from the left operand.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
* @return the result of the subtraction
|
||||
* @throws RuntimeException if the operands cannot be subtracted
|
||||
*/
|
||||
private static Object subtract(Object left, Object right) {
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
return ((Number) left).doubleValue() - ((Number) right).doubleValue();
|
||||
} else {
|
||||
throw new RuntimeException("Cannot subtract " + right + " from " + left);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two numeric operands.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param right the right operand
|
||||
* @return the result of the multiplication
|
||||
* @throws RuntimeException if the operands cannot be multiplied
|
||||
*/
|
||||
private static Object multiply(Object left, Object right) {
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
return ((Number) left).doubleValue() * ((Number) right).doubleValue();
|
||||
} else {
|
||||
throw new RuntimeException("Cannot multiply " + left + " and " + right);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides the left operand by the right operand.
|
||||
*
|
||||
* @param left the left operand (dividend)
|
||||
* @param right the right operand (divisor)
|
||||
* @return the result of the division
|
||||
* @throws RuntimeException if the operands cannot be divided
|
||||
* @throws ArithmeticException if attempting to divide by zero
|
||||
*/
|
||||
private static Object divide(Object left, Object right) {
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
double divisor = ((Number) right).doubleValue();
|
||||
if (divisor == 0) {
|
||||
throw new ArithmeticException("Division by zero");
|
||||
}
|
||||
return ((Number) left).doubleValue() / divisor;
|
||||
} else {
|
||||
throw new RuntimeException("Cannot divide " + left + " by " + right);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two objects based on the given operator.
|
||||
* Supports comparison of numbers and strings, and equality checks for null values.
|
||||
*
|
||||
* @param left the left operand
|
||||
* @param operator the comparison operator (">", "<", ">=", "<=", "==", or "!=")
|
||||
* @param right the right operand
|
||||
* @return the result of the comparison (a boolean value)
|
||||
* @throws IllegalStateException if an unknown operator is provided
|
||||
* @throws RuntimeException if the operands cannot be compared
|
||||
*/
|
||||
private static Object compare(Object left, String operator, Object right) {
|
||||
if (left == null && right == null) {
|
||||
return true;
|
||||
}
|
||||
if (left == null || right == null) {
|
||||
if ("==".equals(operator)) {
|
||||
return false;
|
||||
} else if ("!=".equals(operator)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
double leftVal = ((Number) left).doubleValue();
|
||||
double rightVal = ((Number) right).doubleValue();
|
||||
return switch (operator) {
|
||||
case ">" -> leftVal > rightVal;
|
||||
case "<" -> leftVal < rightVal;
|
||||
case ">=" -> leftVal >= rightVal;
|
||||
case "<=" -> leftVal <= rightVal;
|
||||
case "==" -> Math.abs(leftVal - rightVal) < 1e-9;
|
||||
case "!=" -> Math.abs(leftVal - rightVal) >= 1e-9;
|
||||
default -> throw new IllegalStateException("Unknown operator: " + operator);
|
||||
};
|
||||
} else if (left instanceof String && right instanceof String) {
|
||||
int comparison = ((String) left).compareTo((String) right);
|
||||
return switch (operator) {
|
||||
case ">" -> comparison > 0;
|
||||
case "<" -> comparison < 0;
|
||||
case ">=" -> comparison >= 0;
|
||||
case "<=" -> comparison <= 0;
|
||||
case "==" -> comparison == 0;
|
||||
case "!=" -> comparison != 0;
|
||||
default -> throw new IllegalStateException("Unknown operator: " + operator);
|
||||
};
|
||||
}
|
||||
throw new RuntimeException("Cannot compare " + left + " and " + right + " with operator " + operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to a string representation.
|
||||
* If the object is a {@code Double}, it formats it without any decimal places.
|
||||
* Otherwise, it uses the {@code String.valueOf} method.
|
||||
*
|
||||
* @param value the object to convert to a string
|
||||
* @return the string representation of the object
|
||||
*/
|
||||
public static String toString(Object value) {
|
||||
if (value instanceof Double || value instanceof Float) {
|
||||
double doubleValue = ((Number) value).doubleValue();
|
||||
if (doubleValue == Math.floor(doubleValue) && !Double.isInfinite(doubleValue)) {
|
||||
return String.format("%.0f", doubleValue);
|
||||
}
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to a boolean value.
|
||||
* If the object is:
|
||||
* - a {@code Boolean}, returns its value directly.
|
||||
* - a {@code String}, returns {@code true} if the string is non-blank.
|
||||
* - a {@code Number}, returns {@code true} if its integer value is not zero.
|
||||
* For other object types, returns {@code true} if the object is non-null.
|
||||
*
|
||||
* @param value the object to convert to a boolean
|
||||
* @return the boolean representation of the object
|
||||
*/
|
||||
public static Boolean toBoolean(Object value) {
|
||||
if (value instanceof Boolean b) {
|
||||
return b; // Returns the boolean value
|
||||
} else if (value instanceof String s) {
|
||||
return !s.isBlank(); // True if the string is not blank
|
||||
} else if (value instanceof Number b) {
|
||||
return b.intValue() != 0; // True if the number is not zero
|
||||
} else {
|
||||
return value != null; // True if the object is not null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to a double value.
|
||||
* If the object is:
|
||||
* - a {@code Number}, returns its double value.
|
||||
* - a {@code String}, tries to parse it as a double.
|
||||
* - a {@code Boolean}, returns {@code 1.0} for {@code true}, {@code 0.0} for {@code false}.
|
||||
* If the object cannot be converted, a {@code RuntimeException} is thrown.
|
||||
*
|
||||
* @param value the object to convert to a double
|
||||
* @return the double representation of the object
|
||||
* @throws RuntimeException if the object cannot be converted to a double
|
||||
*/
|
||||
public static double toDouble(Object value) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue(); // Converts number to double
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Double.parseDouble((String) value); // Tries to parse string as double
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("Cannot convert string to number: " + value);
|
||||
}
|
||||
} else if (value instanceof Boolean) {
|
||||
return ((Boolean) value) ? 1.0 : 0.0; // True = 1.0, False = 0.0
|
||||
} else {
|
||||
throw new RuntimeException("Cannot convert to number: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to an integer value.
|
||||
* If the object is:
|
||||
* - a {@code Number}, returns its integer value.
|
||||
* - a {@code String}, tries to parse it as an integer, or as a double then converted to an integer.
|
||||
* - a {@code Boolean}, returns {@code 1} for {@code true}, {@code 0} for {@code false}.
|
||||
* If the object cannot be converted, a {@code RuntimeException} is thrown.
|
||||
*
|
||||
* @param value the object to convert to an integer
|
||||
* @return the integer representation of the object
|
||||
* @throws RuntimeException if the object cannot be converted to an integer
|
||||
*/
|
||||
public static int toInt(Object value) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue(); // Converts number to int
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) value); // Tries to parse string as int
|
||||
} catch (NumberFormatException e) {
|
||||
// If string is not an int, tries parsing as double and converting to int
|
||||
try {
|
||||
return (int) Double.parseDouble((String) value);
|
||||
} catch (NumberFormatException e2) {
|
||||
throw new RuntimeException("Cannot convert string to integer: " + value);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof Boolean) {
|
||||
return ((Boolean) value) ? 1 : 0; // True = 1, False = 0
|
||||
} else {
|
||||
throw new RuntimeException("Cannot convert to integer: " + value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.maven.api.di.Inject;
|
||||
import org.apache.maven.api.di.Named;
|
||||
import org.apache.maven.api.di.Singleton;
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.apache.maven.api.services.BuilderProblem.Severity;
|
||||
import org.apache.maven.api.services.ModelProblem.Version;
|
||||
import org.apache.maven.api.services.ModelProblemCollector;
|
||||
import org.apache.maven.api.services.VersionParser;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.api.services.model.ProfileActivator;
|
||||
import org.apache.maven.api.services.model.RootLocator;
|
||||
import org.apache.maven.internal.impl.model.DefaultInterpolator;
|
||||
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||
|
||||
import static org.apache.maven.internal.impl.model.profile.ConditionParser.toBoolean;
|
||||
|
||||
/**
|
||||
* This class is responsible for activating profiles based on conditions specified in the profile's activation section.
|
||||
* It evaluates the condition expression and determines whether the profile should be active.
|
||||
*/
|
||||
@Named("condition")
|
||||
@Singleton
|
||||
public class ConditionProfileActivator implements ProfileActivator {
|
||||
|
||||
private final VersionParser versionParser;
|
||||
private final ProfileActivationFilePathInterpolator interpolator;
|
||||
private final RootLocator rootLocator;
|
||||
|
||||
/**
|
||||
* Constructs a new ConditionProfileActivator with the necessary dependencies.
|
||||
*
|
||||
* @param versionParser The parser for handling version comparisons
|
||||
* @param interpolator The interpolator for resolving file paths
|
||||
* @param rootLocator The locator for finding the project root directory
|
||||
*/
|
||||
@Inject
|
||||
public ConditionProfileActivator(
|
||||
VersionParser versionParser, ProfileActivationFilePathInterpolator interpolator, RootLocator rootLocator) {
|
||||
this.versionParser = versionParser;
|
||||
this.interpolator = interpolator;
|
||||
this.rootLocator = rootLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a profile should be active based on its condition.
|
||||
*
|
||||
* @param profile The profile to evaluate
|
||||
* @param context The context in which the profile is being evaluated
|
||||
* @param problems A collector for any problems encountered during evaluation
|
||||
* @return true if the profile should be active, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||
if (profile.getActivation() == null || profile.getActivation().getCondition() == null) {
|
||||
return false;
|
||||
}
|
||||
String condition = profile.getActivation().getCondition();
|
||||
try {
|
||||
Map<String, ConditionParser.ExpressionFunction> functions =
|
||||
registerFunctions(context, versionParser, interpolator);
|
||||
Function<String, String> propertyResolver = s -> property(context, rootLocator, s);
|
||||
return toBoolean(new ConditionParser(functions, propertyResolver).parse(condition));
|
||||
} catch (Exception e) {
|
||||
problems.add(
|
||||
Severity.ERROR, Version.V41, "Error parsing profile activation condition: " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the condition is present in the profile's configuration.
|
||||
*
|
||||
* @param profile The profile to check
|
||||
* @param context The context in which the profile is being evaluated
|
||||
* @param problems A collector for any problems encountered during evaluation
|
||||
* @return true if the condition is present and not blank, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean presentInConfig(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
|
||||
Activation activation = profile.getActivation();
|
||||
if (activation == null) {
|
||||
return false;
|
||||
}
|
||||
return activation.getCondition() != null && !activation.getCondition().isBlank();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the condition functions that can be used in profile activation expressions.
|
||||
*
|
||||
* @param context The profile activation context
|
||||
* @param versionParser The parser for handling version comparisons
|
||||
* @param interpolator The interpolator for resolving file paths
|
||||
* @return A map of function names to their implementations
|
||||
*/
|
||||
public static Map<String, ConditionParser.ExpressionFunction> registerFunctions(
|
||||
ProfileActivationContext context,
|
||||
VersionParser versionParser,
|
||||
ProfileActivationFilePathInterpolator interpolator) {
|
||||
Map<String, ConditionParser.ExpressionFunction> functions = new HashMap<>();
|
||||
|
||||
ConditionFunctions conditionFunctions = new ConditionFunctions(context, versionParser, interpolator);
|
||||
|
||||
for (java.lang.reflect.Method method : ConditionFunctions.class.getDeclaredMethods()) {
|
||||
String methodName = method.getName();
|
||||
if (methodName.endsWith("_")) {
|
||||
methodName = methodName.substring(0, methodName.length() - 1);
|
||||
}
|
||||
final String finalMethodName = methodName;
|
||||
|
||||
functions.put(finalMethodName, args -> {
|
||||
try {
|
||||
return method.invoke(conditionFunctions, args);
|
||||
} catch (Exception e) {
|
||||
StringBuilder causeChain = new StringBuilder();
|
||||
Throwable cause = e;
|
||||
while (cause != null) {
|
||||
if (!causeChain.isEmpty()) {
|
||||
causeChain.append(" Caused by: ");
|
||||
}
|
||||
causeChain.append(cause.toString());
|
||||
cause = cause.getCause();
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"Error invoking function '" + finalMethodName + "': " + e + ". Cause chain: " + causeChain,
|
||||
e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a property from the project context.
|
||||
* Special function used to support the <code>${property}</code> syntax.
|
||||
*
|
||||
* The profile activation is done twice: once on the file model (so the model
|
||||
* which has just been read from the file) and once while computing the effective
|
||||
* model (so the model which will be used to build the project). We do need
|
||||
* those two activations to be consistent, so we need to restrict access to
|
||||
* properties that cannot change between file and effective model.
|
||||
*
|
||||
* @param name The property name
|
||||
* @return The value of the property, or null if not found
|
||||
* @throws IllegalArgumentException if the number of arguments is not exactly one
|
||||
*/
|
||||
static String property(ProfileActivationContext context, RootLocator rootLocator, String name) {
|
||||
String value = doGetProperty(context, rootLocator, name);
|
||||
return new DefaultInterpolator().interpolate(value, s -> doGetProperty(context, rootLocator, s));
|
||||
}
|
||||
|
||||
static String doGetProperty(ProfileActivationContext context, RootLocator rootLocator, String name) {
|
||||
// Handle special project-related properties
|
||||
if ("project.basedir".equals(name)) {
|
||||
Path basedir = context.getModel().getProjectDirectory();
|
||||
return basedir != null ? basedir.toFile().getAbsolutePath() : null;
|
||||
}
|
||||
if ("project.rootDirectory".equals(name)) {
|
||||
Path basedir = context.getModel().getProjectDirectory();
|
||||
if (basedir != null) {
|
||||
Path root = rootLocator.findMandatoryRoot(basedir);
|
||||
return root.toFile().getAbsolutePath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if ("project.artifactId".equals(name)) {
|
||||
return context.getModel().getArtifactId();
|
||||
}
|
||||
if ("project.packaging".equals(name)) {
|
||||
return context.getModel().getPackaging();
|
||||
}
|
||||
|
||||
// Check user properties
|
||||
String v = context.getUserProperties().get(name);
|
||||
if (v == null) {
|
||||
// Check project properties
|
||||
// TODO: this may leads to instability between file model activation and effective model activation
|
||||
// as the effective model properties may be different from the file model
|
||||
v = context.getModel().getProperties().get(name);
|
||||
}
|
||||
if (v == null) {
|
||||
// Check system properties
|
||||
v = context.getSystemProperties().get(name);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
|
|||
}
|
||||
|
||||
private boolean determineVersionMatch(String expectedVersion, String actualVersion) {
|
||||
String test = expectedVersion;
|
||||
String test = expectedVersion.toLowerCase(Locale.ENGLISH);
|
||||
boolean reverse = false;
|
||||
final boolean result;
|
||||
if (test.startsWith(REGEX_PREFIX)) {
|
||||
|
@ -117,7 +117,7 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
|
|||
}
|
||||
|
||||
private boolean determineArchMatch(String expectedArch, String actualArch) {
|
||||
String test = expectedArch;
|
||||
String test = expectedArch.toLowerCase(Locale.ENGLISH);
|
||||
boolean reverse = false;
|
||||
|
||||
if (test.startsWith("!")) {
|
||||
|
@ -130,8 +130,8 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
|
|||
return reverse != result;
|
||||
}
|
||||
|
||||
private boolean determineNameMatch(String expectedName, String actualName) {
|
||||
String test = expectedName;
|
||||
private boolean determineNameMatch(String family, String actualName) {
|
||||
String test = family.toLowerCase(Locale.ENGLISH);
|
||||
boolean reverse = false;
|
||||
|
||||
if (test.startsWith("!")) {
|
||||
|
|
|
@ -47,7 +47,7 @@ public class PackagingProfileActivator implements ProfileActivator {
|
|||
}
|
||||
|
||||
private static boolean isPackaging(ProfileActivationContext context, String p) {
|
||||
String packaging = context.getUserProperties().get(ProfileActivationContext.PROPERTY_NAME_PACKAGING);
|
||||
String packaging = context.getModel().getPackaging();
|
||||
return Objects.equals(p, packaging);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,9 @@ public class PropertyProfileActivator implements ProfileActivator {
|
|||
}
|
||||
|
||||
String sysValue = context.getUserProperties().get(name);
|
||||
if (sysValue == null && "packaging".equals(name)) {
|
||||
sysValue = context.getModel().getPackaging();
|
||||
}
|
||||
if (sysValue == null) {
|
||||
sysValue = context.getSystemProperties().get(name);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.api.services.model.ProfileActivator;
|
||||
import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Provides common services to test {@link ProfileActivator} implementations.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractProfileActivatorTest<T extends ProfileActivator> {
|
||||
|
||||
protected T activator;
|
||||
|
||||
@BeforeEach
|
||||
abstract void setUp() throws Exception;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
activator = null;
|
||||
}
|
||||
|
||||
protected ProfileActivationContext newContext(final Properties userProperties, final Properties systemProperties) {
|
||||
DefaultProfileActivationContext context = new DefaultProfileActivationContext();
|
||||
return context.setUserProperties(userProperties)
|
||||
.setSystemProperties(systemProperties)
|
||||
.setModel(Model.newInstance());
|
||||
}
|
||||
|
||||
protected void assertActivation(boolean active, Profile profile, ProfileActivationContext context) {
|
||||
SimpleProblemCollector problems = new SimpleProblemCollector();
|
||||
|
||||
boolean res = activator.isActive(profile, context, problems);
|
||||
assertEquals(0, problems.getErrors().size(), problems.getErrors().toString());
|
||||
assertEquals(0, problems.getWarnings().size(), problems.getWarnings().toString());
|
||||
assertEquals(active, res);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.internal.impl.DefaultModelVersionParser;
|
||||
import org.apache.maven.internal.impl.DefaultVersionParser;
|
||||
import org.apache.maven.internal.impl.model.DefaultInterpolator;
|
||||
import org.apache.maven.internal.impl.model.DefaultPathTranslator;
|
||||
import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
|
||||
import org.apache.maven.internal.impl.model.DefaultRootLocator;
|
||||
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||
import org.apache.maven.internal.impl.model.profile.ConditionParser.ExpressionFunction;
|
||||
import org.eclipse.aether.util.version.GenericVersionScheme;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ConditionParserTest {
|
||||
ConditionParser parser;
|
||||
Map<String, ExpressionFunction> functions;
|
||||
Function<String, String> propertyResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ProfileActivationContext context = createMockContext();
|
||||
DefaultVersionParser versionParser =
|
||||
new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme()));
|
||||
ProfileActivationFilePathInterpolator interpolator = new ProfileActivationFilePathInterpolator(
|
||||
new DefaultPathTranslator(), bd -> true, new DefaultInterpolator());
|
||||
DefaultRootLocator rootLocator = new DefaultRootLocator();
|
||||
|
||||
functions = ConditionProfileActivator.registerFunctions(context, versionParser, interpolator);
|
||||
propertyResolver = s -> ConditionProfileActivator.property(context, rootLocator, s);
|
||||
parser = new ConditionParser(functions, propertyResolver);
|
||||
}
|
||||
|
||||
private ProfileActivationContext createMockContext() {
|
||||
DefaultProfileActivationContext context = new DefaultProfileActivationContext();
|
||||
Properties systemProperties = new Properties();
|
||||
systemProperties.setProperty("os.name", "windows");
|
||||
systemProperties.setProperty("os.arch", "amd64");
|
||||
systemProperties.setProperty("os.version", "10.0");
|
||||
systemProperties.setProperty("java.version", "1.8.0_292");
|
||||
context.setSystemProperties(systemProperties);
|
||||
context.setModel(Model.newInstance());
|
||||
return context;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringLiterals() {
|
||||
assertEquals("Hello, World!", parser.parse("'Hello, World!'"));
|
||||
assertEquals("Hello, World!", parser.parse("\"Hello, World!\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringConcatenation() {
|
||||
assertEquals("HelloWorld", parser.parse("'Hello' + 'World'"));
|
||||
assertEquals("Hello123", parser.parse("'Hello' + 123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLengthFunction() {
|
||||
assertEquals(13, parser.parse("length('Hello, World!')"));
|
||||
assertEquals(5, parser.parse("length(\"Hello\")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCaseConversionFunctions() {
|
||||
assertEquals("HELLO", parser.parse("upper('hello')"));
|
||||
assertEquals("world", parser.parse("lower('WORLD')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConcatFunction() {
|
||||
assertEquals("HelloWorld", parser.parse("'Hello' + 'World'"));
|
||||
assertEquals("The answer is 42", parser.parse("'The answer is ' + 42"));
|
||||
assertEquals("The answer is 42", parser.parse("'The answer is ' + 42.0"));
|
||||
assertEquals("The answer is 42", parser.parse("'The answer is ' + 42.0f"));
|
||||
assertEquals("Pi is approximately 3.14", parser.parse("'Pi is approximately ' + 3.14"));
|
||||
assertEquals("Pi is approximately 3.14", parser.parse("'Pi is approximately ' + 3.14f"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString() {
|
||||
assertEquals("42", ConditionParser.toString(42));
|
||||
assertEquals("42", ConditionParser.toString(42L));
|
||||
assertEquals("42", ConditionParser.toString(42.0));
|
||||
assertEquals("42", ConditionParser.toString(42.0f));
|
||||
assertEquals("3.14", ConditionParser.toString(3.14));
|
||||
assertEquals("3.14", ConditionParser.toString(3.14f));
|
||||
assertEquals("true", ConditionParser.toString(true));
|
||||
assertEquals("false", ConditionParser.toString(false));
|
||||
assertEquals("hello", ConditionParser.toString("hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSubstringFunction() {
|
||||
assertEquals("World", parser.parse("substring('Hello, World!', 7, 12)"));
|
||||
assertEquals("World!", parser.parse("substring('Hello, World!', 7)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIndexOf() {
|
||||
assertEquals(7, parser.parse("indexOf('Hello, World!', 'World')"));
|
||||
assertEquals(-1, parser.parse("indexOf('Hello, World!', 'OpenAI')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInRange() {
|
||||
assertTrue((Boolean) parser.parse("inrange('1.8.0_292', '[1.8,2.0)')"));
|
||||
assertFalse((Boolean) parser.parse("inrange('1.7.0', '[1.8,2.0)')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIfFunction() {
|
||||
assertEquals("long", parser.parse("if(length('test') > 3, 'long', 'short')"));
|
||||
assertEquals("short", parser.parse("if(length('hi') > 3, 'long', 'short')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContainsFunction() {
|
||||
assertTrue((Boolean) parser.parse("contains('Hello, World!', 'World')"));
|
||||
assertFalse((Boolean) parser.parse("contains('Hello, World!', 'OpenAI')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatchesFunction() {
|
||||
assertTrue((Boolean) parser.parse("matches('test123', '\\w+')"));
|
||||
assertFalse((Boolean) parser.parse("matches('test123', '\\d+')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComplexExpression() {
|
||||
String expression = "if(contains(lower('HELLO WORLD'), 'hello'), upper('success') + '!', 'failure')";
|
||||
assertEquals("SUCCESS!", parser.parse(expression));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStringComparison() {
|
||||
assertTrue((Boolean) parser.parse("'abc' != 'cdf'"));
|
||||
assertFalse((Boolean) parser.parse("'abc' != 'abc'"));
|
||||
assertTrue((Boolean) parser.parse("'abc' == 'abc'"));
|
||||
assertFalse((Boolean) parser.parse("'abc' == 'cdf'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParenthesesMismatch() {
|
||||
functions.put("property", args -> "foo");
|
||||
functions.put("inrange", args -> false);
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() -> parser.parse(
|
||||
"property('os.name') == 'windows' && property('os.arch') != 'amd64') && inrange(property('os.version'), '[99,)')"),
|
||||
"Should throw RuntimeException due to parentheses mismatch");
|
||||
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() -> parser.parse(
|
||||
"(property('os.name') == 'windows' && property('os.arch') != 'amd64') && inrange(property('os.version'), '[99,)'"),
|
||||
"Should throw RuntimeException due to parentheses mismatch");
|
||||
|
||||
// This should not throw an exception if parentheses are balanced and properties are handled correctly
|
||||
assertDoesNotThrow(
|
||||
() -> parser.parse(
|
||||
"(property('os.name') == 'windows' && property('os.arch') != 'amd64') && inrange(property('os.version'), '[99,)')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBasicArithmetic() {
|
||||
assertEquals(5.0, parser.parse("2 + 3"));
|
||||
assertEquals(10.0, parser.parse("15 - 5"));
|
||||
assertEquals(24.0, parser.parse("6 * 4"));
|
||||
assertEquals(3.0, parser.parse("9 / 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testArithmeticPrecedence() {
|
||||
assertEquals(14.0, parser.parse("2 + 3 * 4"));
|
||||
assertEquals(20.0, parser.parse("(2 + 3) * 4"));
|
||||
assertEquals(11.0, parser.parse("15 - 6 + 2"));
|
||||
assertEquals(10.0, parser.parse("10 / 2 + 2 * 2.5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFloatingPointArithmetic() {
|
||||
assertEquals(5.5, parser.parse("2.2 + 3.3"));
|
||||
assertEquals(0.1, (Double) parser.parse("3.3 - 3.2"), 1e-10);
|
||||
assertEquals(6.25, parser.parse("2.5 * 2.5"));
|
||||
assertEquals(2.5, parser.parse("5 / 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testArithmeticComparisons() {
|
||||
assertTrue((Boolean) parser.parse("5 > 3"));
|
||||
assertTrue((Boolean) parser.parse("3 < 5"));
|
||||
assertTrue((Boolean) parser.parse("5 >= 5"));
|
||||
assertTrue((Boolean) parser.parse("3 <= 3"));
|
||||
assertTrue((Boolean) parser.parse("5 == 5"));
|
||||
assertTrue((Boolean) parser.parse("5 != 3"));
|
||||
assertFalse((Boolean) parser.parse("5 < 3"));
|
||||
assertFalse((Boolean) parser.parse("3 > 5"));
|
||||
assertFalse((Boolean) parser.parse("5 != 5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComplexArithmeticExpressions() {
|
||||
assertFalse((Boolean) parser.parse("(2 + 3 * 4) > (10 + 5)"));
|
||||
assertTrue((Boolean) parser.parse("(2 + 3 * 4) < (10 + 5)"));
|
||||
assertTrue((Boolean) parser.parse("(10 / 2 + 3) == 8"));
|
||||
assertTrue((Boolean) parser.parse("(10 / 2 + 3) != 9"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testArithmeticFunctions() {
|
||||
assertEquals(5.0, parser.parse("2 + 3"));
|
||||
assertEquals(2.0, parser.parse("5 - 3"));
|
||||
assertEquals(15.0, parser.parse("3 * 5"));
|
||||
assertEquals(2.5, parser.parse("5 / 2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCombinedArithmeticAndLogic() {
|
||||
assertTrue((Boolean) parser.parse("(5 > 3) && (10 / 2 == 5)"));
|
||||
assertFalse((Boolean) parser.parse("(5 < 3) || (10 / 2 != 5)"));
|
||||
assertTrue((Boolean) parser.parse("2 + 3 == 1 * 5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDivisionByZero() {
|
||||
assertThrows(ArithmeticException.class, () -> parser.parse("5 / 0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropertyAlias() {
|
||||
assertTrue((Boolean) parser.parse("${os.name} == 'windows'"));
|
||||
assertFalse((Boolean) parser.parse("${os.name} == 'linux'"));
|
||||
assertTrue((Boolean) parser.parse("${os.arch} == 'amd64' && ${os.name} == 'windows'"));
|
||||
assertThrows(RuntimeException.class, () -> parser.parse("${unclosed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedPropertyAlias() {
|
||||
functions.put("property", args -> {
|
||||
if (args.get(0).equals("project.rootDirectory")) return "/home/user/project";
|
||||
return null;
|
||||
});
|
||||
functions.put("exists", args -> true); // Mock implementation
|
||||
|
||||
Object result = parser.parse("exists('${project.rootDirectory}/someFile.txt')");
|
||||
assertTrue((Boolean) result);
|
||||
|
||||
result = parser.parse("exists('${project.rootDirectory}/${nested.property}/someFile.txt')");
|
||||
assertTrue((Boolean) result);
|
||||
|
||||
assertDoesNotThrow(() -> parser.parse("property('')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToInt() {
|
||||
assertEquals(123, ConditionParser.toInt(123));
|
||||
assertEquals(123, ConditionParser.toInt(123L));
|
||||
assertEquals(123, ConditionParser.toInt(123.0));
|
||||
assertEquals(123, ConditionParser.toInt(123.5)); // This will truncate the decimal part
|
||||
assertEquals(123, ConditionParser.toInt("123"));
|
||||
assertEquals(123, ConditionParser.toInt("123.0"));
|
||||
assertEquals(123, ConditionParser.toInt("123.5")); // This will truncate the decimal part
|
||||
assertEquals(1, ConditionParser.toInt(true));
|
||||
assertEquals(0, ConditionParser.toInt(false));
|
||||
|
||||
assertThrows(RuntimeException.class, () -> ConditionParser.toInt("not a number"));
|
||||
assertThrows(RuntimeException.class, () -> ConditionParser.toInt(new Object()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,503 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.ActivationFile;
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.apache.maven.api.services.model.RootLocator;
|
||||
import org.apache.maven.internal.impl.DefaultModelVersionParser;
|
||||
import org.apache.maven.internal.impl.DefaultVersionParser;
|
||||
import org.apache.maven.internal.impl.model.DefaultInterpolator;
|
||||
import org.apache.maven.internal.impl.model.DefaultPathTranslator;
|
||||
import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
|
||||
import org.apache.maven.internal.impl.model.DefaultRootLocator;
|
||||
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||
import org.eclipse.aether.util.version.GenericVersionScheme;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ConditionProfileActivatorTest extends AbstractProfileActivatorTest<ConditionProfileActivator> {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
void setUp() throws Exception {
|
||||
activator = new ConditionProfileActivator(
|
||||
new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme())),
|
||||
new ProfileActivationFilePathInterpolator(
|
||||
new DefaultPathTranslator(), bd -> true, new DefaultInterpolator()),
|
||||
new DefaultRootLocator());
|
||||
|
||||
Path file = tempDir.resolve("file.txt");
|
||||
Files.createFile(file);
|
||||
|
||||
Path dir = tempDir.resolve("dir");
|
||||
Files.createDirectory(dir);
|
||||
Files.createFile(dir.resolve("test.xsd"));
|
||||
}
|
||||
|
||||
private Profile newProfile(String condition) {
|
||||
Activation a = Activation.newBuilder().condition(condition).build();
|
||||
Profile p = Profile.newBuilder().activation(a).build();
|
||||
return p;
|
||||
}
|
||||
|
||||
private Properties newJdkProperties(String javaVersion) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("java.version", javaVersion);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafe() throws Exception {
|
||||
Profile p = Profile.newInstance();
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
|
||||
p = p.withActivation(Activation.newInstance());
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkPrefix() throws Exception {
|
||||
Profile profile = newProfile("inrange(${java.version}, '[1.4,1.5)')");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.4")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.4.2")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.4.2_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.3")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkPrefixNegated() throws Exception {
|
||||
Profile profile = newProfile("not(inrange(${java.version}, '[1.4,1.5)'))");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.3")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkVersionRangeInclusiveBounds() throws Exception {
|
||||
Profile profile = newProfile("inrange(${java.version}, '[1.5,1.6.1]')");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.1")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkVersionRangeExclusiveBounds() throws Exception {
|
||||
Profile profile = newProfile("inrange(${java.version}, '[1.3.1,1.6)')");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.3")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.3.0")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.3.0_09")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.3.0_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.3.1")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.3.1_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.3.1_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.1")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.6")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkVersionRangeInclusiveLowerBound() throws Exception {
|
||||
Profile profile = newProfile("inrange(${java.version}, '[1.5,)')");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.1")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJdkVersionRangeExclusiveUpperBound() throws Exception {
|
||||
Profile profile = newProfile("inrange(${java.version}, '(,1.6)')");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newJdkProperties("1.5.1")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.6")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.6.0")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.6.0_09")));
|
||||
assertActivation(false, profile, newContext(null, newJdkProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
void testJdkRubbishJavaVersion() {
|
||||
Profile profile = newProfile("inrange(${java.version}, '[1.8,)')");
|
||||
|
||||
assertActivationWithProblems(profile, newContext(null, newJdkProperties("Pūteketeke")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newJdkProperties("rubbish")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newJdkProperties("1.a.0_09")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newJdkProperties("1.a.2.b")), "invalid JDK version");
|
||||
}
|
||||
|
||||
private void assertActivationWithProblems(
|
||||
Profile profile, ProfileActivationContext context, String warningContains) {
|
||||
SimpleProblemCollector problems = new SimpleProblemCollector();
|
||||
|
||||
assertFalse(activator.isActive(profile, context, problems));
|
||||
|
||||
assertEquals(0, problems.getErrors().size(), problems.getErrors().toString());
|
||||
assertEquals(1, problems.getWarnings().size(), problems.getWarnings().toString());
|
||||
assertTrue(
|
||||
problems.getWarnings().get(0).contains(warningContains),
|
||||
problems.getWarnings().toString());
|
||||
}
|
||||
|
||||
private Properties newOsProperties(String osName, String osVersion, String osArch) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("os.name", osName);
|
||||
props.setProperty("os.version", osVersion);
|
||||
props.setProperty("os.arch", osArch);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsVersionStringComparison() throws Exception {
|
||||
Profile profile = newProfile("inrange(${os.version}, '[6.5.0-1014-aws,6.6)')");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "3.1.0", "amd64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsVersionRegexMatching() throws Exception {
|
||||
Profile profile = newProfile("matches(${os.version}, '.*aws')");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "3.1.0", "amd64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsName() {
|
||||
Profile profile = newProfile("${os.name} == 'windows'");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsNegatedName() {
|
||||
Profile profile = newProfile("${os.name} != 'windows'");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsArch() {
|
||||
Profile profile = newProfile("${os.arch} == 'amd64'");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsNegatedArch() {
|
||||
Profile profile = newProfile("${os.arch} != 'amd64'");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
void testOsFamily() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().family("windows"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOsNegatedFamily() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().family("!windows"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@Test
|
||||
void testOsAllConditions() {
|
||||
Profile profile =
|
||||
newProfile("${os.name} == 'windows' && ${os.arch} != 'amd64' && inrange(${os.version}, '[99,)')");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "1", "aarch64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "99", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("windows", "99", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOsCapitalName() {
|
||||
Profile profile = newProfile("lower(${os.name}) == 'mac os x'");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "1", "aarch64")));
|
||||
assertActivation(false, profile, newContext(null, newOsProperties("windows", "99", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newOsProperties("Mac OS X", "14.5", "aarch64")));
|
||||
}
|
||||
|
||||
private Properties newPropProperties(String key, String value) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty(key, value);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNameOnly_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop}");
|
||||
|
||||
assertActivation(true, profile, newContext(newPropProperties("prop", "value"), null));
|
||||
assertActivation(false, profile, newContext(newPropProperties("prop", ""), null));
|
||||
assertActivation(false, profile, newContext(newPropProperties("other", "value"), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNameOnly_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop}");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("prop", "value")));
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("prop", "")));
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("other", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNegatedNameOnly_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("not(${prop})");
|
||||
|
||||
assertActivation(false, profile, newContext(newPropProperties("prop", "value"), null));
|
||||
assertActivation(true, profile, newContext(newPropProperties("prop", ""), null));
|
||||
assertActivation(true, profile, newContext(newPropProperties("other", "value"), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNegatedNameOnly_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("not(${prop})");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("prop", "value")));
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("prop", "")));
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("other", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithValue_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop} == 'value'");
|
||||
|
||||
assertActivation(true, profile, newContext(newPropProperties("prop", "value"), null));
|
||||
assertActivation(false, profile, newContext(newPropProperties("prop", "other"), null));
|
||||
assertActivation(false, profile, newContext(newPropProperties("prop", ""), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithValue_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop} == 'value'");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("prop", "value")));
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("prop", "other")));
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("other", "")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNegatedValue_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop} != 'value'");
|
||||
|
||||
assertActivation(false, profile, newContext(newPropProperties("prop", "value"), null));
|
||||
assertActivation(true, profile, newContext(newPropProperties("prop", "other"), null));
|
||||
assertActivation(true, profile, newContext(newPropProperties("prop", ""), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithNegatedValue_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop} != 'value'");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newPropProperties("prop", "value")));
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("prop", "other")));
|
||||
assertActivation(true, profile, newContext(null, newPropProperties("other", "")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPropWithValue_UserPropertyDominantOverSystemProperty() throws Exception {
|
||||
Profile profile = newProfile("${prop} == 'value'");
|
||||
|
||||
Properties props1 = newPropProperties("prop", "value");
|
||||
Properties props2 = newPropProperties("prop", "other");
|
||||
|
||||
assertActivation(true, profile, newContext(props1, props2));
|
||||
assertActivation(false, profile, newContext(props2, props1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testFileRootDirectoryWithNull() {
|
||||
IllegalStateException e = assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> assertActivation(false, newProfile("exists('${project.rootDirectory}')"), newFileContext(null)));
|
||||
assertEquals(RootLocator.UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE, e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileRootDirectory() {
|
||||
assertActivation(false, newProfile("exists('${project.rootDirectory}/someFile.txt')"), newFileContext());
|
||||
assertActivation(true, newProfile("missing('${project.rootDirectory}/someFile.txt')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.rootDirectory}')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.rootDirectory}/file.txt')"), newFileContext());
|
||||
assertActivation(false, newProfile("missing('${project.rootDirectory}')"), newFileContext());
|
||||
assertActivation(false, newProfile("missing('${project.rootDirectory}/file.txt')"), newFileContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testFileWilcards() {
|
||||
assertActivation(true, newProfile("exists('${project.rootDirectory}/**/*.xsd')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.basedir}/**/*.xsd')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.basedir}/**/*.xsd')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('**/*.xsd')"), newFileContext());
|
||||
assertActivation(true, newProfile("missing('**/*.xml')"), newFileContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileIsActiveNoFileWithShortBasedir() {
|
||||
assertActivation(false, newExistsProfile(null), newFileContext());
|
||||
assertActivation(false, newProfile("exists('someFile.txt')"), newFileContext());
|
||||
assertActivation(false, newProfile("exists('${basedir}/someFile.txt')"), newFileContext());
|
||||
|
||||
assertActivation(false, newMissingProfile(null), newFileContext());
|
||||
assertActivation(true, newProfile("missing('someFile.txt')"), newFileContext());
|
||||
assertActivation(true, newProfile("missing('${basedir}/someFile.txt')"), newFileContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileIsActiveNoFile() {
|
||||
assertActivation(false, newExistsProfile(null), newFileContext());
|
||||
assertActivation(false, newProfile("exists('someFile.txt')"), newFileContext());
|
||||
assertActivation(false, newProfile("exists('${project.basedir}/someFile.txt')"), newFileContext());
|
||||
|
||||
assertActivation(false, newMissingProfile(null), newFileContext());
|
||||
assertActivation(true, newProfile("missing('someFile.txt')"), newFileContext());
|
||||
assertActivation(true, newProfile("missing('${project.basedir}/someFile.txt')"), newFileContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileIsActiveExistsFileExists() {
|
||||
assertActivation(true, newProfile("exists('file.txt')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.basedir}')"), newFileContext());
|
||||
assertActivation(true, newProfile("exists('${project.basedir}/file.txt')"), newFileContext());
|
||||
|
||||
assertActivation(false, newProfile("missing('file.txt')"), newFileContext());
|
||||
assertActivation(false, newProfile("missing('${project.basedir}')"), newFileContext());
|
||||
assertActivation(false, newProfile("missing('${project.basedir}/file.txt')"), newFileContext());
|
||||
}
|
||||
|
||||
private Profile newExistsProfile(String filePath) {
|
||||
ActivationFile activationFile =
|
||||
ActivationFile.newBuilder().exists(filePath).build();
|
||||
return newProfile(activationFile);
|
||||
}
|
||||
|
||||
private Profile newMissingProfile(String filePath) {
|
||||
ActivationFile activationFile =
|
||||
ActivationFile.newBuilder().missing(filePath).build();
|
||||
return newProfile(activationFile);
|
||||
}
|
||||
|
||||
private Profile newProfile(ActivationFile activationFile) {
|
||||
Activation activation = Activation.newBuilder().file(activationFile).build();
|
||||
return Profile.newBuilder().activation(activation).build();
|
||||
}
|
||||
|
||||
protected ProfileActivationContext newFileContext(Path path) {
|
||||
DefaultProfileActivationContext context = new DefaultProfileActivationContext();
|
||||
context.setModel(Model.newBuilder().pomFile(path.resolve("pom.xml")).build());
|
||||
return context;
|
||||
}
|
||||
|
||||
protected ProfileActivationContext newFileContext() {
|
||||
return newFileContext(tempDir);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.ActivationFile;
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.apache.maven.api.services.model.RootLocator;
|
||||
import org.apache.maven.internal.impl.model.DefaultInterpolator;
|
||||
import org.apache.maven.internal.impl.model.DefaultPathTranslator;
|
||||
import org.apache.maven.internal.impl.model.DefaultProfileActivationContext;
|
||||
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
/**
|
||||
* Tests {@link FileProfileActivator}.
|
||||
*
|
||||
*/
|
||||
class FileProfileActivatorTest extends AbstractProfileActivatorTest<FileProfileActivator> {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private final DefaultProfileActivationContext context = new DefaultProfileActivationContext();
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
void setUp() throws Exception {
|
||||
activator = new FileProfileActivator(new ProfileActivationFilePathInterpolator(
|
||||
new DefaultPathTranslator(), bd -> true, new DefaultInterpolator()));
|
||||
|
||||
context.setModel(Model.newBuilder().pomFile(tempDir.resolve("pom.xml")).build());
|
||||
|
||||
File file = new File(tempDir.resolve("file.txt").toString());
|
||||
if (!file.createNewFile()) {
|
||||
throw new IOException("Can't create " + file);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRootDirectoryWithNull() {
|
||||
context.setModel(Model.newInstance());
|
||||
|
||||
IllegalStateException e = assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> assertActivation(false, newExistsProfile("${project.rootDirectory}"), context));
|
||||
assertEquals(RootLocator.UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE, e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRootDirectory() {
|
||||
assertActivation(false, newExistsProfile("${project.rootDirectory}/someFile.txt"), context);
|
||||
assertActivation(true, newMissingProfile("${project.rootDirectory}/someFile.txt"), context);
|
||||
assertActivation(true, newExistsProfile("${project.rootDirectory}"), context);
|
||||
assertActivation(true, newExistsProfile("${project.rootDirectory}/" + "file.txt"), context);
|
||||
assertActivation(false, newMissingProfile("${project.rootDirectory}"), context);
|
||||
assertActivation(false, newMissingProfile("${project.rootDirectory}/" + "file.txt"), context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsActiveNoFileWithShortBasedir() {
|
||||
assertActivation(false, newExistsProfile(null), context);
|
||||
assertActivation(false, newExistsProfile("someFile.txt"), context);
|
||||
assertActivation(false, newExistsProfile("${basedir}/someFile.txt"), context);
|
||||
|
||||
assertActivation(false, newMissingProfile(null), context);
|
||||
assertActivation(true, newMissingProfile("someFile.txt"), context);
|
||||
assertActivation(true, newMissingProfile("${basedir}/someFile.txt"), context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsActiveNoFile() {
|
||||
assertActivation(false, newExistsProfile(null), context);
|
||||
assertActivation(false, newExistsProfile("someFile.txt"), context);
|
||||
assertActivation(false, newExistsProfile("${project.basedir}/someFile.txt"), context);
|
||||
|
||||
assertActivation(false, newMissingProfile(null), context);
|
||||
assertActivation(true, newMissingProfile("someFile.txt"), context);
|
||||
assertActivation(true, newMissingProfile("${project.basedir}/someFile.txt"), context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsActiveExistsFileExists() {
|
||||
assertActivation(true, newExistsProfile("file.txt"), context);
|
||||
assertActivation(true, newExistsProfile("${project.basedir}"), context);
|
||||
assertActivation(true, newExistsProfile("${project.basedir}/" + "file.txt"), context);
|
||||
|
||||
assertActivation(false, newMissingProfile("file.txt"), context);
|
||||
assertActivation(false, newMissingProfile("${project.basedir}"), context);
|
||||
assertActivation(false, newMissingProfile("${project.basedir}/" + "file.txt"), context);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsActiveExistsLeavesFileUnchanged() {
|
||||
Profile profile = newExistsProfile("file.txt");
|
||||
assertEquals("file.txt", profile.getActivation().getFile().getExists());
|
||||
|
||||
assertActivation(true, profile, context);
|
||||
|
||||
assertEquals("file.txt", profile.getActivation().getFile().getExists());
|
||||
}
|
||||
|
||||
private Profile newExistsProfile(String filePath) {
|
||||
ActivationFile activationFile =
|
||||
ActivationFile.newBuilder().exists(filePath).build();
|
||||
return newProfile(activationFile);
|
||||
}
|
||||
|
||||
private Profile newMissingProfile(String filePath) {
|
||||
ActivationFile activationFile =
|
||||
ActivationFile.newBuilder().missing(filePath).build();
|
||||
return newProfile(activationFile);
|
||||
}
|
||||
|
||||
private Profile newProfile(ActivationFile activationFile) {
|
||||
Activation activation = Activation.newBuilder().file(activationFile).build();
|
||||
return Profile.newBuilder().activation(activation).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.apache.maven.api.services.model.ProfileActivationContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests {@link JdkVersionProfileActivator}.
|
||||
*
|
||||
*/
|
||||
class JdkVersionProfileActivatorTest extends AbstractProfileActivatorTest<JdkVersionProfileActivator> {
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
activator = new JdkVersionProfileActivator();
|
||||
}
|
||||
|
||||
private Profile newProfile(String jdkVersion) {
|
||||
Activation a = Activation.newBuilder().jdk(jdkVersion).build();
|
||||
|
||||
Profile p = Profile.newBuilder().activation(a).build();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private Properties newProperties(String javaVersion) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("java.version", javaVersion);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafe() throws Exception {
|
||||
Profile p = Profile.newInstance();
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
|
||||
p = p.withActivation(Activation.newInstance());
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrefix() throws Exception {
|
||||
Profile profile = newProfile("1.4");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.4")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.4.2")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.4.2_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.3")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrefixNegated() throws Exception {
|
||||
Profile profile = newProfile("!1.4");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.3")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionRangeInclusiveBounds() throws Exception {
|
||||
Profile profile = newProfile("[1.5,1.6]");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.1")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionRangeExclusiveBounds() throws Exception {
|
||||
Profile profile = newProfile("(1.3,1.6)");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.3")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.3.0")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.3.0_09")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.3.0_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.3.1")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.3.1_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.3.1_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.1")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.6")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionRangeInclusiveLowerBound() throws Exception {
|
||||
Profile profile = newProfile("[1.5,)");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.4.2_09-b03")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.1")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionRangeExclusiveUpperBound() throws Exception {
|
||||
Profile profile = newProfile("(,1.6)");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.0_09-b03")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("1.5.1")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.6")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.6.0")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.6.0_09")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("1.6.0_09-b03")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRubbishJavaVersion() {
|
||||
Profile profile = newProfile("[1.8,)");
|
||||
|
||||
assertActivationWithProblems(profile, newContext(null, newProperties("Pūteketeke")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newProperties("rubbish")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newProperties("1.a.0_09")), "invalid JDK version");
|
||||
assertActivationWithProblems(profile, newContext(null, newProperties("1.a.2.b")), "invalid JDK version");
|
||||
}
|
||||
|
||||
private void assertActivationWithProblems(
|
||||
Profile profile, ProfileActivationContext context, String warningContains) {
|
||||
SimpleProblemCollector problems = new SimpleProblemCollector();
|
||||
|
||||
assertFalse(activator.isActive(profile, context, problems));
|
||||
|
||||
assertEquals(0, problems.getErrors().size());
|
||||
assertEquals(1, problems.getWarnings().size());
|
||||
assertTrue(problems.getWarnings().get(0).contains(warningContains));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.ActivationOS;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link OperatingSystemProfileActivator}.
|
||||
*
|
||||
*/
|
||||
class OperatingSystemProfileActivatorTest extends AbstractProfileActivatorTest<OperatingSystemProfileActivator> {
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
activator = new OperatingSystemProfileActivator();
|
||||
}
|
||||
|
||||
private Profile newProfile(ActivationOS.Builder activationBuilder) {
|
||||
Activation a = Activation.newBuilder().os(activationBuilder.build()).build();
|
||||
|
||||
Profile p = Profile.newBuilder().activation(a).build();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private Properties newProperties(String osName, String osVersion, String osArch) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("os.name", osName);
|
||||
props.setProperty("os.version", osVersion);
|
||||
props.setProperty("os.arch", osArch);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionStringComparison() throws Exception {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().version("6.5.0-1014-aws"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionRegexMatching() throws Exception {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().version("regex:.*aws"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testName() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().name("windows"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegatedName() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().name("!windows"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testArch() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().arch("amd64"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegatedArch() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().arch("!amd64"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFamily() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().family("windows"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNegatedFamily() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder().family("!windows"));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAllOsConditions() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder()
|
||||
.family("windows")
|
||||
.name("windows")
|
||||
.arch("aarch64")
|
||||
.version("99"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "1", "aarch64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "99", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("windows", "99", "aarch64")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCapitalOsName() {
|
||||
Profile profile = newProfile(ActivationOS.newBuilder()
|
||||
.family("Mac")
|
||||
.name("Mac OS X")
|
||||
.arch("aarch64")
|
||||
.version("14.5"));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "1", "aarch64")));
|
||||
assertActivation(false, profile, newContext(null, newProperties("windows", "99", "amd64")));
|
||||
assertActivation(true, profile, newContext(null, newProperties("Mac OS X", "14.5", "aarch64")));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.maven.api.model.Activation;
|
||||
import org.apache.maven.api.model.ActivationProperty;
|
||||
import org.apache.maven.api.model.Profile;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests {@link PropertyProfileActivator}.
|
||||
*
|
||||
*/
|
||||
class PropertyProfileActivatorTest extends AbstractProfileActivatorTest<PropertyProfileActivator> {
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
void setUp() throws Exception {
|
||||
activator = new PropertyProfileActivator();
|
||||
}
|
||||
|
||||
private Profile newProfile(String key, String value) {
|
||||
ActivationProperty ap =
|
||||
ActivationProperty.newBuilder().name(key).value(value).build();
|
||||
|
||||
Activation a = Activation.newBuilder().property(ap).build();
|
||||
|
||||
Profile p = Profile.newBuilder().activation(a).build();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private Properties newProperties(String key, String value) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty(key, value);
|
||||
return props;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullSafe() throws Exception {
|
||||
Profile p = Profile.newInstance();
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
|
||||
p = p.withActivation(Activation.newInstance());
|
||||
|
||||
assertActivation(false, p, newContext(null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNameOnly_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", null);
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("prop", "value"), null));
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("prop", ""), null));
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("other", "value"), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNameOnly_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", null);
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("prop", "value")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("prop", "")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("other", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNegatedNameOnly_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("!prop", null);
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("prop", "value"), null));
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("prop", ""), null));
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("other", "value"), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNegatedNameOnly_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("!prop", null);
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("prop", "value")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("prop", "")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("other", "value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithValue_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", "value");
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("prop", "value"), null));
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("prop", "other"), null));
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("prop", ""), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithValue_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", "value");
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("prop", "value")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("prop", "other")));
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("other", "")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNegatedValue_UserProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", "!value");
|
||||
|
||||
assertActivation(false, profile, newContext(newProperties("prop", "value"), null));
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("prop", "other"), null));
|
||||
|
||||
assertActivation(true, profile, newContext(newProperties("prop", ""), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithNegatedValue_SystemProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", "!value");
|
||||
|
||||
assertActivation(false, profile, newContext(null, newProperties("prop", "value")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("prop", "other")));
|
||||
|
||||
assertActivation(true, profile, newContext(null, newProperties("other", "")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWithValue_UserPropertyDominantOverSystemProperty() throws Exception {
|
||||
Profile profile = newProfile("prop", "value");
|
||||
|
||||
Properties props1 = newProperties("prop", "value");
|
||||
Properties props2 = newProperties("prop", "other");
|
||||
|
||||
assertActivation(true, profile, newContext(props1, props2));
|
||||
|
||||
assertActivation(false, profile, newContext(props2, props1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.model.profile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.api.model.InputLocation;
|
||||
import org.apache.maven.api.model.Model;
|
||||
import org.apache.maven.api.services.BuilderProblem;
|
||||
import org.apache.maven.api.services.ModelBuilderException;
|
||||
import org.apache.maven.api.services.ModelProblem;
|
||||
import org.apache.maven.api.services.ModelProblemCollector;
|
||||
import org.apache.maven.internal.impl.model.DefaultModelProblem;
|
||||
|
||||
/**
|
||||
* A simple model problem collector for testing the model building components.
|
||||
*/
|
||||
public class SimpleProblemCollector implements ModelProblemCollector {
|
||||
|
||||
final List<ModelProblem> problems = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public List<ModelProblem> getProblems() {
|
||||
return problems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasErrors() {
|
||||
return problems.stream()
|
||||
.anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL
|
||||
|| p.getSeverity() == ModelProblem.Severity.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFatalErrors() {
|
||||
return problems.stream().anyMatch(p -> p.getSeverity() == ModelProblem.Severity.FATAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(
|
||||
BuilderProblem.Severity severity,
|
||||
ModelProblem.Version version,
|
||||
String message,
|
||||
InputLocation location,
|
||||
Exception exception) {
|
||||
add(new DefaultModelProblem(
|
||||
message,
|
||||
severity,
|
||||
version,
|
||||
null,
|
||||
location != null ? location.getLineNumber() : -1,
|
||||
location != null ? location.getColumnNumber() : -1,
|
||||
exception));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ModelProblem problem) {
|
||||
this.problems.add(problem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelBuilderException newModelBuilderException() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSource(String location) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSource(Model model) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootModel(Model model) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model getRootModel() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return problems.stream()
|
||||
.filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
|
||||
.map(p -> p.getMessage())
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<String> getWarnings() {
|
||||
return problems.stream()
|
||||
.filter(p -> p.getSeverity() == ModelProblem.Severity.WARNING)
|
||||
.map(p -> p.getMessage())
|
||||
.toList();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue