All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.project.interpolation.AbstractStringBasedModelInterpolator Maven / Gradle / Ivy

There is a newer version: 4.0.0-rc-2
Show newest version
/*
 * 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.project.interpolation;

import javax.inject.Inject;
import javax.xml.stream.XMLStreamException;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.maven.model.Model;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.model.v4.MavenStaxWriter;
import org.apache.maven.project.DefaultProjectBuilderConfiguration;
import org.apache.maven.project.ProjectBuilderConfiguration;
import org.apache.maven.project.path.PathTranslator;
import org.codehaus.plexus.interpolation.AbstractValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
import org.codehaus.plexus.interpolation.RecursionInterceptor;
import org.codehaus.plexus.interpolation.ValueSource;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;

/**
 * Use a regular expression search to find and resolve expressions within the POM.
 *
 * TODO Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans.
 */
@Deprecated
public abstract class AbstractStringBasedModelInterpolator extends AbstractLogEnabled
        implements ModelInterpolator, Initializable {

    private static final List PROJECT_PREFIXES = Arrays.asList("pom.", "project.");

    private static final List TRANSLATED_PATH_EXPRESSIONS;

    static {
        List translatedPrefixes = new ArrayList<>();

        // MNG-1927, MNG-2124, MNG-3355:
        // If the build section is present and the project directory is non-null, we should make
        // sure interpolation of the directories below uses translated paths.
        // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
        // code below...
        translatedPrefixes.add("build.directory");
        translatedPrefixes.add("build.outputDirectory");
        translatedPrefixes.add("build.testOutputDirectory");
        translatedPrefixes.add("build.sourceDirectory");
        translatedPrefixes.add("build.testSourceDirectory");
        translatedPrefixes.add("build.scriptSourceDirectory");
        translatedPrefixes.add("reporting.outputDirectory");

        TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
    }

    @Inject
    private PathTranslator pathTranslator;

    private Interpolator interpolator;

    private RecursionInterceptor recursionInterceptor;

    // for testing.
    protected AbstractStringBasedModelInterpolator(PathTranslator pathTranslator) {
        this.pathTranslator = pathTranslator;
    }

    protected AbstractStringBasedModelInterpolator() {}

    public Model interpolate(Model model, Map context) throws ModelInterpolationException {
        return interpolate(model, context, true);
    }

    /**
     * Serialize the inbound Model instance to a StringWriter, perform the regex replacement to resolve
     * POM expressions, then re-parse into the resolved Model instance.
     * 

* NOTE: This will result in a different instance of Model being returned!!! * * @param model The inbound Model instance, to serialize and reference for expression resolution * @param context The other context map to be used during resolution * * @return The resolved instance of the inbound Model. This is a different instance! * * @deprecated Use {@link ModelInterpolator#interpolate(Model, File, ProjectBuilderConfiguration, boolean)} instead. */ @Deprecated public Model interpolate(Model model, Map context, boolean strict) throws ModelInterpolationException { Properties props = new Properties(); props.putAll(context); return interpolate(model, null, new DefaultProjectBuilderConfiguration().setExecutionProperties(props), true); } public Model interpolate(Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled) throws ModelInterpolationException { StringWriter sWriter = new StringWriter(1024); MavenStaxWriter writer = new MavenStaxWriter(); try { writer.write(sWriter, model.getDelegate()); } catch (IOException | XMLStreamException e) { throw new ModelInterpolationException("Cannot serialize project model for interpolation.", e); } String serializedModel = sWriter.toString(); serializedModel = interpolate(serializedModel, model, projectDir, config, debugEnabled); StringReader sReader = new StringReader(serializedModel); MavenStaxReader modelReader = new MavenStaxReader(); try { model = new Model(modelReader.read(sReader)); } catch (XMLStreamException e) { throw new ModelInterpolationException( "Cannot read project model from interpolating filter of serialized version.", e); } return model; } /** * Interpolates all expressions in the src parameter. *

* The algorithm used for each expression is: *

    *
  • If it starts with either "pom." or "project.", the expression is evaluated against the model.
  • *
  • If the value is null, get the value from the context.
  • *
  • If the value is null, but the context contains the expression, don't replace the expression string * with the value, and continue to find other expressions.
  • *
  • If the value is null, get it from the model properties.
  • *
*/ public String interpolate( String src, Model model, final File projectDir, ProjectBuilderConfiguration config, boolean debug) throws ModelInterpolationException { try { List valueSources = createValueSources(model, projectDir, config); List postProcessors = createPostProcessors(model, projectDir, config); return interpolateInternal(src, valueSources, postProcessors, debug); } finally { interpolator.clearAnswers(); } } protected List createValueSources( final Model model, final File projectDir, final ProjectBuilderConfiguration config) { String timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT; Properties modelProperties = model.getProperties(); if (modelProperties != null) { timestampFormat = modelProperties.getProperty(BUILD_TIMESTAMP_FORMAT_PROPERTY, timestampFormat); } ValueSource modelValueSource1 = new PrefixedObjectValueSource(PROJECT_PREFIXES, model, false); ValueSource modelValueSource2 = new ObjectBasedValueSource(model); ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource(false) { public Object getValue(String expression) { if (projectDir != null && "basedir".equals(expression)) { return projectDir.getAbsolutePath(); } return null; } }, PROJECT_PREFIXES, true); ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource(false) { public Object getValue(String expression) { if (projectDir != null && "baseUri".equals(expression)) { return projectDir.getAbsoluteFile().toPath().toUri().toASCIIString(); } return null; } }, PROJECT_PREFIXES, false); List valueSources = new ArrayList<>(9); // NOTE: Order counts here! valueSources.add(basedirValueSource); valueSources.add(baseUriValueSource); valueSources.add(new BuildTimestampValueSource(config.getBuildStartTime(), timestampFormat)); valueSources.add(modelValueSource1); valueSources.add(new MapBasedValueSource(config.getUserProperties())); valueSources.add(new MapBasedValueSource(modelProperties)); valueSources.add(new MapBasedValueSource(config.getExecutionProperties())); valueSources.add(new AbstractValueSource(false) { public Object getValue(String expression) { return config.getExecutionProperties().getProperty("env." + expression); } }); valueSources.add(modelValueSource2); return valueSources; } protected List createPostProcessors( final Model model, final File projectDir, final ProjectBuilderConfiguration config) { return Collections.singletonList((InterpolationPostProcessor) new PathTranslatingPostProcessor( PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS, projectDir, pathTranslator)); } @SuppressWarnings("unchecked") protected String interpolateInternal( String src, List valueSources, List postProcessors, boolean debug) throws ModelInterpolationException { if (!src.contains("${")) { return src; } Logger logger = getLogger(); String result = src; synchronized (this) { for (ValueSource vs : valueSources) { interpolator.addValueSource(vs); } for (InterpolationPostProcessor postProcessor : postProcessors) { interpolator.addPostProcessor(postProcessor); } try { try { result = interpolator.interpolate(result, recursionInterceptor); } catch (InterpolationException e) { throw new ModelInterpolationException(e.getMessage(), e); } if (debug) { List feedback = interpolator.getFeedback(); if (feedback != null && !feedback.isEmpty()) { logger.debug("Maven encountered the following problems during initial POM interpolation:"); Object last = null; for (Object next : feedback) { if (next instanceof Throwable) { if (last == null) { logger.debug("", ((Throwable) next)); } else { logger.debug(String.valueOf(last), ((Throwable) next)); } } else { if (last != null) { logger.debug(String.valueOf(last)); } last = next; } } if (last != null) { logger.debug(String.valueOf(last)); } } } interpolator.clearFeedback(); } finally { for (ValueSource vs : valueSources) { interpolator.removeValuesSource(vs); } for (InterpolationPostProcessor postProcessor : postProcessors) { interpolator.removePostProcessor(postProcessor); } } } return result; } protected RecursionInterceptor getRecursionInterceptor() { return recursionInterceptor; } protected void setRecursionInterceptor(RecursionInterceptor recursionInterceptor) { this.recursionInterceptor = recursionInterceptor; } protected abstract Interpolator createInterpolator(); public void initialize() throws InitializationException { interpolator = createInterpolator(); recursionInterceptor = new PrefixAwareRecursionInterceptor(PROJECT_PREFIXES); } protected final Interpolator getInterpolator() { return interpolator; } }