o Flipped interpolation over to use the in-memory based interpolator from 2.2.x which makes the overall building twice as fast as before

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@780749 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Benjamin Bentmann 2009-06-01 17:49:34 +00:00
parent 9501ad8835
commit 0968ba79f3
14 changed files with 819 additions and 2857 deletions

View File

@ -29,6 +29,10 @@
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-interpolation</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-annotations</artifactId>

View File

@ -28,11 +28,13 @@ import java.util.List;
import java.util.Map;
import org.apache.maven.model.inheritance.InheritanceAssembler;
import org.apache.maven.model.interpolator.Interpolator;
import org.apache.maven.model.interpolation.ModelInterpolationException;
import org.apache.maven.model.interpolation.ModelInterpolator;
import org.apache.maven.model.io.ModelParseException;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.management.ManagementInjector;
import org.apache.maven.model.normalization.ModelNormalizer;
import org.apache.maven.model.path.ModelPathTranslator;
import org.apache.maven.model.plugin.LifecycleBindingsInjector;
import org.apache.maven.model.plugin.PluginConfigurationExpander;
import org.apache.maven.model.profile.ProfileActivationException;
@ -66,7 +68,10 @@ public class DefaultModelBuilder
private ModelNormalizer modelNormalizer;
@Requirement
private Interpolator modelInterpolator;
private ModelInterpolator modelInterpolator;
@Requirement
private ModelPathTranslator modelPathTranslator;
@Requirement
private InheritanceAssembler inheritanceAssembler;
@ -155,6 +160,8 @@ public class DefaultModelBuilder
resultModel = interpolateModel( resultModel, request );
resultModels.set( 0, resultModel );
modelPathTranslator.alignToBaseDirectory( resultModel, resultModel.getProjectDirectory() );
if ( request.isProcessPlugins() )
{
lifecycleBindingsInjector.injectLifecycleBindings( resultModel );
@ -281,14 +288,11 @@ public class DefaultModelBuilder
{
try
{
Model result =
modelInterpolator.interpolateModel( model,
request.getProfileActivationContext().getExecutionProperties(),
model.getProjectDirectory() );
Model result = modelInterpolator.interpolateModel( model, model.getProjectDirectory(), request );
result.setPomFile( model.getPomFile() );
return result;
}
catch ( IOException e )
catch ( ModelInterpolationException e )
{
throw new ModelBuildingException( "Failed to interpolate model " + toSourceHint( model ), e );
}

View File

@ -0,0 +1,229 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
import org.apache.maven.model.Model;
import org.apache.maven.model.ModelBuildingRequest;
import org.apache.maven.model.path.PathTranslator;
import org.codehaus.plexus.component.annotations.Requirement;
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 java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* Use a regular expression search to find and resolve expressions within the POM.
*
* @author jdcasey Created on Feb 3, 2005
* @version $Id$
* @todo Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans.
*/
public abstract class AbstractStringBasedModelInterpolator
implements ModelInterpolator
{
private static final List<String> PROJECT_PREFIXES = Arrays.asList( new String[]{ "pom.", "project." } );
private static final List<String> TRANSLATED_PATH_EXPRESSIONS;
static
{
List<String> translatedPrefixes = new ArrayList<String>();
// 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;
}
@Requirement
private PathTranslator pathTranslator;
private Interpolator interpolator;
private RecursionInterceptor recursionInterceptor;
public AbstractStringBasedModelInterpolator()
{
interpolator = createInterpolator();
recursionInterceptor = new PrefixAwareRecursionInterceptor( PROJECT_PREFIXES );
}
protected List<ValueSource> createValueSources( final Model model, final File projectDir, final ModelBuildingRequest 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().toURI().toString();
}
return null;
}
},
PROJECT_PREFIXES, false );
List<ValueSource> valueSources = new ArrayList<ValueSource>( 9 );
// NOTE: Order counts here!
valueSources.add( basedirValueSource );
valueSources.add( baseUriValueSource );
// TODO: valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), timestampFormat ) );
valueSources.add( modelValueSource1 );
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<InterpolationPostProcessor> createPostProcessors( final Model model, final File projectDir,
final ModelBuildingRequest config )
{
return Collections.singletonList( (InterpolationPostProcessor) new PathTranslatingPostProcessor(
PROJECT_PREFIXES,
TRANSLATED_PATH_EXPRESSIONS,
projectDir,
pathTranslator ) );
}
protected String interpolateInternal( String src, List<ValueSource> valueSources,
List<InterpolationPostProcessor> postProcessors )
throws ModelInterpolationException
{
if ( src.indexOf( "${" ) < 0 )
{
return src;
}
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 );
}
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();
protected final Interpolator getInterpolator()
{
return interpolator;
}
}

View File

@ -0,0 +1,63 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.plexus.interpolation.AbstractValueSource;
/**
*
* @version $Id$
*/
public class BuildTimestampValueSource
extends AbstractValueSource
{
private final Date startTime;
private final String format;
private String formattedDate;
public BuildTimestampValueSource( Date startTime, String format )
{
super( false );
this.startTime = startTime;
this.format = format;
}
public Object getValue( String expression )
{
if ( "build.timestamp".equals( expression ) || "maven.build.timestamp".equals( expression ) )
{
if ( formattedDate == null && startTime != null )
{
formattedDate = new SimpleDateFormat( format ).format( startTime );
}
return formattedDate;
}
return null;
}
}

View File

@ -0,0 +1,42 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
/**
* @author jdcasey
* <p/>
* Created on Feb 2, 2005
*/
@SuppressWarnings("serial")
public class ModelInterpolationException
extends Exception
{
public ModelInterpolationException( String message )
{
super( message );
}
public ModelInterpolationException( String message, Throwable cause )
{
super( message, cause );
}
}

View File

@ -0,0 +1,42 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
import org.apache.maven.model.Model;
import org.apache.maven.model.ModelBuildingRequest;
import java.io.File;
/**
* @author jdcasey
* <p/>
* Created on Feb 2, 2005
*/
public interface ModelInterpolator
{
String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyyMMdd-HHmm";
String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
Model interpolateModel( Model model, File projectDir, ModelBuildingRequest request )
throws ModelInterpolationException;
}

View File

@ -0,0 +1,62 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
import org.apache.maven.model.path.PathTranslator;
import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
import org.codehaus.plexus.interpolation.util.ValueSourceUtils;
import java.io.File;
import java.util.List;
/**
*
* @version $Id$
*/
public class PathTranslatingPostProcessor
implements InterpolationPostProcessor
{
private final List<String> unprefixedPathKeys;
private final File projectDir;
private final PathTranslator pathTranslator;
private final List<String> expressionPrefixes;
public PathTranslatingPostProcessor( List<String> expressionPrefixes, List<String> unprefixedPathKeys, File projectDir, PathTranslator pathTranslator )
{
this.expressionPrefixes = expressionPrefixes;
this.unprefixedPathKeys = unprefixedPathKeys;
this.projectDir = projectDir;
this.pathTranslator = pathTranslator;
}
public Object execute( String expression, Object value )
{
expression = ValueSourceUtils.trimPrefix( expression, expressionPrefixes, true );
if ( projectDir != null && value != null && unprefixedPathKeys.contains( expression ) )
{
return pathTranslator.alignToBaseDirectory( String.valueOf( value ), projectDir );
}
return value;
}
}

View File

@ -0,0 +1,365 @@
package org.apache.maven.model.interpolation;
/*
* 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.
*/
import org.apache.maven.model.Model;
import org.apache.maven.model.ModelBuildingRequest;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
import org.codehaus.plexus.interpolation.ValueSource;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@Component( role = ModelInterpolator.class )
public class StringSearchModelInterpolator
extends AbstractStringBasedModelInterpolator
{
private static final Map<Class<?>, Field[]> fieldsByClass = new WeakHashMap<Class<?>, Field[]>();
private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = new WeakHashMap<Class<?>, Boolean>();
public Model interpolateModel( Model model, File projectDir, ModelBuildingRequest config )
throws ModelInterpolationException
{
interpolateObject( model, model, projectDir, config );
return model;
}
protected void interpolateObject( Object obj, Model model, File projectDir, ModelBuildingRequest config )
throws ModelInterpolationException
{
try
{
List<ValueSource> valueSources = createValueSources( model, projectDir, config );
List<InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config );
InterpolateObjectAction action =
new InterpolateObjectAction( obj, valueSources, postProcessors, this );
ModelInterpolationException error = AccessController.doPrivileged( action );
if ( error != null )
{
throw error;
}
}
finally
{
getInterpolator().clearAnswers();
}
}
protected Interpolator createInterpolator()
{
StringSearchInterpolator interpolator = new StringSearchInterpolator();
interpolator.setCacheAnswers( true );
return interpolator;
}
private static final class InterpolateObjectAction implements PrivilegedAction<ModelInterpolationException>
{
private final LinkedList<Object> interpolationTargets;
private final StringSearchModelInterpolator modelInterpolator;
private final List<ValueSource> valueSources;
private final List<InterpolationPostProcessor> postProcessors;
public InterpolateObjectAction( Object target, List<ValueSource> valueSources,
List<InterpolationPostProcessor> postProcessors,
StringSearchModelInterpolator modelInterpolator )
{
this.valueSources = valueSources;
this.postProcessors = postProcessors;
this.interpolationTargets = new LinkedList<Object>();
interpolationTargets.add( target );
this.modelInterpolator = modelInterpolator;
}
public ModelInterpolationException run()
{
while( !interpolationTargets.isEmpty() )
{
Object obj = interpolationTargets.removeFirst();
try
{
traverseObjectWithParents( obj.getClass(), obj );
}
catch ( ModelInterpolationException e )
{
return e;
}
}
return null;
}
@SuppressWarnings("unchecked")
private void traverseObjectWithParents( Class<?> cls, Object target )
throws ModelInterpolationException
{
if ( cls == null )
{
return;
}
if ( cls.isArray() )
{
evaluateArray( target );
}
else if ( isQualifiedForInterpolation( cls ) )
{
Field[] fields = fieldsByClass.get( cls );
if ( fields == null )
{
fields = cls.getDeclaredFields();
fieldsByClass.put( cls, fields );
}
for ( int i = 0; i < fields.length; i++ )
{
Class<?> type = fields[i].getType();
if ( isQualifiedForInterpolation( fields[i], type ) )
{
boolean isAccessible = fields[i].isAccessible();
fields[i].setAccessible( true );
try
{
try
{
if ( String.class == type )
{
String value = (String) fields[i].get( target );
if ( value != null )
{
String interpolated = modelInterpolator.interpolateInternal( value, valueSources, postProcessors );
if ( !interpolated.equals( value ) )
{
fields[i].set( target, interpolated );
}
}
}
else if ( Collection.class.isAssignableFrom( type ) )
{
Collection<Object> c = (Collection<Object>) fields[i].get( target );
if ( c != null && !c.isEmpty() )
{
List<Object> originalValues = new ArrayList<Object>( c );
try
{
c.clear();
}
catch( UnsupportedOperationException e )
{
continue;
}
for ( Object value : originalValues )
{
if ( value != null )
{
if( String.class == value.getClass() )
{
String interpolated = modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors );
if ( !interpolated.equals( value ) )
{
c.add( interpolated );
}
else
{
c.add( value );
}
}
else
{
c.add( value );
if ( value.getClass().isArray() )
{
evaluateArray( value );
}
else
{
interpolationTargets.add( value );
}
}
}
else
{
// add the null back in...not sure what else to do...
c.add( value );
}
}
}
}
else if ( Map.class.isAssignableFrom( type ) )
{
Map<Object, Object> m = (Map<Object, Object>) fields[i].get( target );
if ( m != null && !m.isEmpty() )
{
for ( Map.Entry<Object, Object> entry : m.entrySet() )
{
Object value = entry.getValue();
if ( value != null )
{
if( String.class == value.getClass() )
{
String interpolated = modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors );
if ( !interpolated.equals( value ) )
{
try
{
entry.setValue( interpolated );
}
catch( UnsupportedOperationException e )
{
continue;
}
}
}
else
{
if ( value.getClass().isArray() )
{
evaluateArray( value );
}
else
{
interpolationTargets.add( value );
}
}
}
}
}
}
else
{
Object value = fields[i].get( target );
if ( value != null )
{
if ( fields[i].getType().isArray() )
{
evaluateArray( value );
}
else
{
interpolationTargets.add( value );
}
}
}
}
catch ( IllegalArgumentException e )
{
throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i] + " on class: " + cls.getName(), e );
}
catch ( IllegalAccessException e )
{
throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i] + " on class: " + cls.getName(), e );
}
}
finally
{
fields[i].setAccessible( isAccessible );
}
}
}
traverseObjectWithParents( cls.getSuperclass(), target );
}
}
private boolean isQualifiedForInterpolation( Class<?> cls )
{
return !cls.getPackage().getName().startsWith( "java" );
}
private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType )
{
if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) )
{
fieldIsPrimitiveByClass.put( fieldType, Boolean.valueOf( fieldType.isPrimitive() ) );
}
if ( fieldIsPrimitiveByClass.get( fieldType ).booleanValue() )
{
return false;
}
// if ( fieldType.isPrimitive() )
// {
// return false;
// }
if ( "parent".equals( field.getName() ) )
{
return false;
}
return true;
}
private void evaluateArray( Object target )
throws ModelInterpolationException
{
int len = Array.getLength( target );
for( int i = 0; i < len; i++ )
{
Object value = Array.get( target, i );
if ( value != null )
{
if ( String.class == value.getClass() )
{
String interpolated = modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors );
if ( !interpolated.equals( value ) )
{
Array.set( target, i, interpolated );
}
}
else
{
interpolationTargets.add( value );
}
}
}
}
}
}

View File

@ -1,710 +0,0 @@
package org.apache.maven.model.interpolator;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.io.ModelWriter;
import org.apache.maven.model.path.ModelPathTranslator;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.xml.pull.MXParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
@Component(role = Interpolator.class)
public class DefaultInterpolator
implements Interpolator
{
@Requirement
private ModelReader modelReader;
@Requirement
private ModelWriter modelWriter;
@Requirement
private ModelPathTranslator modelPathTranslator;
public Model interpolateModel( Model model, Properties properties, File projectDirectory )
throws IOException
{
if ( model == null )
{
throw new IllegalArgumentException( "model: null" );
}
if(properties == null)
{
properties = new Properties();
}
List<InterpolatorProperty> interpolatorProperties = new ArrayList<InterpolatorProperty>();
for ( Entry<Object, Object> e : properties.entrySet() )
{
interpolatorProperties.add( new InterpolatorProperty( "${" + e.getKey() + "}", (String) e.getValue(),
PomInterpolatorTag.EXECUTION_PROPERTIES.toString() ) );
}
if ( !containsProjectVersion( interpolatorProperties ) )
{
aliases.put( "\\$\\{project.version\\}", "\\$\\{version\\}" );
}
//TODO: Insert customized logic for parsing
List<ModelProperty> modelProperties = getModelProperties( model );
if ( "jar".equals( model.getPackaging() ) )
{
modelProperties.add( new ModelProperty( ProjectUri.packaging, "jar" ) );
}
List<ModelProperty> firstPassModelProperties = new ArrayList<ModelProperty>();
List<ModelProperty> secondPassModelProperties = new ArrayList<ModelProperty>();
ModelProperty buildProperty = new ModelProperty( ProjectUri.Build.xUri, null );
for ( ModelProperty mp : modelProperties )
{
if ( mp.getValue() != null && !mp.getUri().contains( "#property" ) && !mp.getUri().contains( "#collection" ) )
{
if ( ( !buildProperty.isParentOf( mp ) && !mp.getUri().equals( ProjectUri.Reporting.outputDirectory ) || mp.getUri().equals( ProjectUri.Build.finalName ) ) )
{
firstPassModelProperties.add( mp );
}
else
{
secondPassModelProperties.add( mp );
}
}
}
List<InterpolatorProperty> standardInterpolatorProperties = new ArrayList<InterpolatorProperty>();
String basedir = null;
if ( projectDirectory != null )
{
basedir = projectDirectory.getAbsolutePath();
standardInterpolatorProperties.add( new InterpolatorProperty( "${project.basedir}", basedir,
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
standardInterpolatorProperties.add( new InterpolatorProperty( "${basedir}", basedir,
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
standardInterpolatorProperties.add( new InterpolatorProperty( "${pom.basedir}", basedir,
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
String baseuri = projectDirectory.toURI().toString();
standardInterpolatorProperties.add( new InterpolatorProperty( "${project.baseUri}", baseuri,
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
standardInterpolatorProperties.add( new InterpolatorProperty( "${pom.baseUri}", baseuri,
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
}
for ( ModelProperty mp : modelProperties )
{
if ( mp.getUri().startsWith( ProjectUri.properties ) && mp.getValue() != null )
{
String uri = mp.getUri();
standardInterpolatorProperties.add( new InterpolatorProperty( "${" + uri.substring( uri.lastIndexOf( "/" ) + 1, uri.length() ) + "}", mp.getValue(),
PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
}
}
// FIRST PASS - Withhold using build directories as interpolator
// properties
List<InterpolatorProperty> ips1 = new ArrayList<InterpolatorProperty>( interpolatorProperties );
ips1.addAll( standardInterpolatorProperties );
ips1.addAll( createInterpolatorProperties( firstPassModelProperties, ProjectUri.baseUri, aliases, PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
Collections.sort( ips1, new Comparator<InterpolatorProperty>()
{
public int compare( InterpolatorProperty o, InterpolatorProperty o1 )
{
if ( o.getTag() == null || o1.getTag() == null )
{
return 0;
}
return PomInterpolatorTag.valueOf( o.getTag() ).compareTo( PomInterpolatorTag.valueOf( o1.getTag() ) );
}
} );
interpolateModelProperties( modelProperties, ips1 );
Map<ModelProperty, ModelProperty> buildDirectories = new HashMap<ModelProperty, ModelProperty>();
for ( ModelProperty mp : secondPassModelProperties )
{
if ( mp.getUri().startsWith( ProjectUri.Build.xUri ) || mp.getUri().equals( ProjectUri.Reporting.outputDirectory ) )
{
File file = new File( mp.getResolvedValue() );
if ( !file.isAbsolute() && !mp.getResolvedValue().startsWith( "${project.build." )
&& !mp.getResolvedValue().equals( "${project.basedir}" ) && basedir != null )
{
buildDirectories.put( mp, new ModelProperty( mp.getUri(), new File( basedir, file.getPath() ).getAbsolutePath() ) );
}
}
}
for ( Map.Entry<ModelProperty, ModelProperty> e : buildDirectories.entrySet() )
{
secondPassModelProperties.remove( e.getKey() );
secondPassModelProperties.add( e.getValue() );
}
// THIRD PASS - Use build directories as interpolator properties
List<InterpolatorProperty> ips2 = new ArrayList<InterpolatorProperty>( interpolatorProperties );
ips2.addAll( standardInterpolatorProperties );
ips2.addAll( createInterpolatorProperties( secondPassModelProperties, ProjectUri.baseUri, aliases, PomInterpolatorTag.PROJECT_PROPERTIES.name() ) );
ips2.addAll( interpolatorProperties );
Collections.sort( ips2, new Comparator<InterpolatorProperty>()
{
public int compare( InterpolatorProperty o, InterpolatorProperty o1 )
{
if ( o.getTag() == null || o1.getTag() == null )
{
return 0;
}
return PomInterpolatorTag.valueOf( o.getTag() ).compareTo( PomInterpolatorTag.valueOf( o1.getTag() ) );
}
} );
interpolateModelProperties( modelProperties, ips2 );
try
{
String xml = unmarshalModelPropertiesToXml( modelProperties, ProjectUri.baseUri );
Model m = modelReader.read( new StringReader( xml ), null );
modelPathTranslator.alignToBaseDirectory( m, projectDirectory );
return m;
}
catch ( IOException e )
{
throw new IllegalStateException( "Unmarshalling of model properties failed", e );
}
}
private static void interpolateModelProperties( List<ModelProperty> modelProperties, List<InterpolatorProperty> interpolatorProperties )
{
if ( modelProperties == null )
{
throw new IllegalArgumentException( "modelProperties: null" );
}
if ( interpolatorProperties == null )
{
throw new IllegalArgumentException( "interpolatorProperties: null" );
}
List<ModelProperty> unresolvedProperties = new ArrayList<ModelProperty>();
for ( ModelProperty mp : modelProperties )
{
if ( !mp.isResolved() )
{
unresolvedProperties.add( mp );
}
}
LinkedHashSet<InterpolatorProperty> ips = new LinkedHashSet<InterpolatorProperty>();
ips.addAll( interpolatorProperties );
boolean continueInterpolation = true;
while ( continueInterpolation )
{
continueInterpolation = false;
for ( InterpolatorProperty ip : ips )
{
for ( ModelProperty mp : unresolvedProperties )
{
if ( mp.resolveWith( ip ) && !continueInterpolation )
{
continueInterpolation = true;
break;
}
}
}
}
}
private static List<InterpolatorProperty> createInterpolatorProperties( List<ModelProperty> modelProperties, String baseUriForModel, Map<String, String> aliases, String interpolatorTag )
{
if ( modelProperties == null )
{
throw new IllegalArgumentException( "modelProperties: null" );
}
if ( baseUriForModel == null )
{
throw new IllegalArgumentException( "baseUriForModel: null" );
}
List<InterpolatorProperty> interpolatorProperties = new ArrayList<InterpolatorProperty>();
for ( ModelProperty mp : modelProperties )
{
InterpolatorProperty ip = mp.asInterpolatorProperty( baseUriForModel );
if ( ip != null )
{
ip.setTag( interpolatorTag );
interpolatorProperties.add( ip );
for ( Map.Entry<String, String> a : aliases.entrySet() )
{
interpolatorProperties.add( new InterpolatorProperty( ip.getKey().replaceAll( a.getKey(), a.getValue() ), ip.getValue().replaceAll( a.getKey(), a.getValue() ), interpolatorTag ) );
}
}
}
List<InterpolatorProperty> ips = new ArrayList<InterpolatorProperty>();
for ( InterpolatorProperty ip : interpolatorProperties )
{
if ( !ips.contains( ip ) )
{
ips.add( ip );
}
}
return ips;
}
private List<ModelProperty> getModelProperties( Model model )
throws IOException
{
StringWriter writer = new StringWriter();
modelWriter.write( writer, null, model );
Set<String> s = new HashSet<String>();
//TODO: Should add all collections from ProjectUri
s.addAll( URIS );
s.add( ProjectUri.Build.PluginManagement.Plugins.Plugin.Executions.xUri );
s.add( ProjectUri.DependencyManagement.Dependencies.Dependency.Exclusions.xUri );
s.add( ProjectUri.Dependencies.Dependency.Exclusions.xUri );
s.add( ProjectUri.Build.Plugins.Plugin.Executions.xUri );
s.add( ProjectUri.Build.Plugins.Plugin.Executions.Execution.Goals.xURI );
s.add( ProjectUri.Reporting.Plugins.Plugin.ReportSets.xUri );
s.add( ProjectUri.Reporting.Plugins.Plugin.ReportSets.ReportSet.configuration );
s.add( ProjectUri.Build.Plugins.Plugin.Executions.Execution.configuration );
//TODO: More profile info
s.add( ProjectUri.Profiles.Profile.Build.PluginManagement.Plugins.Plugin.Executions.xUri );
s.add( ProjectUri.Profiles.Profile.DependencyManagement.Dependencies.Dependency.Exclusions.xUri );
s.add( ProjectUri.Profiles.Profile.Dependencies.Dependency.Exclusions.xUri );
s.add( ProjectUri.Profiles.Profile.Build.Plugins.Plugin.Executions.xUri );
s.add( ProjectUri.Profiles.Profile.Build.Plugins.Plugin.Executions.Execution.Goals.xURI );
s.add( ProjectUri.Profiles.Profile.Reporting.Plugins.Plugin.ReportSets.xUri );
s.add( ProjectUri.Profiles.Profile.Reporting.Plugins.Plugin.ReportSets.ReportSet.configuration );
s.add( ProjectUri.Profiles.Profile.Build.Plugins.Plugin.Executions.Execution.configuration );
s.add( ProjectUri.Profiles.Profile.properties );
s.add( ProjectUri.Profiles.Profile.modules );
s.add( ProjectUri.Profiles.Profile.Dependencies.xUri );
s.add( ProjectUri.Profiles.Profile.Build.Plugins.Plugin.configuration );
return new ArrayList<ModelProperty>( marshallXmlToModelProperties( new ByteArrayInputStream(writer.toString().getBytes( "UTF-8" )), ProjectUri.baseUri, s ) );
}
/**
* Returns XML string unmarshalled from the specified list of model properties
*
* @param modelProperties the model properties to unmarshal. May not be null or empty
* @param baseUri the base uri of every model property. May not be null or empty.
* @return XML string unmarshalled from the specified list of model properties
* @throws IOException if there was a problem with unmarshalling
*/
private static String unmarshalModelPropertiesToXml( List<ModelProperty> modelProperties, String baseUri )
throws IOException
{
if ( modelProperties == null || modelProperties.isEmpty() )
{
throw new IllegalArgumentException( "modelProperties: null or empty" );
}
if ( baseUri == null || baseUri.trim().length() == 0 )
{
throw new IllegalArgumentException( "baseUri: null or empty" );
}
final int basePosition = baseUri.length();
StringBuffer sb = new StringBuffer();
List<String> lastUriTags = new ArrayList<String>();
for ( ModelProperty mp : modelProperties )
{
String uri = mp.getUri();
if ( uri.contains( "#property" ) )
{
continue;
}
if ( !uri.startsWith( baseUri ) )
{
throw new IllegalArgumentException( "Passed in model property that does not match baseUri: Property URI = " + uri + ", Base URI = " + baseUri );
}
List<String> tagNames = getTagNamesFromUri( basePosition, uri );
for ( int i = lastUriTags.size() - 1; i >= 0 && i >= tagNames.size() - 1; i-- )
{
sb.append( toEndTag( lastUriTags.get( i ) ) );
}
String tag = tagNames.get( tagNames.size() - 1 );
List<ModelProperty> attributes = new ArrayList<ModelProperty>();
for ( int peekIndex = modelProperties.indexOf( mp ) + 1; peekIndex < modelProperties.size(); peekIndex++ )
{
if ( peekIndex <= modelProperties.size() - 1 )
{
ModelProperty peekProperty = modelProperties.get( peekIndex );
if ( peekProperty.getUri().contains( "#property" ) )
{
attributes.add( peekProperty );
}
else
{
break;
}
}
else
{
break;
}
}
sb.append( toStartTag( tag, attributes ) );
if ( mp.getResolvedValue() != null )
{
sb.append( mp.getResolvedValue() );
}
lastUriTags = tagNames;
}
for ( int i = lastUriTags.size() - 1; i >= 1; i-- )
{
sb.append( toEndTag( lastUriTags.get( i ) ) );
}
return sb.toString();
}
/**
* Returns list of tag names parsed from the specified uri. All #collection parts of the tag are
* removed from the tag names.
*
* @param basePosition the base position in the specified URI to start the parse
* @param uri the uri to parse for tag names
* @return list of tag names parsed from the specified uri
*/
private static List<String> getTagNamesFromUri( int basePosition, String uri )
{
return Arrays.asList( uri.substring( basePosition ).replaceAll( "#collection", "" ).replaceAll( "#set", "" ).split( "/" ) );
}
/**
* Returns the XML formatted start tag for the specified value and the specified attribute.
*
* @param value the value to use for the start tag
* @param attributes the attribute to use in constructing of start tag
* @return the XML formatted start tag for the specified value and the specified attribute
*/
private static String toStartTag( String value, List<ModelProperty> attributes )
{
StringBuffer sb = new StringBuffer(); //TODO: Support more than one attribute
sb.append( "\r\n<" ).append( value );
if ( attributes != null )
{
for ( ModelProperty attribute : attributes )
{
sb.append( " " ).append( attribute.getUri().substring( attribute.getUri().indexOf( "#property/" ) + 10 ) ).append( "=\"" ).append( attribute.getResolvedValue() ).append( "\" " );
}
}
sb.append( ">" );
return sb.toString();
}
/**
* Returns XML formatted end tag for the specified value.
*
* @param value the value to use for the end tag
* @return xml formatted end tag for the specified value
*/
private static String toEndTag( String value )
{
if ( value.trim().length() == 0 )
{
return "";
}
StringBuffer sb = new StringBuffer();
sb.append( "</" ).append( value ).append( ">" );
return sb.toString();
}
private static final Set<String> URIS = Collections.unmodifiableSet( new HashSet<String>( Arrays.asList( ProjectUri.Build.Extensions.xUri, ProjectUri.Build.PluginManagement.Plugins.xUri,
ProjectUri.Build.PluginManagement.Plugins.Plugin.configuration,
ProjectUri.Build.PluginManagement.Plugins.Plugin.Executions.xUri,
ProjectUri.Build.PluginManagement.Plugins.Plugin.Executions.Execution.Goals.xURI,
ProjectUri.Build.PluginManagement.Plugins.Plugin.Dependencies.xUri,
ProjectUri.Build.PluginManagement.Plugins.Plugin.Dependencies.Dependency.Exclusions.xUri,
ProjectUri.Build.Plugins.xUri, ProjectUri.properties,
ProjectUri.Build.Plugins.Plugin.configuration, ProjectUri.Reporting.Plugins.xUri,
ProjectUri.Reporting.Plugins.Plugin.configuration,
ProjectUri.Build.Plugins.Plugin.Dependencies.xUri, ProjectUri.Build.Resources.xUri,
ProjectUri.Build.Resources.Resource.includes,
ProjectUri.Build.Resources.Resource.excludes, ProjectUri.Build.TestResources.xUri,
ProjectUri.Build.Filters.xUri, ProjectUri.CiManagement.Notifiers.xUri,
ProjectUri.Contributors.xUri, ProjectUri.Dependencies.xUri,
ProjectUri.DependencyManagement.Dependencies.xUri, ProjectUri.Developers.xUri,
ProjectUri.Developers.Developer.roles, ProjectUri.Licenses.xUri,
ProjectUri.MailingLists.xUri, ProjectUri.Modules.xUri, ProjectUri.PluginRepositories.xUri,
ProjectUri.Profiles.xUri, ProjectUri.Profiles.Profile.Build.Plugins.xUri,
ProjectUri.Profiles.Profile.Build.Plugins.Plugin.Dependencies.xUri,
ProjectUri.Profiles.Profile.Build.Plugins.Plugin.Executions.xUri,
ProjectUri.Profiles.Profile.Build.Resources.xUri,
ProjectUri.Profiles.Profile.Build.TestResources.xUri,
ProjectUri.Profiles.Profile.Dependencies.xUri,
ProjectUri.Profiles.Profile.DependencyManagement.Dependencies.xUri,
ProjectUri.Profiles.Profile.PluginRepositories.xUri,
ProjectUri.Profiles.Profile.Reporting.Plugins.xUri,
ProjectUri.Profiles.Profile.Repositories.xUri,
ProjectUri.Profiles.Profile.Build.PluginManagement.Plugins.xUri,
ProjectUri.Profiles.Profile.Build.PluginManagement.Plugins.Plugin.Dependencies.xUri,
ProjectUri.Reporting.Plugins.xUri, ProjectUri.Repositories.xUri ) ) );
/**
* Returns list of model properties transformed from the specified input stream.
*
* @param inputStream input stream containing the xml document. May not be null.
* @param baseUri the base uri of every model property. May not be null or empty.
* @param collections set of uris that are to be treated as a collection (multiple entries). May
* be null.
* @return list of model properties transformed from the specified input stream.
* @throws IOException if there was a problem doing the transform
*/
private static List<ModelProperty> marshallXmlToModelProperties( InputStream inputStream, String baseUri, Set<String> collections )
throws IOException
{
if ( inputStream == null )
{
throw new IllegalArgumentException( "inputStream: null" );
}
if ( baseUri == null || baseUri.trim().length() == 0 )
{
throw new IllegalArgumentException( "baseUri: null" );
}
if ( collections == null )
{
collections = Collections.emptySet();
}
List<ModelProperty> modelProperties = new ArrayList<ModelProperty>();
Uri uri = new Uri( baseUri );
String tagName = baseUri;
StringBuilder tagValue = new StringBuilder( 256 );
int depth = 0;
int depthOfTagValue = depth;
Reader reader = null;
try
{
reader = ReaderFactory.newXmlReader( inputStream );
XmlPullParser parser = new MXParser();
parser.setInput( reader );
Map<String, String> attributes = new HashMap<String, String>();
for ( int type = parser.getEventType();; type = parser.next() )
{
switch ( type )
{
case XmlPullParser.TEXT:
{
if ( depth == depthOfTagValue )
{
tagValue.append( parser.getText() );
}
break;
}
case XmlPullParser.START_TAG:
{
if ( !tagName.equals( baseUri ) )
{
String value = null;
if ( depth < depthOfTagValue )
{
value = tagValue.toString().trim();
}
modelProperties.add( new ModelProperty( tagName, value ) );
if ( !attributes.isEmpty() )
{
for ( Map.Entry<String, String> e : attributes.entrySet() )
{
modelProperties.add( new ModelProperty( e.getKey(), e.getValue() ) );
}
attributes.clear();
}
}
depth++;
tagName = uri.getUriFor( parser.getName(), depth );
if ( collections.contains( tagName + "#collection" ) )
{
tagName = tagName + "#collection";
uri.addTag( parser.getName() + "#collection" );
}
else if ( collections.contains( tagName + "#set" ) )
{
tagName = tagName + "#set";
uri.addTag( parser.getName() + "#set" );
}
else
{
uri.addTag( parser.getName() );
}
tagValue.setLength( 0 );
depthOfTagValue = depth;
for ( int i = 0; i < parser.getAttributeCount(); i++ )
{
attributes.put( tagName + "#property/" + parser.getAttributeName( i ),
parser.getAttributeValue( i ) );
}
break;
}
case XmlPullParser.END_TAG:
{
depth--;
break;
}
case XmlPullParser.END_DOCUMENT:
{
modelProperties.add( new ModelProperty( tagName, tagValue.toString().trim() ) );
if ( !attributes.isEmpty() )
{
for ( Map.Entry<String, String> e : attributes.entrySet() )
{
modelProperties.add( new ModelProperty( e.getKey(), e.getValue() ) );
}
}
return modelProperties;
}
}
}
}
catch ( XmlPullParserException e )
{
throw (IOException) new IOException( "Failed to parser POM:" + e.toString() ).initCause( e );
}
finally
{
IOUtil.close( reader );
}
}
private static final Map<String, String> aliases = new HashMap<String, String>();
private static void addProjectAlias( String element, boolean leaf )
{
String suffix = leaf ? "\\}" : "\\.";
aliases.put( "\\$\\{project\\." + element + suffix, "\\$\\{" + element + suffix );
}
static
{
aliases.put( "\\$\\{project\\.", "\\$\\{pom\\." );
addProjectAlias( "modelVersion", true );
addProjectAlias( "groupId", true );
addProjectAlias( "artifactId", true );
addProjectAlias( "version", true );
addProjectAlias( "packaging", true );
addProjectAlias( "name", true );
addProjectAlias( "description", true );
addProjectAlias( "inceptionYear", true );
addProjectAlias( "url", true );
addProjectAlias( "parent", false );
addProjectAlias( "prerequisites", false );
addProjectAlias( "organization", false );
addProjectAlias( "build", false );
addProjectAlias( "reporting", false );
addProjectAlias( "scm", false );
addProjectAlias( "distributionManagement", false );
addProjectAlias( "issueManagement", false );
addProjectAlias( "ciManagement", false );
}
private static boolean containsProjectVersion( List<InterpolatorProperty> interpolatorProperties )
{
InterpolatorProperty versionInterpolatorProperty = new ModelProperty( ProjectUri.version, "" ).asInterpolatorProperty( ProjectUri.baseUri );
for ( InterpolatorProperty ip : interpolatorProperties )
{
if ( ip.equals( versionInterpolatorProperty ) )
{
return true;
}
}
return false;
}
/**
* Class for storing information about URIs.
*/
private static class Uri
{
List<String> uris;
Uri( String baseUri )
{
uris = new LinkedList<String>();
uris.add( baseUri );
}
String getUriFor( String tag, int depth )
{
setUrisToDepth( depth );
StringBuffer sb = new StringBuffer();
for ( String tagName : uris )
{
sb.append( tagName ).append( "/" );
}
sb.append( tag );
return sb.toString();
}
void addTag( String tag )
{
uris.add( tag );
}
void setUrisToDepth( int depth )
{
uris = new LinkedList<String>( uris.subList( 0, depth ) );
}
}
private static enum PomInterpolatorTag
{
PROJECT_PROPERTIES,
EXECUTION_PROPERTIES
}
}

View File

@ -1,13 +0,0 @@
package org.apache.maven.model.interpolator;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import org.apache.maven.model.Model;
public interface Interpolator
{
Model interpolateModel( Model model, Properties properties, File projectDirectory )
throws IOException;
}

View File

@ -1,145 +0,0 @@
package org.apache.maven.model.interpolator;
/*
* 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.
*/
/**
* Provides interpolator property information.
*/
final class InterpolatorProperty
{
/**
* The key (or name) of the property
*/
private final String key;
/**
* The value of the property
*/
private final String value;
/**
* Metadata tag (general use)
*/
private String tag;
/**
* Constructor
*
* @param key the key (or name) of the property. May not be null
* @param value the value of the property. May not be null.
*/
public InterpolatorProperty( String key, String value )
{
this(key, value, null);
}
public InterpolatorProperty( String key, String value, String tag )
{
if ( key == null )
{
throw new IllegalArgumentException( "key: null" );
}
if ( value == null )
{
throw new IllegalArgumentException( "value: null" );
}
this.key = key;
this.value = value;
this.tag = tag;
}
/**
* Returns key (or name) of property.
*
* @return key (or name) of property
*/
public String getKey()
{
return key;
}
/**
* Returns value of property.
*
* @return value of property
*/
public String getValue()
{
return value;
}
public String getTag()
{
return tag;
}
public void setTag(String tag)
{
this.tag = tag;
}
/**
* Returns true if key values match, otherwise returns false.
*
* @param o interpolator property to compare
* @return true if key values match, otherwise returns false
*/
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( o == null || getClass() != o.getClass() )
{
return false;
}
InterpolatorProperty that = (InterpolatorProperty) o;
if ( !key.equals( that.key ) )
{
return false;
}
return true;
}
/**
* Returns hash code of interpolator property key.
*
* @return hash code of interpolator property key
*/
public int hashCode()
{
return key.hashCode();
}
public String toString()
{
return "Key = " + key + ", Value = " + value + ", Hash = " +
this.hashCode();
}
}

View File

@ -1,255 +0,0 @@
package org.apache.maven.model.interpolator;
/*
* 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.
*/
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Maps a URI to a string value, which may be null. This class is immutable.
*/
final class ModelProperty
{
/**
* A pattern used for finding pom, project and env properties
*/
private static final Pattern EXPRESSION_PATTERN = Pattern.compile( "\\$\\{(pom\\.|project\\.|env\\.)?([^}]+)\\}" );
/**
* URI of the resource
*/
private final String uri;
/**
* Value associated with the uri
*/
private final String value;
/**
* The count of '/' within this model property's uri, which is the depth of its XML nodes.
*/
private final int depth;
/**
* Value of this model property after interpolation
*/
private String resolvedValue;
/**
* List of unresolved expressions within this model property's value
*/
private final List<String> unresolvedExpressions;
/**
* Constructor
*
* @param uri URI of the resource. May not be null
* @param value Value associated with specified uri. Value may be null if uri does not map to primitive type.
*/
public ModelProperty( String uri, String value )
{
if ( uri == null )
{
throw new IllegalArgumentException( "uri" );
}
this.uri = uri;
this.value = value;
resolvedValue = value;
unresolvedExpressions = new ArrayList<String>();
if ( value != null )
{
Matcher matcher = EXPRESSION_PATTERN.matcher( value );
while ( matcher.find() )
{
unresolvedExpressions.add( matcher.group( 0 ) );
}
}
String uriWithoutProperty;
int index = uri.lastIndexOf( "/" );
if(index > -1) {
uriWithoutProperty = uri.substring( 0, uri.lastIndexOf( "/" ) );
if(uriWithoutProperty.endsWith("#property") || uriWithoutProperty.endsWith("combine.children") )
{
uriWithoutProperty = uriWithoutProperty.substring( 0, uriWithoutProperty.lastIndexOf( "/" ) );
}
}
else
{
uriWithoutProperty = uri;
}
depth = uriWithoutProperty.split( "/" ).length;
}
/**
* Returns URI key
*
* @return URI key
*/
public String getUri()
{
return uri;
}
/**
* Returns value for the URI key. Value may be null.
*
* @return value for the URI key. Value may be null
*/
public String getValue()
{
return value;
}
/**
* Value of this model property after interpolation. CDATA section will be added if needed.
*
* @return value of this model property after interpolation
*/
public String getResolvedValue()
{
if( !uri.contains("#property") && resolvedValue != null && !resolvedValue.startsWith ("<![CDATA[")
&& (resolvedValue.contains( "=" ) || resolvedValue.contains( "<" ) || resolvedValue.contains( "&" )))
{
resolvedValue = "<![CDATA[" + resolvedValue + "]]>";
}
return resolvedValue;
}
/**
* Returns true if model property is completely interpolated, otherwise returns false.
*
* @return true if model property is completely interpolated, otherwise returns false
*/
public boolean isResolved()
{
return unresolvedExpressions.isEmpty();
}
/**
* Returns copy of the uninterpolated model property
*
* @return copy of the uninterpolated model property
*/
public ModelProperty createCopyOfOriginal()
{
return new ModelProperty( uri, value );
}
/**
* Returns the count of '/' within this model property's uri, which is the depth of its XML nodes.
*
* @return the count of '/' within this model property's uri, which is the depth of its XML nodes
*/
public int getDepth()
{
return depth;
}
/**
* Returns true if this model property is a direct parent of the specified model property, otherwise returns false.
*
* @param modelProperty the model property
* @return true if this model property is a direct parent of the specified model property, otherwise returns false
*/
public boolean isParentOf( ModelProperty modelProperty )
{
if ( Math.abs( depth - modelProperty.getDepth() ) > 1 )
{
return false;
}
if ( uri.equals( modelProperty.getUri() ) || uri.startsWith( modelProperty.getUri() ) )
{
return false;
}
return ( modelProperty.getUri().startsWith( uri ) );
}
/**
* Returns this model property as an interpolator property, allowing the interpolation of model elements within
* other model elements.
*
* @param baseUri the base uri of the model property
* @return this model property as an interpolator property, allowing the interpolation of model elements within
* other model elements
*/
public InterpolatorProperty asInterpolatorProperty( String baseUri )
{
if ( uri.contains( "#collection" ) || uri.contains("#set") || value == null )
{
return null;
}
String key = "${" + uri.replace( baseUri + "/", "" ).replace( "/", "." ) + "}";
return new InterpolatorProperty( key, value );
}
/**
* Resolves any unresolved model property expressions using the specified interpolator property
*
* @param property the interpolator property used to resolve
*/
public boolean resolveWith( InterpolatorProperty property )
{
if ( property == null )
{
throw new IllegalArgumentException( "property: null" );
}
if ( isResolved() )
{
return false;
}
boolean resolved = false;
for ( String expression : unresolvedExpressions )
{
if ( property.getKey().equals( expression ) )
{
resolved = true;
resolvedValue = resolvedValue.replace( property.getKey(), property.getValue() );
unresolvedExpressions.clear();
Matcher matcher = EXPRESSION_PATTERN.matcher( resolvedValue );
while ( matcher.find() )
{
unresolvedExpressions.add( matcher.group( 0 ) );
}
break;
}
}
return resolved;
}
public String toCode() {
String val = (value != null) ? "\"" + value + "\"" : null;
return "mpz.add(new ModelProperty(\"" + uri + "\", " + val +"));";
}
public String toString()
{
return "Uri = " + uri + ", Value = " + value + ", Resolved Value = " + resolvedValue + ", Hash = " +
this.hashCode();
}
}

View File

@ -52,7 +52,7 @@ under the License.
<easyMockVersion>1.2_Java1.3</easyMockVersion>
<junitVersion>3.8.1</junitVersion>
<plexusVersion>1.0-beta-3.0.8-SNAPSHOT</plexusVersion>
<plexusInterpolationVersion>1.1</plexusInterpolationVersion>
<plexusInterpolationVersion>1.8.1</plexusInterpolationVersion>
<plexusPluginManagerVersion>1.0-alpha-1</plexusPluginManagerVersion>
<plexusUtilsVersion>1.5.8</plexusUtilsVersion>
<plexusJetty6Version>1.6</plexusJetty6Version>