diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
new file mode 100644
index 0000000..001fdea
--- /dev/null
+++ b/dependency-reduced-pom.xml
@@ -0,0 +1,82 @@
+
+
+
+ plexus
+ org.codehaus.plexus
+ 3.3.3
+ ../pom.xml/pom.xml
+
+ 4.0.0
+ plexus-interpolation
+ Plexus Interpolation API
+ 1.23-SNAPSHOT
+
+ JIRA
+ https://github.com/codehaus-plexus/plexus-interpolation/issues
+
+
+ scm:git:git@github.com:codehaus-plexus/plexus-interpolation.git
+ scm:git:git@github.com:codehaus-plexus/plexus-interpolation.git
+ http://github.com/codehaus-plexus/plexus-interpolation
+
+
+
+
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
+ maven-release-plugin
+
+
+ **/src/test/resources/utf8/**
+
+
+
+
+ maven-shade-plugin
+ 2.3
+
+
+ package
+
+ shade
+
+
+ true
+
+
+ commons-io:commons-io
+
+
+
+
+ org.apache.commons.io
+ org.codehaus.plexus.interpolation.io
+
+
+
+
+
+
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+ hamcrest-core
+ org.hamcrest
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index ad76542..901d2db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,15 @@
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+
+
org.apache.maven.plugins
maven-release-plugin
@@ -38,6 +47,40 @@
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.3
+
+
+ package
+
+ shade
+
+
+ true
+
+
+ commons-io:commons-io
+
+
+
+
+ org.apache.commons.io
+ org.codehaus.plexus.interpolation.io
+
+
+
+
+
+
+
+
+ commons-io
+ commons-io
+ 2.2
+
+
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedInterpolator.java
new file mode 100644
index 0000000..0838ca2
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedInterpolator.java
@@ -0,0 +1,38 @@
+package org.codehaus.plexus.interpolation.fixed;
+/*
+ * Copyright 2014 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 org.codehaus.plexus.interpolation.InterpolationException;
+import org.codehaus.plexus.interpolation.RecursionInterceptor;
+
+/**
+ * A stateless interpolator that can be re-used safely.
+ * May also be thread safe, depending on safety of underlying model objects
+ *
+ */
+public interface FixedInterpolator
+{
+ /**
+ * Interpolate the supplied string with the enclosed interpolationstate
+ * @param interpolationState the state of the interpolation operation
+ * @param input The input string to interpolate
+ * @return the interpolated value
+ */
+
+ public String interpolate( String input, InterpolationState interpolationState )
+ throws InterpolationCycleException;
+
+}
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolator.java
new file mode 100644
index 0000000..5b6ddb1
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolator.java
@@ -0,0 +1,277 @@
+package org.codehaus.plexus.interpolation.fixed;
+
+/*
+ * Copyright 2001-2009 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 org.codehaus.plexus.interpolation.InterpolationPostProcessor;
+import org.codehaus.plexus.interpolation.multi.DelimiterSpecification;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+public class FixedMultiDelimiterStringSearchInterpolator
+ implements FixedInterpolator
+{
+
+ private static final int MAX_TRIES = 10;
+
+ private final List valueSources;
+
+ private final InterpolationPostProcessor postProcessors;
+
+ private final LinkedHashSet delimiters;
+
+ private String escapeString;
+
+ public FixedMultiDelimiterStringSearchInterpolator( List valueSources, InterpolationPostProcessor postProcessors,
+ LinkedHashSet delimiters )
+ {
+ this.valueSources = valueSources;
+ this.postProcessors = postProcessors;
+ this.delimiters = delimiters;
+ this.delimiters.add( DelimiterSpecification.DEFAULT_SPEC );
+ }
+
+ public static FixedMultiDelimiterStringSearchInterpolator create( )
+ {
+ LinkedHashSet delimiters = new LinkedHashSet( 1 );
+ delimiters.add(DelimiterSpecification.DEFAULT_SPEC );
+ return new FixedMultiDelimiterStringSearchInterpolator( new ArrayList( ), null, delimiters);
+ }
+
+ public FixedMultiDelimiterStringSearchInterpolator withDelimiterSpec( DelimiterSpecification vs )
+ {
+ LinkedHashSet newList = new LinkedHashSet( delimiters );
+ newList.add( vs);
+ return new FixedMultiDelimiterStringSearchInterpolator( valueSources, postProcessors, newList);
+ }
+
+ public FixedMultiDelimiterStringSearchInterpolator withDelimiterSpec( String delimiterSpec )
+ {
+ return withDelimiterSpec( DelimiterSpecification.parse( delimiterSpec ));
+ }
+
+ public FixedMultiDelimiterStringSearchInterpolator withDelimiterSpec( Iterable delimiterSpec )
+ {
+ FixedMultiDelimiterStringSearchInterpolator current = this;
+ for ( String s : delimiterSpec )
+ {
+ current = current.withDelimiterSpec( s );
+ }
+ return current;
+ }
+
+
+ public FixedMultiDelimiterStringSearchInterpolator addDelimiterSpec( String delimiterSpec )
+ {
+ if ( delimiterSpec == null )
+ {
+ return this;
+ }
+ delimiters.add( DelimiterSpecification.parse( delimiterSpec ) );
+ return this;
+ }
+
+
+ public FixedMultiDelimiterStringSearchInterpolator withValueSource( FixedValueSource vs )
+ {
+ addValueSource( vs );
+ return this;
+ }
+
+ public FixedMultiDelimiterStringSearchInterpolator withPostProcessor( InterpolationPostProcessor postProcessor )
+ {
+ return new FixedMultiDelimiterStringSearchInterpolator( valueSources, postProcessor, delimiters );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addValueSource( FixedValueSource valueSource )
+ {
+ valueSources.add( valueSource );
+ }
+
+
+ public String interpolate( String input, InterpolationState interpolationState )
+ {
+ if ( input == null )
+ {
+ // return empty String to prevent NPE too
+ return "";
+ }
+ StringBuilder result = new StringBuilder( input.length() * 2 );
+
+ String lastResult = input;
+ int tries = 0;
+ do
+ {
+ tries++;
+ if ( result.length() > 0 )
+ {
+ lastResult = result.toString();
+ result.setLength( 0 );
+ }
+
+ int startIdx = -1;
+ int endIdx = -1;
+
+ DelimiterSpecification selectedSpec;
+ while( ( selectedSpec = select( input, endIdx ) ) != null )
+ {
+ String startExpr = selectedSpec.getBegin();
+ String endExpr = selectedSpec.getEnd();
+
+ startIdx = selectedSpec.getNextStartIndex();
+ result.append( input, endIdx + 1, startIdx );
+
+ endIdx = input.indexOf( endExpr, startIdx + 1 );
+ if ( endIdx < 0 )
+ {
+ break;
+ }
+
+ String wholeExpr = input.substring( startIdx, endIdx + endExpr.length() );
+ String realExpr = wholeExpr.substring( startExpr.length(), wholeExpr.length() - endExpr.length() );
+
+ if ( startIdx >= 0 && escapeString != null && escapeString.length() > 0 )
+ {
+ int startEscapeIdx = startIdx == 0 ? 0 : startIdx - escapeString.length();
+ if ( startEscapeIdx >= 0 )
+ {
+ String escape = input.substring( startEscapeIdx, startIdx );
+ if ( escape != null && escapeString.equals( escape ) )
+ {
+ result.append( wholeExpr );
+ result.replace( startEscapeIdx, startEscapeIdx + escapeString.length(), "" );
+ continue;
+ }
+ }
+ }
+
+ boolean resolved = false;
+ if ( ! interpolationState.unresolvable.contains( wholeExpr ) )
+ {
+ if ( realExpr.startsWith( "." ) )
+ {
+ realExpr = realExpr.substring( 1 );
+ }
+
+ if ( interpolationState.recursionInterceptor.hasRecursiveExpression( realExpr ) )
+ {
+ throw new InterpolationCycleException( interpolationState.recursionInterceptor, realExpr, wholeExpr );
+ }
+
+ interpolationState.recursionInterceptor.expressionResolutionStarted( realExpr );
+
+ Object value = null;
+ Object bestAnswer = null;
+ for ( FixedValueSource vs : valueSources )
+ {
+ if (value != null ) break;
+
+ value = vs.getValue( realExpr, interpolationState );
+
+ if ( value != null && value.toString().contains( wholeExpr ) )
+ {
+ bestAnswer = value;
+ value = null;
+ }
+ }
+
+ // this is the simplest recursion check to catch exact recursion
+ // (non synonym), and avoid the extra effort of more string
+ // searching.
+ if ( value == null && bestAnswer != null )
+ {
+ throw new InterpolationCycleException( interpolationState.recursionInterceptor, realExpr, wholeExpr );
+ }
+
+ if ( value != null )
+ {
+ value = interpolate( String.valueOf( value ), interpolationState );
+
+ if ( postProcessors != null )
+ {
+ Object newVal = postProcessors.execute( realExpr, value );
+ if ( newVal != null )
+ {
+ value = newVal;
+ }
+ }
+
+ // could use:
+ // result = matcher.replaceFirst( stringValue );
+ // but this could result in multiple lookups of stringValue, and replaceAll is not correct behaviour
+ result.append( String.valueOf( value ) );
+ resolved = true;
+ }
+ else
+ {
+ interpolationState.unresolvable.add( wholeExpr );
+ }
+
+ interpolationState.recursionInterceptor.expressionResolutionFinished( realExpr );
+ }
+
+ if ( !resolved )
+ {
+ result.append( wholeExpr );
+ }
+
+ if ( endIdx > -1 )
+ {
+ endIdx += endExpr.length() - 1;
+ }
+ }
+
+ if ( endIdx == -1 && startIdx > -1 )
+ {
+ result.append( input, startIdx, input.length() );
+ }
+ else if ( endIdx < input.length() )
+ {
+ result.append( input, endIdx + 1, input.length() );
+ }
+ }
+ while( !lastResult.equals( result.toString() ) && tries < MAX_TRIES );
+
+ return result.toString();
+ }
+
+ private DelimiterSpecification select( String input, int lastEndIdx )
+ {
+ DelimiterSpecification selected = null;
+
+ for ( DelimiterSpecification spec : delimiters )
+ {
+ spec.clearNextStart();
+
+ if ( selected == null )
+ {
+ int idx = input.indexOf( spec.getBegin(), lastEndIdx + 1 );
+ if ( idx > -1 )
+ {
+ spec.setNextStartIndex( idx );
+ selected = spec;
+ }
+ }
+ }
+
+ return selected;
+ }
+}
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolator.java
new file mode 100644
index 0000000..9411417
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolator.java
@@ -0,0 +1,295 @@
+package org.codehaus.plexus.interpolation.fixed;
+
+/*
+ * 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 org.codehaus.plexus.interpolation.InterpolationPostProcessor;
+import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
+import org.codehaus.plexus.interpolation.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Expansion of the original RegexBasedInterpolator, found in plexus-utils, this
+ * interpolator provides options for setting custom prefix/suffix regex parts,
+ * and includes a {@link org.codehaus.plexus.interpolation.RecursionInterceptor} parameter in its interpolate(..)
+ * call, to allow the detection of cyclical expression references.
+ *
+ * @version $Id$
+ */
+public class FixedRegexBasedInterpolator
+ implements FixedInterpolator
+{
+
+ private final String startRegex;
+
+ private final String endRegex;
+
+ private final String prefixPattern;
+
+ private final List valueSources;
+
+ private final InterpolationPostProcessor postProcessor;
+
+ private boolean reusePatterns = 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();
+
+ private FixedRegexBasedInterpolator( String startRegex, String endRegex, String prefixPattern,
+ List valueSources, InterpolationPostProcessor postProcessor,
+ boolean reusePatterns )
+ {
+ this.startRegex = startRegex;
+ this.endRegex = endRegex;
+ this.valueSources = valueSources;
+ this.prefixPattern = prefixPattern != null && prefixPattern.length() > 0 ? prefixPattern : null;
+ this.postProcessor = postProcessor;
+ this.reusePatterns = reusePatterns;
+ compiledPatterns.put( DEFAULT_REGEXP, Pattern.compile( DEFAULT_REGEXP ) );
+ }
+
+
+
+ public static FixedRegexBasedInterpolator create()
+ {
+ return new FixedRegexBasedInterpolator( null, null, null, new ArrayList(), null, false );
+ }
+
+
+ public static FixedRegexBasedInterpolator create(String startRegex, String endRegex, FixedValueSource... fixedValueSources)
+ {
+ return FixedRegexBasedInterpolator.create().withStartRegex( startRegex )
+ .withEndRegex( endRegex ).withValueSources( fixedValueSources );
+ }
+
+ public static FixedRegexBasedInterpolator create( FixedValueSource... fixedValueSources)
+ {
+ return create().withValueSources( fixedValueSources );
+ }
+
+ public FixedRegexBasedInterpolator withPrefix( String prefix )
+ {
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefix, valueSources, postProcessor,
+ reusePatterns );
+ }
+
+ public FixedRegexBasedInterpolator withValueSources( FixedValueSource... valueSources )
+ {
+ FixedRegexBasedInterpolator result = this;
+ for ( FixedValueSource valueSource : valueSources )
+ {
+ result = withValueSource( valueSource );
+ }
+ return result;
+ }
+
+ public FixedRegexBasedInterpolator withValueSource( FixedValueSource vs )
+ {
+ List sources = new ArrayList( valueSources );
+ sources.add( vs );
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefixPattern, sources, postProcessor,
+ reusePatterns );
+ }
+
+ public FixedRegexBasedInterpolator withStartRegex( String startRegex )
+ {
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefixPattern, valueSources, postProcessor,
+ reusePatterns );
+ }
+
+ public FixedRegexBasedInterpolator withEndRegex( String endRegex )
+ {
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefixPattern, valueSources, postProcessor,
+ reusePatterns );
+ }
+
+ public FixedRegexBasedInterpolator withPostProcessor( InterpolationPostProcessor postProcessor )
+ {
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefixPattern, valueSources, postProcessor,
+ reusePatterns );
+ }
+
+ public FixedRegexBasedInterpolator reusePatterns( boolean reusePatterns )
+ {
+ return new FixedRegexBasedInterpolator( startRegex, endRegex, prefixPattern, valueSources, postProcessor,
+ reusePatterns );
+ }
+
+ /**
+ * 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 interpolationState The state used during interpolation.
+ */
+ public String interpolate( String input, InterpolationState interpolationState )
+ throws org.codehaus.plexus.interpolation.fixed.InterpolationCycleException
+ {
+ if ( input == null )
+ {
+ // return empty String to prevent NPE too
+ return "";
+ }
+ if ( interpolationState.recursionInterceptor == null )
+ {
+ interpolationState.setRecursionInterceptor( new SimpleRecursionInterceptor() );
+ }
+
+ int realExprGroup = 2;
+ Pattern expressionPattern;
+ if ( startRegex != null || endRegex != null )
+ {
+ if ( prefixPattern == null )
+ {
+ expressionPattern = getPattern( startRegex + endRegex );
+ realExprGroup = 1;
+ }
+ else
+ {
+ expressionPattern = getPattern( startRegex + prefixPattern + endRegex );
+ }
+
+ }
+ else if ( prefixPattern != null )
+ {
+ expressionPattern = getPattern( "\\$\\{(" + prefixPattern + ")?(.+?)\\}" );
+ }
+ else
+ {
+ expressionPattern = getPattern( DEFAULT_REGEXP );
+ realExprGroup = 1;
+ }
+
+ return interpolate( input, interpolationState, expressionPattern, realExprGroup );
+ }
+
+ 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, InterpolationState interpolationState, Pattern expressionPattern,
+ int realExprGroup )
+ {
+ 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 ( interpolationState.recursionInterceptor.hasRecursiveExpression( realExpr ) )
+ {
+ throw new InterpolationCycleException( interpolationState.recursionInterceptor, realExpr, wholeExpr );
+ }
+
+ interpolationState.recursionInterceptor.expressionResolutionStarted( realExpr );
+ try
+ {
+ Object value = null;
+ for ( FixedValueSource vs : valueSources )
+ {
+ if ( value != null )
+ {
+ break;
+ }
+
+ value = vs.getValue( realExpr, interpolationState );
+ }
+
+ if ( value != null )
+ {
+ value =
+ interpolate( String.valueOf( value ), interpolationState, expressionPattern, realExprGroup );
+
+ if ( postProcessor != null )
+ {
+ Object newVal = postProcessor.execute( realExpr, value );
+ if ( newVal != null )
+ {
+ value = newVal;
+ }
+ }
+
+ // 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
+ {
+ interpolationState.recursionInterceptor.expressionResolutionFinished( realExpr );
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedSingleResponseValueSource.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedSingleResponseValueSource.java
new file mode 100644
index 0000000..fa1bb83
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedSingleResponseValueSource.java
@@ -0,0 +1,52 @@
+package org.codehaus.plexus.interpolation.fixed;
+
+/*
+ * Copyright 2001-2009 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 org.codehaus.plexus.interpolation.ValueSource;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * If the expression matches, simply return the response object.
+ * @since 1.12
+ */
+public class FixedSingleResponseValueSource
+ implements FixedValueSource
+{
+
+ private final String expression;
+ private final Object response;
+
+ public FixedSingleResponseValueSource( String expression, Object response )
+ {
+ this.expression = expression;
+ this.response = response;
+ }
+
+
+ public Object getValue( String expression, InterpolationState interpolationState )
+ {
+ if ( this.expression.equals( expression ) )
+ {
+ return response;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolator.java
index 95b15d6..5cc49e9 100644
--- a/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolator.java
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolator.java
@@ -29,14 +29,14 @@
* A fixed string search interpolator is permanently bound to a given set of value sources,
* an is totally fixed and stateless over these value sources.
*
- * The fixed interpolator is also a #StatelessValueSource and can be used as a source
+ * The fixed interpolator is also a {@link FixedValueSource} and can be used as a source
* for a different fixed interpolator, creating a scope chain.
*
* Once constructed, this interpolator will always point to the same set of objects (value sources),
* in such a way that if the underlying object is fixed, expressions will always
* evaluate to the same result.
*
- * Th fixed interpolator can be shared among different clients and is thread safe to
+ * The fixed interpolator can be shared among different clients and is thread safe to
* the extent the underlying value sources can be accessed safely.
* Since interpolation expressions cannot modify the objects, thread safety concerns
* this will normally be limited to safe publication and memory model visibility of
@@ -46,7 +46,7 @@
* The fixed interpolator can be a valuesource
*/
public class FixedStringSearchInterpolator
- implements FixedValueSource
+ implements FixedValueSource, FixedInterpolator
{
private final FixedValueSource[] valueSources;
@@ -63,12 +63,17 @@ public class FixedStringSearchInterpolator
private final String escapeString;
+ private final InterpolationContextCache cache;
+
private FixedStringSearchInterpolator( String startExpr, String endExpr, String escapeString,
- InterpolationPostProcessor postProcessor, FixedValueSource... valueSources )
+ InterpolationPostProcessor postProcessor,
+ InterpolationContextCache cache,
+ FixedValueSource... valueSources )
{
this.startExpr = startExpr;
this.endExpr = endExpr;
this.escapeString = escapeString;
+ this.cache = cache;
if ( valueSources == null )
{
throw new IllegalArgumentException( "valueSources cannot be null" );
@@ -88,13 +93,18 @@ private FixedStringSearchInterpolator( String startExpr, String endExpr, String
public static FixedStringSearchInterpolator create( String startExpr, String endExpr,
FixedValueSource... valueSources )
{
- return new FixedStringSearchInterpolator( startExpr, endExpr, null, null, valueSources );
+ return new FixedStringSearchInterpolator( startExpr, endExpr, null, null, null, valueSources );
}
public static FixedStringSearchInterpolator create( FixedValueSource... valueSources )
{
- return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, valueSources );
+ return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, null, valueSources );
+ }
+
+ public static FixedStringSearchInterpolator create( InterpolationContextCache cache, FixedValueSource... valueSources )
+ {
+ return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, cache, valueSources );
}
public static FixedStringSearchInterpolator createWithPermittedNulls( FixedValueSource... valueSources )
@@ -104,23 +114,23 @@ public static FixedStringSearchInterpolator createWithPermittedNulls( FixedValue
{
if (item != null) nonnulls.add( item);
}
- return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, nonnulls.toArray(new FixedValueSource[nonnulls.size()]) );
+ return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, null, nonnulls.toArray(new FixedValueSource[nonnulls.size()]) );
}
public FixedStringSearchInterpolator withExpressionMarkers( String startExpr, String endExpr )
{
- return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
+ return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, null, valueSources );
}
public FixedStringSearchInterpolator withPostProcessor( InterpolationPostProcessor postProcessor )
{
- return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
+ return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, null, valueSources );
}
public FixedStringSearchInterpolator withEscapeString( String escapeString )
{
- return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
+ return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, null, valueSources );
}
public String interpolate( String input )
@@ -257,6 +267,10 @@ public String interpolate( String input, InterpolationState interpolationState )
{
value = interpolate( String.valueOf( value ), interpolationState );
+ if (cache != null) {
+ cache.putValue( realExpr, value );
+ }
+
if ( postProcessor != null )
{
Object newVal = postProcessor.execute( realExpr, value );
diff --git a/src/main/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCache.java b/src/main/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCache.java
new file mode 100644
index 0000000..5616fb5
--- /dev/null
+++ b/src/main/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCache.java
@@ -0,0 +1,204 @@
+/*
+ Copyright 2015 the original author or authors
+
+ 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.
+*/
+
+package org.codehaus.plexus.interpolation.fixed;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedHashMap;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class InterpolationContextCache
+{
+ private final LinkedHashMap values = new LinkedHashMap();
+
+ private final MessageDigest md = getMesageDigestInstance();
+
+ private final File cacheFile;
+
+ private boolean cacheable = true;
+
+ private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ public InterpolationContextCache( File cacheFile )
+ {
+ this.cacheFile = cacheFile;
+ }
+
+ /**
+ * Indicates if the cached interpolation expressions resolve to the same values in the new interpolator
+ *
+ * @param interpolator The new interpolator
+ * @param interpolationState The interpolation state, will be cleared completion of this method
+ * @return true if the new interpolator resolves to the same values as the old one. If false, the caller should
+ * always re-interpolate, no matter what
+ * @throws IOException
+ */
+ public boolean resolvesToSameValues( FixedValueSource interpolator, InterpolationState interpolationState )
+ throws IOException
+ {
+ if (!hasCacheFile()) return false;
+ BufferedReader br = new BufferedReader( new FileReader( cacheFile ) );
+ String sha1 = br.readLine();
+ String key;
+ Object value;
+
+ while ( ( key = br.readLine() ) != null )
+ {
+ value = interpolator.getValue( key, interpolationState );
+ addToHash( value );
+ }
+
+ interpolationState.clear();
+ return getHexHash().equals( sha1 );
+ }
+
+ private static MessageDigest getMesageDigestInstance()
+ {
+ try
+ {
+ return MessageDigest.getInstance( "SHA-1" );
+ }
+ catch ( NoSuchAlgorithmException e )
+ {
+ throw new RuntimeException( e );
+ }
+ }
+
+ /**
+ * Registers the result of an interpolation
+ *
+ * @param key The interpolation key
+ * @param value The resolved value
+ */
+
+ public InterpolationContextCache putValue( String key, Object value )
+ {
+ Object existing = values.get( key );
+ if ( existing != null )
+ {
+ if ( !existing.equals( value ) )
+ {
+ cacheable = false;
+ }
+ }
+ else
+ {
+ values.put( key, value );
+ }
+ return this;
+ }
+
+ public InterpolationContextCache store()
+ throws IOException
+ {
+ if ( !cacheable )
+ {
+ if ( cacheFile.exists() )
+ {
+ FileUtils.deleteQuietly( cacheFile );
+ }
+ return this;
+ }
+ FileOutputStream fos = new FileOutputStream( cacheFile );
+ Writer writer = new OutputStreamWriter( fos, UTF_8 );
+ writer.write( getSha1( values.values() ) );
+ writer.write( '\n' );
+ for ( String s : values.keySet() )
+ {
+ writer.write( s );
+ writer.write( '\n' );
+ }
+ writer.close();
+ return this;
+ }
+
+ /**
+ * Inquires if we have to filter no matter what, based on file attributes
+ * @param targetFile The filtered output file
+ * @return True if the source file is newer than the target, or the target does not exist
+ */
+ public boolean mustFilterDueToUpdateCheck( File sourceFile, File targetFile )
+ {
+ return !targetFile.exists() || sourceFile.lastModified() > targetFile.lastModified();
+ }
+
+ /**
+ * Indicates if we have a cache file
+ * @return True if we have a cache file
+ */
+ private boolean hasCacheFile( ){
+ return cacheFile.exists();
+ }
+
+
+ private String getSha1( Iterable values )
+ {
+ for ( Object value : values )
+ {
+ addToHash( value );
+ }
+
+ return getHexHash();
+ }
+
+ private String getHexHash()
+ {
+ byte[] sha1hash = md.digest();
+ md.reset();
+ return asHexString( sha1hash );
+ }
+
+ private void addToHash( Object value )
+ {
+ // dont really care which encoding is used as long as we're consistent
+ md.update( value.toString().getBytes( UTF_8 ) );
+ }
+
+ @SuppressWarnings( "checkstyle:magicnumber" )
+ private static String asHexString( byte[] bytes )
+ {
+ if ( bytes == null )
+ {
+ return null;
+ }
+ final StringBuilder result = new StringBuilder( 2 * bytes.length );
+ for ( byte b : bytes )
+ {
+ result.append( HEX.charAt( ( b & 0xF0 ) >> 4 ) ).append( HEX.charAt( ( b & 0x0F ) ) );
+ }
+ return result.toString();
+ }
+
+ private static final String HEX = "0123456789ABCDEF";
+
+ public File getCacheFile()
+ {
+ return cacheFile;
+ }
+}
diff --git a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolatorTest.java
new file mode 100644
index 0000000..697571f
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedMultiDelimiterStringSearchInterpolatorTest.java
@@ -0,0 +1,59 @@
+package org.codehaus.plexus.interpolation.fixed;
+
+import org.codehaus.plexus.interpolation.*;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.*;
+
+public class FixedMultiDelimiterStringSearchInterpolatorTest
+{
+
+ @Test
+ public void interpolationWithDifferentDelimiters()
+ throws InterpolationException
+ {
+ Map ctx = new HashMap();
+ ctx.put( "name", "User" );
+ ctx.put( "otherName", "@name@" );
+
+ String input = "${otherName}";
+
+ FixedValueSource vs = new MapBasedValueSource( ctx );
+ FixedMultiDelimiterStringSearchInterpolator interpolator = FixedMultiDelimiterStringSearchInterpolator.create().withDelimiterSpec(
+ Arrays.asList( "@" ) )
+ .withValueSource( vs );
+
+ InterpolationState is = new InterpolationState();
+
+ String result = interpolator.interpolate( input,is );
+
+ assertEquals( ctx.get( "name" ), result );
+ }
+
+ @Test
+ public void testSuccessiveInterpolationWithDifferentDelimiters_ReversedDelimiterSequence()
+ throws InterpolationException
+ {
+ Map ctx = new HashMap();
+ ctx.put( "name", "User" );
+ ctx.put( "otherName", "${name}" );
+
+ String input = "@otherName@";
+
+ FixedValueSource vs = new MapBasedValueSource( ctx );
+ FixedMultiDelimiterStringSearchInterpolator interpolator = FixedMultiDelimiterStringSearchInterpolator.create().addDelimiterSpec(
+ "@" )
+ .withValueSource( vs );
+
+ InterpolationState is = new InterpolationState();
+
+ String result = interpolator.interpolate( input, is );
+
+ assertEquals( ctx.get( "name" ), result );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolatorTest.java
new file mode 100644
index 0000000..d6731de
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedRegexBasedInterpolatorTest.java
@@ -0,0 +1,170 @@
+package org.codehaus.plexus.interpolation.fixed;
+
+import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
+import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.*;
+
+public class FixedRegexBasedInterpolatorTest
+{
+ @SuppressWarnings( "UnusedDeclaration" )
+ public String getVar()
+ {
+ return "testVar";
+ }
+
+ @Test
+ public void shouldFailOnExpressionCycle()
+ {
+ Properties props = new Properties();
+ props.setProperty( "key1", "${key2}" );
+ props.setProperty( "key2", "${key1}" );
+
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create(
+ new org.codehaus.plexus.interpolation.fixed.PropertiesBasedValueSource( props ) );
+
+ InterpolationState is = new InterpolationState();
+ is.setRecursionInterceptor( new SimpleRecursionInterceptor() );
+ try
+ {
+ rbi.interpolate( "${key1}", is );
+
+ fail( "Should detect expression cycle and fail." );
+ }
+ catch ( InterpolationCycleException e )
+ {
+ // expected
+ }
+ }
+
+ @Test
+ public void shouldResolveByMy_getVar_Method()
+ {
+ FixedRegexBasedInterpolator rbi =
+ FixedRegexBasedInterpolator.create( new ObjectBasedValueSource( this ) );
+ InterpolationState is = new InterpolationState();
+
+ String result = rbi.withPrefix( "this" ).interpolate( "this is a ${this.var}", is );
+
+ assertEquals( "this is a testVar", result );
+ }
+
+ @Test
+ public void shouldResolveByContextValue()
+ {
+ Map context = new HashMap();
+ context.put( "var", "testVar" );
+
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create(
+ new org.codehaus.plexus.interpolation.fixed.MapBasedValueSource( context ) );
+ InterpolationState is = new InterpolationState();
+
+ String result = rbi.withPrefix( "this" ).interpolate( "this is a ${this.var}", is );
+
+ assertEquals( "this is a testVar", result );
+ }
+
+ @Test
+ public void shouldResolveByEnvar()
+ throws IOException
+ {
+ InterpolationState is = new InterpolationState();
+ FixedRegexBasedInterpolator rbi =
+ FixedRegexBasedInterpolator.create( new EnvarBasedValueSource() );
+
+ String result = rbi.withPrefix( "this" ).interpolate( "this is a ${env.HOME}", is );
+
+ assertFalse( "this is a ${HOME}".equals( result ) );
+ assertFalse( "this is a ${env.HOME}".equals( result ) );
+ }
+
+ @Test
+ public void useAlternateRegex()
+ throws Exception
+ {
+ Map context = new HashMap();
+ context.put( "var", "testVar" );
+
+ InterpolationState is = new InterpolationState();
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create( "\\@\\{(", ")?([^}]+)\\}@",
+ new org.codehaus.plexus.interpolation.fixed.MapBasedValueSource(
+ context ) );
+
+ String result = rbi.withPrefix( "this" ).interpolate( "this is a @{this.var}@", is );
+
+ assertEquals( "this is a testVar", result );
+ }
+
+ @Test
+ public void testNPEFree()
+ throws Exception
+ {
+ Map context = new HashMap();
+ context.put( "var", "testVar" );
+
+ InterpolationState is = new InterpolationState();
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create( "\\@\\{(", ")?([^}]+)\\}@",
+ new org.codehaus.plexus.interpolation.fixed.MapBasedValueSource(
+ context ) );
+
+ String result = rbi.interpolate( null, is );
+
+ assertEquals( "", result );
+ }
+
+ @Test
+ public void testUsePostProcessor_DoesNotChangeValue()
+ {
+ Map context = new HashMap();
+ context.put( "test.var", "testVar" );
+
+ InterpolationState is = new InterpolationState();
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create(
+ new org.codehaus.plexus.interpolation.fixed.MapBasedValueSource( context ) ).withPostProcessor(
+ new InterpolationPostProcessor()
+ {
+ public Object execute( String expression, Object value )
+ {
+ return null;
+ }
+ } );
+
+ String result = rbi.interpolate( "this is a ${test.var}", is );
+
+ assertEquals( "this is a testVar", result );
+ }
+
+ @Test
+ public void testUsePostProcessor_ChangesValue()
+ {
+ int loopNumber = 200000;
+
+ Map context = new HashMap();
+ context.put( "test.var", "testVar" );
+
+ InterpolationState is = new InterpolationState();
+ FixedRegexBasedInterpolator rbi = FixedRegexBasedInterpolator.create(
+ new org.codehaus.plexus.interpolation.fixed.MapBasedValueSource( context ) ).withPostProcessor(
+ new InterpolationPostProcessor()
+ {
+ public Object execute( String expression, Object value )
+ {
+ return value + "2";
+ }
+ } );
+
+ for ( int i = 0; i < loopNumber; i++ )
+ {
+ String result = rbi.interpolate( "this is a ${test.var}", is );
+
+ assertEquals( "this is a testVar2", result );
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCacheTest.java b/src/test/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCacheTest.java
new file mode 100644
index 0000000..8009f2b
--- /dev/null
+++ b/src/test/java/org/codehaus/plexus/interpolation/fixed/InterpolationContextCacheTest.java
@@ -0,0 +1,245 @@
+/*
+ Copyright 2015 the original author or authors
+
+ 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.
+*/
+
+package org.codehaus.plexus.interpolation.fixed;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator.create;
+import static org.junit.Assert.*;
+
+public class InterpolationContextCacheTest
+{
+
+ private static final int MINIMAL_FS_TIMESTAMP_GRANULARITY = 2000;
+
+ private File testDir = new File( "target/output" );
+
+ {
+ //noinspection ResultOfMethodCallIgnored
+ testDir.mkdirs();
+ }
+
+ private final String initalpayload = "a${v1}payload\n";
+
+ private final String expectedPayload = "aabcpayload\n";
+
+ @Test
+ public void basicStoreOfAttrs()
+ throws Exception
+ {
+ assertContents( getBasicCache( new File( testDir, "testStore.txt" ) ).store() );
+ }
+
+ @Test
+ public void repeatedValuesShouldProduceSameResults()
+ throws Exception
+ {
+ assertContents( getBasicCache( new File( testDir, "testStore2.txt" ) ).putValue( "v1", "abc" ).store() );
+ }
+
+
+ @Test
+ public void shouldDeletesExistingWhenNowUncacheableDoesNotStore()
+ throws Exception
+ {
+ InterpolationContextCache ic = getBasicCache( new File( testDir, "testStore3.txt" ) );
+ ic.store();
+ assertTrue( ic.getCacheFile().exists() );
+ ic.putValue( "v1", "abdc" );
+ ic.store(); // removes existing file
+ assertFalse( ic.getCacheFile().exists() );
+ ic.store(); // does not store
+ assertFalse( ic.getCacheFile().exists() );
+ }
+
+ @Test
+ public void shouldNotStoreWhenUncacheable()
+ throws Exception
+ {
+ InterpolationContextCache ic = getBasicCache( new File( testDir, "testStore4.txt" ) );
+ ic.putValue( "v1", "abdc" );
+ ic.store(); // does not store
+ assertFalse( ic.getCacheFile().exists() );
+ }
+
+ @Test
+ public void shouldBeAbleToResolveToSame()
+ throws IOException
+ {
+ InterpolationContextCache ic = getBasicCache( new File( testDir, "testStore5.txt" ) );
+ ic.store();
+
+ assertTrue( ic.resolvesToSameValues( getMatchingValueSource(), new InterpolationState() ) );
+ }
+
+ @Test
+ public void shouldNotBeAbleToResolveToSame()
+ throws IOException
+ {
+ InterpolationContextCache ic = getBasicCache( new File( testDir, "testStore6.txt" ) );
+ ic.store();
+
+ assertFalse( ic.resolvesToSameValues( getNonMatchingValueSource(), new InterpolationState() ) );
+ }
+
+ @Test
+ public void needsToFilter()
+ throws IOException
+ {
+ File cacheFile = interpolateOnceToWriteCacheFile( "needsToFilter" );
+
+ InterpolationContextCache nextRun = getBasicCache( cacheFile );
+
+ FixedStringSearchInterpolator nextInterpolator = create( nextRun, getNonMatchingValueSource() );
+
+ assertFalse( nextRun.resolvesToSameValues( nextInterpolator, new InterpolationState() ) );
+ }
+
+ @Test
+ public void doesNotNeedToFilter()
+ throws IOException
+ {
+ File cacheFile = interpolateOnceToWriteCacheFile( "needsToFilter" );
+
+ InterpolationContextCache nextRun = getBasicCache( cacheFile );
+
+ FixedStringSearchInterpolator nextInterpolator = create( nextRun, getMatchingValueSource() );
+
+ assertTrue( nextRun.resolvesToSameValues( nextInterpolator, new InterpolationState() ) );
+ }
+
+ @Test
+ public void mustFilterNoMatterWhatWhenTimeStampChanges()
+ throws IOException
+ {
+ long timeStamp = System.currentTimeMillis();
+ File sourceFile = writeTestFile( "shouldNotNeedToFilter", initalpayload, timeStamp );
+ File target = writeTestFile( "shouldNotNeedToFilter.interpolated", expectedPayload, timeStamp );
+
+ InterpolationContextCache nextRun = getBasicCache( interpolateOnceToWriteCacheFile( "needsToFilter" ) );
+
+ assertFalse( nextRun.mustFilterDueToUpdateCheck( sourceFile, target ) );
+
+ assertTrue( sourceFile.setLastModified( timeStamp + MINIMAL_FS_TIMESTAMP_GRANULARITY ) );
+
+ assertTrue( nextRun.mustFilterDueToUpdateCheck( sourceFile, target ) );
+ }
+
+ @Test
+ public void missingCacheFileAlways()
+ throws IOException
+ {
+ InterpolationContextCache nextRun = getBasicCache( new File( "NonExisting" ) );
+
+ FixedStringSearchInterpolator nextInterpolator = create( nextRun, getNonMatchingValueSource() );
+
+ assertFalse( nextRun.resolvesToSameValues( nextInterpolator, new InterpolationState() ) );
+ }
+
+ @Test
+ public void mustFilterNoMatterWithoutTargetFile()
+ throws IOException
+ {
+ long timeStamp = System.currentTimeMillis();
+ File sourceFile = writeTestFile( "shouldNotNeedToFilter", initalpayload, timeStamp );
+ File target = new File("doesNotExist" );
+
+
+ InterpolationContextCache nextRun = getBasicCache( new File("cacheFile" ));
+
+ assertTrue( nextRun.mustFilterDueToUpdateCheck( sourceFile, target ) );
+ }
+
+
+
+ private File interpolateOnceToWriteCacheFile( String cacheFileName )
+ throws IOException
+ {
+ File cacheFile = createTestFile( cacheFileName + ".cacheFile" );
+ InterpolationContextCache basicCache = getBasicCache( cacheFile );
+
+ FixedStringSearchInterpolator interpolator = create( basicCache, getMatchingValueSource() );
+
+ InterpolationState interpolationState = new InterpolationState();
+ String actual = interpolator.interpolate( initalpayload, interpolationState );
+ assertEquals( expectedPayload, actual );
+
+ basicCache.store();
+ return cacheFile;
+ }
+
+ @SuppressWarnings( "ResultOfMethodCallIgnored" )
+ private File writeTestFile( String fileName, String a1payload, long timeStamp )
+ throws IOException
+ {
+ File file = createTestFile( fileName );
+ FileOutputStream fos = new FileOutputStream( file );
+ fos.write( a1payload.getBytes( "utf-8" ));
+ fos.close();
+ file.setLastModified( timeStamp );
+ return file;
+
+ }
+
+ private File createTestFile( String fileName )
+ {
+ return new File( testDir, fileName );
+ }
+
+ private InterpolationContextCache getBasicCache( File cachefile )
+ {
+ InterpolationContextCache ic = new InterpolationContextCache( cachefile );
+ ic.putValue( "v1", "abc" );
+ ic.putValue( "v2", "cde" );
+ return ic;
+ }
+
+ private FixedValueSource getMatchingValueSource()
+ {
+ Map values = new HashMap();
+ values.put( "v1", "abc" );
+ values.put( "v2", "cde" );
+ return new MapBasedValueSource( values );
+ }
+
+ private FixedValueSource getNonMatchingValueSource()
+ {
+ Map values = new HashMap();
+ values.put( "v1", "aDDc" );
+ values.put( "v2", "cde" );
+ return new MapBasedValueSource( values );
+ }
+
+ private void assertContents( InterpolationContextCache ic )
+ throws IOException
+ {
+ BufferedReader br = new BufferedReader( new FileReader( ic.getCacheFile() ) );
+ assertEquals( "D9F4E651F88121479D8C6CDA4441ECBA65687415", br.readLine() );
+ assertEquals( "v1", br.readLine() );
+ assertEquals( "v2", br.readLine() );
+ br.close();
+ }
+
+}
\ No newline at end of file