From fe7b78d4afa7e4182b8a03dc9196540277c574f3 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Tue, 23 Apr 2024 22:49:24 +0200 Subject: [PATCH] Port to the new v4 builder --- .../impl/model/DefaultModelBuilder.java | 107 ++++--- .../impl/model/DefaultModelValidator.java | 263 ++++++++++++++++-- .../model/building/DefaultModelBuilder.java | 18 +- .../validation/DefaultModelValidator.java | 87 +++--- 4 files changed, 364 insertions(+), 111 deletions(-) diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java index e10a05887fcc..d9cb34eac8d4 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java @@ -35,7 +35,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Supplier; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.maven.api.VersionRange; import org.apache.maven.api.annotations.Nullable; @@ -50,6 +52,7 @@ import org.apache.maven.api.model.DependencyManagement; import org.apache.maven.api.model.Exclusion; import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.InputLocationTracker; import org.apache.maven.api.model.InputSource; import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Parent; @@ -80,8 +83,11 @@ import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.impl.resolver.DefaultModelCache; import org.apache.maven.internal.impl.resolver.DefaultModelResolver; +import org.apache.maven.model.v4.MavenTransformer; import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.Interpolator; import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; import org.codehaus.plexus.interpolation.StringSearchInterpolator; import org.eclipse.aether.impl.RemoteRepositoryManager; @@ -377,54 +383,75 @@ private Model readEffectiveModel( private List interpolateActivations( List profiles, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) { - List newProfiles = null; - for (int index = 0; index < profiles.size(); index++) { - Profile profile = profiles.get(index); - Activation activation = profile.getActivation(); - if (activation != null) { - ActivationFile file = activation.getFile(); - if (file != null) { - String oldExists = file.getExists(); - if (isNotEmpty(oldExists)) { + if (profiles.stream() + .map(org.apache.maven.api.model.Profile::getActivation) + .noneMatch(Objects::nonNull)) { + return profiles; + } + final Interpolator xform = new RegexBasedInterpolator(); + xform.setCacheAnswers(true); + Stream.of(context.getUserProperties(), context.getSystemProperties()) + .map(MapBasedValueSource::new) + .forEach(xform::addValueSource); + + class ProfileInterpolator extends MavenTransformer implements UnaryOperator { + ProfileInterpolator() { + super(s -> { + if (isNotEmpty(s)) { try { - String newExists = interpolate(oldExists, context); - if (!Objects.equals(oldExists, newExists)) { - if (newProfiles == null) { - newProfiles = new ArrayList<>(profiles); - } - newProfiles.set( - index, profile.withActivation(activation.withFile(file.withExists(newExists)))); - } + return xform.interpolate(s); } catch (InterpolationException e) { - addInterpolationProblem(problems, file, oldExists, e, "exists"); - } - } else { - String oldMissing = file.getMissing(); - if (isNotEmpty(oldMissing)) { - try { - String newMissing = interpolate(oldMissing, context); - if (!Objects.equals(oldMissing, newMissing)) { - if (newProfiles == null) { - newProfiles = new ArrayList<>(profiles); - } - newProfiles.set( - index, - profile.withActivation(activation.withFile(file.withMissing(newMissing)))); - } - } catch (InterpolationException e) { - addInterpolationProblem(problems, file, oldMissing, e, "missing"); - } + problems.add(Severity.ERROR, ModelProblem.Version.BASE, e.getMessage(), e); } } + return s; + }); + } + + @Override + public org.apache.maven.api.model.Profile apply(org.apache.maven.api.model.Profile p) { + return org.apache.maven.api.model.Profile.newBuilder(p) + .activation(transformActivation(p.getActivation())) + .build(); + } + + @Override + protected ActivationFile.Builder transformActivationFile_Missing( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + String path = target.getMissing(); + String xformed = transformPath(path, target, "missing"); + return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder; + } + + @Override + protected ActivationFile.Builder transformActivationFile_Exists( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + final String path = target.getExists(); + final String xformed = transformPath(path, target, "exists"); + return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder; + } + + private String transformPath(String path, ActivationFile target, String locationKey) { + if (isNotEmpty(path)) { + try { + return profileActivationFilePathInterpolator.interpolate(path, context); + } catch (InterpolationException e) { + addInterpolationProblem(problems, target, path, e, locationKey); + } } + return path; } } - return newProfiles != null ? newProfiles : profiles; + return profiles.stream().map(new ProfileInterpolator()).toList(); } private static void addInterpolationProblem( DefaultModelProblemCollector problems, - ActivationFile file, + InputLocationTracker target, String path, InterpolationException e, String locationKey) { @@ -432,14 +459,10 @@ private static void addInterpolationProblem( Severity.ERROR, ModelProblem.Version.BASE, "Failed to interpolate file location " + path + ": " + e.getMessage(), - file.getLocation(locationKey), + target.getLocation(locationKey), e); } - private String interpolate(String path, ProfileActivationContext context) throws InterpolationException { - return isNotEmpty(path) ? profileActivationFilePathInterpolator.interpolate(path, context) : path; - } - private static boolean isNotEmpty(String string) { return string != null && !string.isEmpty(); } diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java index 120a34e41961..ce57f6443361 100644 --- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java +++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java @@ -21,20 +21,31 @@ import java.io.File; import java.util.Arrays; import java.util.Collections; +import java.util.Deque; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; 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.ActivationFile; +import org.apache.maven.api.model.ActivationOS; +import org.apache.maven.api.model.ActivationProperty; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.BuildBase; import org.apache.maven.api.model.Dependency; @@ -59,6 +70,7 @@ import org.apache.maven.api.services.ModelProblemCollector; import org.apache.maven.api.services.model.*; import org.apache.maven.model.v4.MavenModelVersion; +import org.apache.maven.model.v4.MavenTransformer; /** */ @@ -80,6 +92,196 @@ public class DefaultModelValidator implements ModelValidator { private static final String EMPTY = ""; + private record ActivationFrame(String location, Optional parent) {} + + private static class ActivationWalker extends MavenTransformer { + + private final Deque stk; + + ActivationWalker(Deque stk, UnaryOperator transformer) { + super(transformer); + this.stk = stk; + } + + private ActivationFrame nextFrame(String property) { + return new ActivationFrame(property, Optional.empty()); + } + + private

ActivationFrame nextFrame(String property, Function child) { + @SuppressWarnings("unchecked") + final Optional

parent = (Optional

) stk.peek().parent; + return new ActivationFrame(property, parent.map(child)); + } + + @Override + public Activation transformActivation(Activation target) { + stk.push(new ActivationFrame("activation", Optional.of(target))); + try { + return super.transformActivation(target); + } finally { + stk.pop(); + } + } + + @Override + protected Activation.Builder transformActivation_ActiveByDefault( + Supplier creator, Activation.Builder builder, Activation target) { + return builder; + } + + @Override + protected Activation.Builder transformActivation_File( + Supplier creator, Activation.Builder builder, Activation target) { + stk.push(nextFrame("file", Activation::getFile)); + Optional.ofNullable(target.getFile()); + try { + return super.transformActivation_File(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationFile.Builder transformActivationFile_Exists( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + stk.push(nextFrame("exists")); + try { + return super.transformActivationFile_Exists(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationFile.Builder transformActivationFile_Missing( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { + stk.push(nextFrame("missing")); + try { + return super.transformActivationFile_Missing(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected Activation.Builder transformActivation_Jdk( + Supplier creator, Activation.Builder builder, Activation target) { + stk.push(nextFrame("jdk")); + try { + return super.transformActivation_Jdk(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected Activation.Builder transformActivation_Os( + Supplier creator, Activation.Builder builder, Activation target) { + stk.push(nextFrame("os", Activation::getOs)); + try { + return super.transformActivation_Os(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationOS.Builder transformActivationOS_Arch( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { + stk.push(nextFrame("arch")); + try { + return super.transformActivationOS_Arch(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationOS.Builder transformActivationOS_Family( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { + stk.push(nextFrame("family")); + try { + return super.transformActivationOS_Family(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationOS.Builder transformActivationOS_Name( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { + stk.push(nextFrame("name")); + try { + return super.transformActivationOS_Name(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationOS.Builder transformActivationOS_Version( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { + stk.push(nextFrame("version")); + try { + return super.transformActivationOS_Version(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected Activation.Builder transformActivation_Packaging( + Supplier creator, Activation.Builder builder, Activation target) { + stk.push(nextFrame("packaging")); + try { + return super.transformActivation_Packaging(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected Activation.Builder transformActivation_Property( + Supplier creator, Activation.Builder builder, Activation target) { + stk.push(nextFrame("property", Activation::getProperty)); + try { + return super.transformActivation_Property(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationProperty.Builder transformActivationProperty_Name( + Supplier creator, + ActivationProperty.Builder builder, + ActivationProperty target) { + stk.push(nextFrame("name")); + try { + return super.transformActivationProperty_Name(creator, builder, target); + } finally { + stk.pop(); + } + } + + @Override + protected ActivationProperty.Builder transformActivationProperty_Value( + Supplier creator, + ActivationProperty.Builder builder, + ActivationProperty target) { + stk.push(nextFrame("value")); + try { + return super.transformActivationProperty_Value(creator, builder, target); + } finally { + stk.pop(); + } + } + } + private final Set validCoordinateIds = new HashSet<>(); private final Set validProfileIds = new HashSet<>(); @@ -282,42 +484,53 @@ public void validateRawModel(Model m, ModelBuilderRequest request, ModelProblemC } private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) { - if (activation == null || activation.getFile() == null) { + if (activation == null) { return; } - ActivationFile file = activation.getFile(); - - String path; - String location; - - if (file.getExists() != null && !file.getExists().isEmpty()) { - path = file.getExists(); - location = "exists"; - } else if (file.getMissing() != null && !file.getMissing().isEmpty()) { - path = file.getMissing(); - location = "missing"; - } else { - return; - } - - if (hasProjectExpression(path)) { - Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(path); - while (matcher.find()) { - String propertyName = matcher.group(0); - if (!"${project.basedir}".equals(propertyName)) { + final Deque stk = new LinkedList<>(); + + final Supplier pathSupplier = () -> { + final boolean parallel = false; + return StreamSupport.stream(((Iterable) stk::descendingIterator).spliterator(), parallel) + .map(ActivationFrame::location) + .collect(Collectors.joining(".")); + }; + final Supplier locationSupplier = () -> { + if (stk.size() < 2) { + return null; + } + Iterator f = stk.iterator(); + + String location = f.next().location; + ActivationFrame parent = f.next(); + + return parent.parent.map(p -> p.getLocation(location)).orElse(null); + }; + final UnaryOperator transformer = s -> { + if (hasProjectExpression(s)) { + String path = pathSupplier.get(); + Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s); + while (matcher.find()) { + String propertyName = matcher.group(0); + + if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) { + continue; + } addViolation( problems, Severity.WARNING, Version.V30, - prefix + "activation.file." + location, + prefix + path, null, - "Failed to interpolate file location " + path + ": " + propertyName + "Failed to interpolate profile activation property " + s + ": " + propertyName + " expressions are not supported during profile activation.", - file.getLocation(location)); + locationSupplier.get()); } } - } + return s; + }; + new ActivationWalker(stk, transformer).transformActivation(activation); } private void validate20RawPlugins( diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java index 057fb4a31065..69ec67a2f400 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java @@ -940,21 +940,23 @@ public org.apache.maven.api.model.Profile apply(org.apache.maven.api.model.Profi } @Override - protected void transformActivationFile_Missing(ActivationFile.Builder builder, ActivationFile target) { + protected ActivationFile.Builder transformActivationFile_Missing( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { final String path = target.getMissing(); final String xformed = transformPath(path, target, "missing"); - if (xformed != path) { - builder.missing(xformed); - } + return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder; } @Override - protected void transformActivationFile_Exists(ActivationFile.Builder builder, ActivationFile target) { + protected ActivationFile.Builder transformActivationFile_Exists( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { final String path = target.getExists(); final String xformed = transformPath(path, target, "exists"); - if (xformed != path) { - builder.exists(xformed); - } + return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder; } private String transformPath(String path, ActivationFile target, String locationKey) { diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java index 39c4ecbb1707..246e2f8b1314 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java @@ -44,7 +44,6 @@ import java.util.stream.StreamSupport; import org.apache.maven.api.model.Activation; -import org.apache.maven.api.model.Activation.Builder; import org.apache.maven.api.model.ActivationFile; import org.apache.maven.api.model.ActivationOS; import org.apache.maven.api.model.ActivationProperty; @@ -127,142 +126,158 @@ public Activation transformActivation(Activation target) { } @Override - protected void transformActivation_ActiveByDefault(Builder builder, Activation target) {} + protected Activation.Builder transformActivation_ActiveByDefault( + Supplier creator, Activation.Builder builder, Activation target) { + return builder; + } @Override - protected void transformActivation_File(Builder builder, Activation target) { + protected Activation.Builder transformActivation_File( + Supplier creator, Activation.Builder builder, Activation target) { stk.push(nextFrame("file", Activation::getFile)); Optional.ofNullable(target.getFile()); try { - super.transformActivation_File(builder, target); + return super.transformActivation_File(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationFile_Exists( - org.apache.maven.api.model.ActivationFile.Builder builder, ActivationFile target) { + protected ActivationFile.Builder transformActivationFile_Exists( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { stk.push(nextFrame("exists")); try { - super.transformActivationFile_Exists(builder, target); + return super.transformActivationFile_Exists(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationFile_Missing( - org.apache.maven.api.model.ActivationFile.Builder builder, ActivationFile target) { + protected ActivationFile.Builder transformActivationFile_Missing( + Supplier creator, + ActivationFile.Builder builder, + ActivationFile target) { stk.push(nextFrame("missing")); try { - super.transformActivationFile_Missing(builder, target); + return super.transformActivationFile_Missing(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivation_Jdk(Builder builder, Activation target) { + protected Activation.Builder transformActivation_Jdk( + Supplier creator, Activation.Builder builder, Activation target) { stk.push(nextFrame("jdk")); try { - super.transformActivation_Jdk(builder, target); + return super.transformActivation_Jdk(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivation_Os(Builder builder, Activation target) { + protected Activation.Builder transformActivation_Os( + Supplier creator, Activation.Builder builder, Activation target) { stk.push(nextFrame("os", Activation::getOs)); try { - super.transformActivation_Os(builder, target); + return super.transformActivation_Os(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationOS_Arch( - org.apache.maven.api.model.ActivationOS.Builder builder, ActivationOS target) { + protected ActivationOS.Builder transformActivationOS_Arch( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { stk.push(nextFrame("arch")); try { - super.transformActivationOS_Arch(builder, target); + return super.transformActivationOS_Arch(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationOS_Family( - org.apache.maven.api.model.ActivationOS.Builder builder, ActivationOS target) { + protected ActivationOS.Builder transformActivationOS_Family( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { stk.push(nextFrame("family")); try { - super.transformActivationOS_Family(builder, target); + return super.transformActivationOS_Family(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationOS_Name( - org.apache.maven.api.model.ActivationOS.Builder builder, ActivationOS target) { + protected ActivationOS.Builder transformActivationOS_Name( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { stk.push(nextFrame("name")); try { - super.transformActivationOS_Name(builder, target); + return super.transformActivationOS_Name(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationOS_Version( - org.apache.maven.api.model.ActivationOS.Builder builder, ActivationOS target) { + protected ActivationOS.Builder transformActivationOS_Version( + Supplier creator, ActivationOS.Builder builder, ActivationOS target) { stk.push(nextFrame("version")); try { - super.transformActivationOS_Version(builder, target); + return super.transformActivationOS_Version(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivation_Packaging(Builder builder, Activation target) { + protected Activation.Builder transformActivation_Packaging( + Supplier creator, Activation.Builder builder, Activation target) { stk.push(nextFrame("packaging")); try { - super.transformActivation_Packaging(builder, target); + return super.transformActivation_Packaging(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivation_Property(Builder builder, Activation target) { + protected Activation.Builder transformActivation_Property( + Supplier creator, Activation.Builder builder, Activation target) { stk.push(nextFrame("property", Activation::getProperty)); try { - super.transformActivation_Property(builder, target); + return super.transformActivation_Property(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationProperty_Name( - org.apache.maven.api.model.ActivationProperty.Builder builder, ActivationProperty target) { + protected ActivationProperty.Builder transformActivationProperty_Name( + Supplier creator, + ActivationProperty.Builder builder, + ActivationProperty target) { stk.push(nextFrame("name")); try { - super.transformActivationProperty_Name(builder, target); + return super.transformActivationProperty_Name(creator, builder, target); } finally { stk.pop(); } } @Override - protected void transformActivationProperty_Value( - org.apache.maven.api.model.ActivationProperty.Builder builder, ActivationProperty target) { + protected ActivationProperty.Builder transformActivationProperty_Value( + Supplier creator, + ActivationProperty.Builder builder, + ActivationProperty target) { stk.push(nextFrame("value")); try { - super.transformActivationProperty_Value(builder, target); + return super.transformActivationProperty_Value(creator, builder, target); } finally { stk.pop(); }