001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 * http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014
015package org.basepom.mojo.propertyhelper;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018import static java.lang.String.format;
019import static org.basepom.mojo.propertyhelper.IgnoreWarnFail.checkIgnoreWarnFailState;
020
021import java.io.IOException;
022import java.util.List;
023import java.util.Map;
024import java.util.function.Function;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028import com.google.common.collect.ImmutableList;
029import org.apache.maven.model.Model;
030import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
031import org.codehaus.plexus.interpolation.InterpolationException;
032import org.codehaus.plexus.interpolation.Interpolator;
033import org.codehaus.plexus.interpolation.MapBasedValueSource;
034import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
035import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
036import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
037import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
038import org.codehaus.plexus.interpolation.StringSearchInterpolator;
039
040public final class InterpolatorFactory {
041
042    private static final List<String> SYNONYM_PREFIXES = ImmutableList.of("project", "pom");
043    private static final String PREFIX = "@{";
044    private static final String POSTFIX = "}";
045
046    private final Model model;
047
048    public InterpolatorFactory(final Model model) {
049        this.model = checkNotNull(model, "model is null");
050    }
051
052    public static InterpolatorFactory forTesting() {
053        return new InterpolatorFactory(new Model());
054    }
055
056    public Function<String, String> interpolate(String name, final IgnoreWarnFail onMissingField, final Map<String, String> properties) {
057        return value -> {
058            try {
059                return interpolate(name, value, onMissingField, properties);
060            } catch (Exception e) {
061                throw Sneaky.throwAnyway(e);
062            }
063        };
064    }
065
066    private String interpolate(final String name, final String value, final IgnoreWarnFail onMissingField, final Map<String, String> properties)
067        throws IOException, InterpolationException {
068        checkNotNull(name, "name is null");
069        checkNotNull(value, "value is null");
070        checkNotNull(properties, "properties is null");
071
072        final Interpolator interpolator = new StringSearchInterpolator(PREFIX, POSTFIX);
073        interpolator.addValueSource(new EnvarBasedValueSource());
074        interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
075
076        interpolator.addValueSource(new PrefixedValueSourceWrapper(new ObjectBasedValueSource(model),
077            SYNONYM_PREFIXES,
078            true));
079
080        interpolator.addValueSource(new PrefixedValueSourceWrapper(new PropertiesBasedValueSource(model.getProperties()),
081            SYNONYM_PREFIXES,
082            true));
083
084        interpolator.addValueSource(new MapBasedValueSource(properties));
085
086        final String result = interpolator.interpolate(value, new PrefixAwareRecursionInterceptor(SYNONYM_PREFIXES, true));
087
088        Matcher matcher = Pattern.compile(Pattern.quote(PREFIX) + ".*?" + Pattern.quote(POSTFIX)).matcher(result);
089
090        checkIgnoreWarnFailState(!matcher.find(), onMissingField,
091            () -> format("template %s evaluated to %s", value, result),
092            () -> format("could not evaluate %s! (result is %s)", value, result));
093
094        return matcher.replaceAll("");
095    }
096}