Coverage Report - org.apache.maven.project.interpolation.AbstractStringBasedModelInterpolator
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractStringBasedModelInterpolator
80 %
83/104
54 %
15/28
0
AbstractStringBasedModelInterpolator$1
100 %
4/4
100 %
4/4
0
AbstractStringBasedModelInterpolator$2
75 %
3/4
75 %
3/4
0
AbstractStringBasedModelInterpolator$3
100 %
2/2
N/A
0
 
 1  
 package org.apache.maven.project.interpolation;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.maven.model.Model;
 23  
 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
 24  
 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
 25  
 import org.apache.maven.project.DefaultProjectBuilderConfiguration;
 26  
 import org.apache.maven.project.ProjectBuilderConfiguration;
 27  
 import org.apache.maven.project.path.PathTranslator;
 28  
 import org.codehaus.plexus.interpolation.AbstractValueSource;
 29  
 import org.codehaus.plexus.interpolation.InterpolationException;
 30  
 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
 31  
 import org.codehaus.plexus.interpolation.Interpolator;
 32  
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
 33  
 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
 34  
 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
 35  
 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
 36  
 import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper;
 37  
 import org.codehaus.plexus.interpolation.RecursionInterceptor;
 38  
 import org.codehaus.plexus.interpolation.ValueSource;
 39  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 40  
 import org.codehaus.plexus.logging.Logger;
 41  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
 42  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
 43  
 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 44  
 
 45  
 import java.io.File;
 46  
 import java.io.IOException;
 47  
 import java.io.StringReader;
 48  
 import java.io.StringWriter;
 49  
 import java.util.ArrayList;
 50  
 import java.util.Arrays;
 51  
 import java.util.Collections;
 52  
 import java.util.List;
 53  
 import java.util.Map;
 54  
 import java.util.Properties;
 55  
 
 56  
 /**
 57  
  * Use a regular expression search to find and resolve expressions within the POM.
 58  
  *
 59  
  * @author jdcasey Created on Feb 3, 2005
 60  
  * @version $Id: AbstractStringBasedModelInterpolator.java 767323 2009-04-21 22:53:31Z jdcasey $
 61  
  * @todo Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans.
 62  
  */
 63  
 public abstract class AbstractStringBasedModelInterpolator
 64  
     extends AbstractLogEnabled
 65  
     implements ModelInterpolator, Initializable
 66  
 {
 67  1
     private static final List<String> PROJECT_PREFIXES = Arrays.asList( new String[]{ "pom.", "project." } );
 68  
 
 69  
     private static final List<String> TRANSLATED_PATH_EXPRESSIONS;
 70  
 
 71  
     static
 72  
     {
 73  1
         List<String> translatedPrefixes = new ArrayList<String>();
 74  
 
 75  
         // MNG-1927, MNG-2124, MNG-3355:
 76  
         // If the build section is present and the project directory is non-null, we should make
 77  
         // sure interpolation of the directories below uses translated paths.
 78  
         // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the
 79  
         // code below...
 80  1
         translatedPrefixes.add( "build.directory" );
 81  1
         translatedPrefixes.add( "build.outputDirectory" );
 82  1
         translatedPrefixes.add( "build.testOutputDirectory" );
 83  1
         translatedPrefixes.add( "build.sourceDirectory" );
 84  1
         translatedPrefixes.add( "build.testSourceDirectory" );
 85  1
         translatedPrefixes.add( "build.scriptSourceDirectory" );
 86  1
         translatedPrefixes.add( "reporting.outputDirectory" );
 87  
 
 88  1
         TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes;
 89  1
     }
 90  
 
 91  
     private PathTranslator pathTranslator;
 92  
     
 93  
     private Interpolator interpolator;
 94  
     
 95  
     private RecursionInterceptor recursionInterceptor;
 96  
 
 97  
     // for testing.
 98  
     protected AbstractStringBasedModelInterpolator( PathTranslator pathTranslator )
 99  2
     {
 100  2
         this.pathTranslator = pathTranslator;
 101  2
     }
 102  
 
 103  
     /**
 104  
      * @todo: Remove the throws clause.
 105  
      * @throws IOException This exception is not thrown any more, and needs to be removed.
 106  
      */
 107  
     protected AbstractStringBasedModelInterpolator()
 108  96
     {
 109  96
     }
 110  
 
 111  
     public Model interpolate( Model model, Map<String, ?> context )
 112  
         throws ModelInterpolationException
 113  
     {
 114  26
         return interpolate( model, context, true );
 115  
     }
 116  
 
 117  
     /**
 118  
      * Serialize the inbound Model instance to a StringWriter, perform the regex replacement to resolve
 119  
      * POM expressions, then re-parse into the resolved Model instance.
 120  
      * <br/>
 121  
      * <b>NOTE:</b> This will result in a different instance of Model being returned!!!
 122  
      *
 123  
      * @param model   The inbound Model instance, to serialize and reference for expression resolution
 124  
      * @param context The other context map to be used during resolution
 125  
      * @return The resolved instance of the inbound Model. This is a different instance!
 126  
      *
 127  
      * @deprecated Use {@link ModelInterpolator#interpolate(Model, File, ProjectBuilderConfiguration, boolean)} instead.
 128  
      */
 129  
     public Model interpolate( Model model, Map<String, ?> context, boolean strict )
 130  
         throws ModelInterpolationException
 131  
     {
 132  26
         Properties props = new Properties();
 133  26
         props.putAll( context );
 134  
 
 135  26
         return interpolate( model,
 136  
                             null,
 137  
                             new DefaultProjectBuilderConfiguration().setExecutionProperties( props ),
 138  
                             true );
 139  
     }
 140  
 
 141  
     public Model interpolate( Model model,
 142  
                               File projectDir,
 143  
                               ProjectBuilderConfiguration config,
 144  
                               boolean debugEnabled )
 145  
         throws ModelInterpolationException
 146  
     {
 147  14
         StringWriter sWriter = new StringWriter( 1024 );
 148  
 
 149  14
         MavenXpp3Writer writer = new MavenXpp3Writer();
 150  
         try
 151  
         {
 152  14
             writer.write( sWriter, model );
 153  
         }
 154  0
         catch ( IOException e )
 155  
         {
 156  0
             throw new ModelInterpolationException( "Cannot serialize project model for interpolation.", e );
 157  14
         }
 158  
 
 159  14
         String serializedModel = sWriter.toString();
 160  14
         serializedModel = interpolate( serializedModel, model, projectDir, config, debugEnabled );
 161  
 
 162  13
         StringReader sReader = new StringReader( serializedModel );
 163  
 
 164  13
         MavenXpp3Reader modelReader = new MavenXpp3Reader();
 165  
         try
 166  
         {
 167  13
             model = modelReader.read( sReader );
 168  
         }
 169  0
         catch ( IOException e )
 170  
         {
 171  0
             throw new ModelInterpolationException(
 172  
                 "Cannot read project model from interpolating filter of serialized version.", e );
 173  
         }
 174  0
         catch ( XmlPullParserException e )
 175  
         {
 176  0
             throw new ModelInterpolationException(
 177  
                 "Cannot read project model from interpolating filter of serialized version.", e );
 178  13
         }
 179  
 
 180  13
         return model;
 181  
     }
 182  
 
 183  
     /**
 184  
      * Interpolates all expressions in the src parameter.
 185  
      * <p>
 186  
      * The algorithm used for each expression is:
 187  
      * <ul>
 188  
      *   <li>If it starts with either "pom." or "project.", the expression is evaluated against the model.</li>
 189  
      *   <li>If the value is null, get the value from the context.</li>
 190  
      *   <li>If the value is null, but the context contains the expression, don't replace the expression string
 191  
      *       with the value, and continue to find other expressions.</li>
 192  
      *   <li>If the value is null, get it from the model properties.</li>
 193  
      *   <li>
 194  
      * @param overrideContext
 195  
      * @param outputDebugMessages
 196  
      */
 197  
     public String interpolate( String src,
 198  
                                Model model,
 199  
                                final File projectDir,
 200  
                                ProjectBuilderConfiguration config,
 201  
                                boolean debug )
 202  
         throws ModelInterpolationException
 203  
     {
 204  
         try
 205  
         {
 206  375
             List<ValueSource> valueSources = createValueSources( model, projectDir, config );
 207  375
             List<InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config );
 208  
             
 209  375
             return interpolateInternal( src, valueSources, postProcessors, debug );
 210  
         }
 211  
         finally
 212  
         {
 213  374
             interpolator.clearAnswers();
 214  
         }
 215  
     }
 216  
     
 217  
     protected List<ValueSource> createValueSources( final Model model, final File projectDir, final ProjectBuilderConfiguration config )
 218  
     {
 219  523
         String timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT;
 220  
 
 221  523
         Properties modelProperties = model.getProperties();
 222  523
         if ( modelProperties != null )
 223  
         {
 224  523
             timestampFormat = modelProperties.getProperty( BUILD_TIMESTAMP_FORMAT_PROPERTY, timestampFormat );
 225  
         }
 226  
 
 227  523
         ValueSource modelValueSource1 = new PrefixedObjectValueSource( PROJECT_PREFIXES, model, false );
 228  523
         ValueSource modelValueSource2 = new ObjectBasedValueSource( model );
 229  
 
 230  523
         ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ){
 231  523
             public Object getValue( String expression )
 232  
             {
 233  1689
                 if ( projectDir != null && "basedir".equals( expression ) )
 234  
                 {
 235  495
                     return projectDir.getAbsolutePath();
 236  
                 }
 237  1194
                 return null;
 238  
             }
 239  
         },
 240  
         PROJECT_PREFIXES, true );
 241  523
         ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ){
 242  523
             public Object getValue( String expression )
 243  
             {
 244  1147
                 if ( projectDir != null && "baseUri".equals( expression ) )
 245  
                 {
 246  0
                     return projectDir.getAbsoluteFile().toURI().toString();
 247  
                 }
 248  1147
                 return null;
 249  
             }
 250  
         },
 251  
         PROJECT_PREFIXES, false );
 252  
         
 253  523
         List<ValueSource> valueSources = new ArrayList<ValueSource>( 9 );
 254  
         
 255  
         // NOTE: Order counts here!
 256  523
         valueSources.add( basedirValueSource );
 257  523
         valueSources.add( baseUriValueSource );
 258  523
         valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), timestampFormat ) );
 259  523
         valueSources.add( modelValueSource1 );
 260  523
         valueSources.add( new MapBasedValueSource( config.getUserProperties() ) );
 261  523
         valueSources.add( new MapBasedValueSource( modelProperties ) );
 262  523
         valueSources.add( new MapBasedValueSource( config.getExecutionProperties() ) );
 263  523
         valueSources.add( new AbstractValueSource( false )
 264  
         {
 265  523
             public Object getValue( String expression )
 266  
             {
 267  467
                 return config.getExecutionProperties().getProperty( "env." + expression );
 268  
             }
 269  
         } );
 270  523
         valueSources.add( modelValueSource2 );
 271  
         
 272  523
         return valueSources;
 273  
     }
 274  
     
 275  
     protected List<InterpolationPostProcessor> createPostProcessors( final Model model, final File projectDir,
 276  
                                                                      final ProjectBuilderConfiguration config )
 277  
     {
 278  523
         return Collections.singletonList( (InterpolationPostProcessor) new PathTranslatingPostProcessor(
 279  
                                                                                                          PROJECT_PREFIXES,
 280  
                                                                                                          TRANSLATED_PATH_EXPRESSIONS,
 281  
                                                                                                          projectDir,
 282  
                                                                                                          pathTranslator ) );
 283  
     }
 284  
     
 285  
     @SuppressWarnings("unchecked")
 286  
     protected String interpolateInternal( String src, List<ValueSource> valueSources,
 287  
                                           List<InterpolationPostProcessor> postProcessors, boolean debug )
 288  
         throws ModelInterpolationException
 289  
     {
 290  13510
         if ( src.indexOf( "${" ) < 0 )
 291  
         {
 292  12229
             return src;
 293  
         }
 294  
         
 295  1281
         Logger logger = getLogger();
 296  
 
 297  1281
         String result = src;
 298  1281
         synchronized( this )
 299  
         {
 300  
             
 301  1281
             for ( ValueSource vs : valueSources )
 302  
             {
 303  11529
                 interpolator.addValueSource( vs );
 304  
             }
 305  
             
 306  1281
             for ( InterpolationPostProcessor postProcessor : postProcessors )
 307  
             {
 308  1281
                 interpolator.addPostProcessor( postProcessor );
 309  
             }
 310  
 
 311  
             try
 312  
             {
 313  
                 try
 314  
                 {
 315  1281
                     result = interpolator.interpolate( result, recursionInterceptor );
 316  
                 }
 317  2
                 catch( InterpolationException e )
 318  
                 {
 319  2
                     throw new ModelInterpolationException( e.getMessage(), e );
 320  1279
                 }
 321  
 
 322  1279
                 if ( debug )
 323  
                 {
 324  49
                     List<Object> feedback = (List<Object>) interpolator.getFeedback();
 325  49
                     if ( feedback != null && !feedback.isEmpty() )
 326  
                     {
 327  0
                         logger.debug( "Maven encountered the following problems during initial POM interpolation:" );
 328  
 
 329  0
                         Object last = null;
 330  0
                         for ( Object next : feedback )
 331  
                         {
 332  0
                             if ( next instanceof Throwable )
 333  
                             {
 334  0
                                 if ( last == null )
 335  
                                 {
 336  0
                                     logger.debug( "", ( (Throwable) next ) );
 337  
                                 }
 338  
                                 else
 339  
                                 {
 340  0
                                     logger.debug( String.valueOf( last ), ( (Throwable) next ) );
 341  
                                 }
 342  
                             }
 343  
                             else
 344  
                             {
 345  0
                                 if ( last != null )
 346  
                                 {
 347  0
                                     logger.debug( String.valueOf( last ) );
 348  
                                 }
 349  
 
 350  0
                                 last = next;
 351  
                             }
 352  
                         }
 353  
 
 354  0
                         if ( last != null )
 355  
                         {
 356  0
                             logger.debug( String.valueOf( last ) );
 357  
                         }
 358  
                     }
 359  
                 }
 360  
 
 361  1279
                 interpolator.clearFeedback();
 362  
             }
 363  
             finally
 364  
             {
 365  1281
                 for ( ValueSource vs : valueSources )
 366  
                 {
 367  11529
                     interpolator.removeValuesSource( vs );
 368  
                 }
 369  
                 
 370  1281
                 for ( InterpolationPostProcessor postProcessor : postProcessors )
 371  
                 {
 372  1281
                     interpolator.removePostProcessor( postProcessor );
 373  
                 }
 374  1279
             }
 375  1279
         }
 376  
 
 377  1279
         return result;
 378  
     }
 379  
     
 380  
     protected RecursionInterceptor getRecursionInterceptor()
 381  
     {
 382  0
         return recursionInterceptor;
 383  
     }
 384  
 
 385  
     protected void setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
 386  
     {
 387  0
         this.recursionInterceptor = recursionInterceptor;
 388  0
     }
 389  
 
 390  
     protected abstract Interpolator createInterpolator();
 391  
 
 392  
     public void initialize()
 393  
         throws InitializationException
 394  
     {
 395  98
         interpolator = createInterpolator();
 396  98
         recursionInterceptor = new PrefixAwareRecursionInterceptor( PROJECT_PREFIXES );
 397  98
     }
 398  
     
 399  
     protected final Interpolator getInterpolator()
 400  
     {
 401  148
         return interpolator;
 402  
     }
 403  
 
 404  
 }