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

org.codehaus.plexus.interpolation.RegexBasedInterpolator Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
package org.codehaus.plexus.interpolation;

/*
 * Copyright 2001-2008 Codehaus Foundation.
 *
 * Licensed 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.
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.codehaus.plexus.interpolation.util.StringUtils;

/**
 * Expansion of the original RegexBasedInterpolator, found in plexus-utils, this
 * interpolator provides options for setting custom prefix/suffix regex parts,
 * and includes a {@link RecursionInterceptor} parameter in its interpolate(..)
 * call, to allow the detection of cyclical expression references.
 *
 */
public class RegexBasedInterpolator implements Interpolator {

    private String startRegex;

    private String endRegex;

    private Map existingAnswers = new HashMap();

    private List valueSources = new ArrayList();

    private List postProcessors = new ArrayList();

    private boolean reusePatterns = false;

    private boolean cacheAnswers = false;

    public static final String DEFAULT_REGEXP = "\\$\\{(.+?)\\}";

    /**
     * the key is the regex the value is the Pattern
     * At the class construction time the Map will contains the default Pattern
     */
    private Map compiledPatterns = new WeakHashMap();

    /**
     * Setup a basic interpolator.
     * 

NOTE: You will have to call

* {@link RegexBasedInterpolator#addValueSource(ValueSource)} at least once * if you use this constructor! */ public RegexBasedInterpolator() { compiledPatterns.put(DEFAULT_REGEXP, Pattern.compile(DEFAULT_REGEXP)); } /** * * @param reusePatterns already compiled patterns will be reused */ public RegexBasedInterpolator(boolean reusePatterns) { this(); this.reusePatterns = reusePatterns; } /** * Setup an interpolator with no value sources, and the specified regex pattern * prefix and suffix in place of the default one. *

NOTE: You will have to call * {@link RegexBasedInterpolator#addValueSource(ValueSource)} at least once * if you use this constructor!

* * @param startRegex start of the regular expression to use * @param endRegex end of the regular expression to use */ public RegexBasedInterpolator(String startRegex, String endRegex) { this(); this.startRegex = startRegex; this.endRegex = endRegex; } /** * Setup a basic interpolator with the specified list of value sources. * * @param valueSources The list of value sources to use */ public RegexBasedInterpolator(List valueSources) { this(); this.valueSources.addAll(valueSources); } /** * Setup an interpolator with the specified value sources, and the specified * regex pattern prefix and suffix in place of the default one. * * @param startRegex start of the regular expression to use * @param endRegex end of the regular expression to use * @param valueSources The list of value sources to use */ public RegexBasedInterpolator(String startRegex, String endRegex, List valueSources) { this(); this.startRegex = startRegex; this.endRegex = endRegex; this.valueSources.addAll(valueSources); } /** * {@inheritDoc} */ public void addValueSource(ValueSource valueSource) { valueSources.add(valueSource); } /** * {@inheritDoc} */ public void removeValuesSource(ValueSource valueSource) { valueSources.remove(valueSource); } /** * {@inheritDoc} */ public void addPostProcessor(InterpolationPostProcessor postProcessor) { postProcessors.add(postProcessor); } /** * {@inheritDoc} */ public void removePostProcessor(InterpolationPostProcessor postProcessor) { postProcessors.remove(postProcessor); } /** * Attempt to resolve all expressions in the given input string, using the * given pattern to first trim an optional prefix from each expression. The * supplied recursion interceptor will provide protection from expression * cycles, ensuring that the input can be resolved or an exception is * thrown. * * @param input The input string to interpolate * * @param thisPrefixPattern An optional pattern that should be trimmed from * the start of any expressions found in the input. * * @param recursionInterceptor Used to protect the interpolation process * from expression cycles, and throw an * exception if one is detected. */ public String interpolate(String input, String thisPrefixPattern, RecursionInterceptor recursionInterceptor) throws InterpolationException { if (input == null) { // return empty String to prevent NPE too return ""; } if (recursionInterceptor == null) { recursionInterceptor = new SimpleRecursionInterceptor(); } if (thisPrefixPattern != null && thisPrefixPattern.length() == 0) { thisPrefixPattern = null; } int realExprGroup = 2; Pattern expressionPattern; if (startRegex != null || endRegex != null) { if (thisPrefixPattern == null) { expressionPattern = getPattern(startRegex + endRegex); realExprGroup = 1; } else { expressionPattern = getPattern(startRegex + thisPrefixPattern + endRegex); } } else if (thisPrefixPattern != null) { expressionPattern = getPattern("\\$\\{(" + thisPrefixPattern + ")?(.+?)\\}"); } else { expressionPattern = getPattern(DEFAULT_REGEXP); realExprGroup = 1; } try { return interpolate(input, recursionInterceptor, expressionPattern, realExprGroup); } finally { if (!cacheAnswers) { clearAnswers(); } } } private Pattern getPattern(String regExp) { if (!reusePatterns) { return Pattern.compile(regExp); } Pattern pattern; synchronized (this) { pattern = compiledPatterns.get(regExp); if (pattern != null) { return pattern; } pattern = Pattern.compile(regExp); compiledPatterns.put(regExp, pattern); } return pattern; } /** * Entry point for recursive resolution of an expression and all of its * nested expressions. * * @todo Ensure unresolvable expressions don't trigger infinite recursion. */ private String interpolate( String input, RecursionInterceptor recursionInterceptor, Pattern expressionPattern, int realExprGroup) throws InterpolationException { if (input == null) { // return empty String to prevent NPE too return ""; } String result = input; Matcher matcher = expressionPattern.matcher(result); while (matcher.find()) { String wholeExpr = matcher.group(0); String realExpr = matcher.group(realExprGroup); if (realExpr.startsWith(".")) { realExpr = realExpr.substring(1); } if (recursionInterceptor.hasRecursiveExpression(realExpr)) { throw new InterpolationCycleException(recursionInterceptor, realExpr, wholeExpr); } recursionInterceptor.expressionResolutionStarted(realExpr); try { Object value = existingAnswers.get(realExpr); for (ValueSource vs : valueSources) { if (value != null) break; value = vs.getValue(realExpr); } if (value != null) { value = interpolate(String.valueOf(value), recursionInterceptor, expressionPattern, realExprGroup); if (postProcessors != null && !postProcessors.isEmpty()) { for (InterpolationPostProcessor postProcessor : postProcessors) { Object newVal = postProcessor.execute(realExpr, value); if (newVal != null) { value = newVal; break; } } } // could use: // result = matcher.replaceFirst( stringValue ); // but this could result in multiple lookups of stringValue, and replaceAll is not correct behaviour result = StringUtils.replace(result, wholeExpr, String.valueOf(value)); matcher.reset(result); } } finally { recursionInterceptor.expressionResolutionFinished(realExpr); } } return result; } /** * Return any feedback messages and errors that were generated - but * suppressed - during the interpolation process. Since unresolvable * expressions will be left in the source string as-is, this feedback is * optional, and will only be useful for debugging interpolation problems. * * @return a {@link List} that may be interspersed with {@link String} and * {@link Throwable} instances. */ public List getFeedback() { List messages = new ArrayList(); for (Object valueSource : valueSources) { ValueSource vs = (ValueSource) valueSource; List feedback = vs.getFeedback(); if (feedback != null && !feedback.isEmpty()) { messages.addAll(feedback); } } return messages; } /** * Clear the feedback messages from previous interpolate(..) calls. */ public void clearFeedback() { for (Object valueSource : valueSources) { ValueSource vs = (ValueSource) valueSource; vs.clearFeedback(); } } /** * See {@link RegexBasedInterpolator#interpolate(String, String, RecursionInterceptor)}. *

* This method triggers the use of a {@link SimpleRecursionInterceptor} * instance for protection against expression cycles.

* * @param input The input string to interpolate * * @param thisPrefixPattern An optional pattern that should be trimmed from * the start of any expressions found in the input. */ public String interpolate(String input, String thisPrefixPattern) throws InterpolationException { return interpolate(input, thisPrefixPattern, null); } /** * See {@link RegexBasedInterpolator#interpolate(String, String, RecursionInterceptor)}. *

* This method triggers the use of a {@link SimpleRecursionInterceptor} * instance for protection against expression cycles. It also leaves empty the * expression prefix which would otherwise be trimmed from expressions. The * result is that any detected expression will be resolved as-is.

* * @param input The input string to interpolate */ public String interpolate(String input) throws InterpolationException { return interpolate(input, null, null); } /** * See {@link RegexBasedInterpolator#interpolate(String, String, RecursionInterceptor)}. *

* This method leaves empty the expression prefix which would otherwise be * trimmed from expressions. The result is that any detected expression will * be resolved as-is.

* * @param input The input string to interpolate * * @param recursionInterceptor Used to protect the interpolation process * from expression cycles, and throw an * exception if one is detected. */ public String interpolate(String input, RecursionInterceptor recursionInterceptor) throws InterpolationException { return interpolate(input, null, recursionInterceptor); } public boolean isReusePatterns() { return reusePatterns; } public void setReusePatterns(boolean reusePatterns) { this.reusePatterns = reusePatterns; } public boolean isCacheAnswers() { return cacheAnswers; } public void setCacheAnswers(boolean cacheAnswers) { this.cacheAnswers = cacheAnswers; } public void clearAnswers() { existingAnswers.clear(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy