001package org.apache.maven.model.interpolation; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collection; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Properties; 029 030import org.apache.maven.model.Model; 031import org.apache.maven.model.building.ModelBuildingRequest; 032import org.apache.maven.model.building.ModelProblem.Severity; 033import org.apache.maven.model.building.ModelProblem.Version; 034import org.apache.maven.model.building.ModelProblemCollector; 035import org.apache.maven.model.building.ModelProblemCollectorRequest; 036import org.apache.maven.model.path.PathTranslator; 037import org.apache.maven.model.path.UrlNormalizer; 038import org.codehaus.plexus.component.annotations.Requirement; 039import org.codehaus.plexus.interpolation.AbstractValueSource; 040import org.codehaus.plexus.interpolation.InterpolationException; 041import org.codehaus.plexus.interpolation.InterpolationPostProcessor; 042import org.codehaus.plexus.interpolation.Interpolator; 043import org.codehaus.plexus.interpolation.MapBasedValueSource; 044import org.codehaus.plexus.interpolation.ObjectBasedValueSource; 045import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor; 046import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; 047import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper; 048import org.codehaus.plexus.interpolation.RecursionInterceptor; 049import org.codehaus.plexus.interpolation.ValueSource; 050 051/** 052 * Use a regular expression search to find and resolve expressions within the POM. 053 * 054 * @author jdcasey Created on Feb 3, 2005 055 */ 056public abstract class AbstractStringBasedModelInterpolator 057 implements ModelInterpolator 058{ 059 private static final List<String> PROJECT_PREFIXES = Arrays.asList( "pom.", "project." ); 060 061 private static final Collection<String> TRANSLATED_PATH_EXPRESSIONS; 062 063 static 064 { 065 Collection<String> translatedPrefixes = new HashSet<String>(); 066 067 // MNG-1927, MNG-2124, MNG-3355: 068 // If the build section is present and the project directory is non-null, we should make 069 // sure interpolation of the directories below uses translated paths. 070 // Afterward, we'll double back and translate any paths that weren't covered during interpolation via the 071 // code below... 072 translatedPrefixes.add( "build.directory" ); 073 translatedPrefixes.add( "build.outputDirectory" ); 074 translatedPrefixes.add( "build.testOutputDirectory" ); 075 translatedPrefixes.add( "build.sourceDirectory" ); 076 translatedPrefixes.add( "build.testSourceDirectory" ); 077 translatedPrefixes.add( "build.scriptSourceDirectory" ); 078 translatedPrefixes.add( "reporting.outputDirectory" ); 079 080 TRANSLATED_PATH_EXPRESSIONS = translatedPrefixes; 081 } 082 083 @Requirement 084 private PathTranslator pathTranslator; 085 086 @Requirement 087 private UrlNormalizer urlNormalizer; 088 089 private Interpolator interpolator; 090 091 private RecursionInterceptor recursionInterceptor; 092 093 public AbstractStringBasedModelInterpolator() 094 { 095 interpolator = createInterpolator(); 096 recursionInterceptor = new PrefixAwareRecursionInterceptor( PROJECT_PREFIXES ); 097 } 098 099 public AbstractStringBasedModelInterpolator setPathTranslator( PathTranslator pathTranslator ) 100 { 101 this.pathTranslator = pathTranslator; 102 return this; 103 } 104 105 public AbstractStringBasedModelInterpolator setUrlNormalizer( UrlNormalizer urlNormalizer ) 106 { 107 this.urlNormalizer = urlNormalizer; 108 return this; 109 } 110 111 protected List<ValueSource> createValueSources( final Model model, final File projectDir, 112 final ModelBuildingRequest config, 113 final ModelProblemCollector problems ) 114 { 115 Properties modelProperties = model.getProperties(); 116 117 ValueSource modelValueSource1 = new PrefixedObjectValueSource( PROJECT_PREFIXES, model, false ); 118 if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 119 { 120 modelValueSource1 = new ProblemDetectingValueSource( modelValueSource1, "pom.", "project.", problems ); 121 } 122 123 ValueSource modelValueSource2 = new ObjectBasedValueSource( model ); 124 if ( config.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0 ) 125 { 126 modelValueSource2 = new ProblemDetectingValueSource( modelValueSource2, "", "project.", problems ); 127 } 128 129 // NOTE: Order counts here! 130 List<ValueSource> valueSources = new ArrayList<ValueSource>( 9 ); 131 132 if ( projectDir != null ) 133 { 134 ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ) 135 { 136 public Object getValue( String expression ) 137 { 138 if ( "basedir".equals( expression ) ) 139 { 140 return projectDir.getAbsolutePath(); 141 } 142 return null; 143 } 144 }, PROJECT_PREFIXES, true ); 145 valueSources.add( basedirValueSource ); 146 147 ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ) 148 { 149 public Object getValue( String expression ) 150 { 151 if ( "baseUri".equals( expression ) ) 152 { 153 return projectDir.getAbsoluteFile().toURI().toString(); 154 } 155 return null; 156 } 157 }, PROJECT_PREFIXES, false ); 158 valueSources.add( baseUriValueSource ); 159 valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), modelProperties ) ); 160 } 161 162 valueSources.add( modelValueSource1 ); 163 164 valueSources.add( new MapBasedValueSource( config.getUserProperties() ) ); 165 166 valueSources.add( new MapBasedValueSource( modelProperties ) ); 167 168 valueSources.add( new MapBasedValueSource( config.getSystemProperties() ) ); 169 170 valueSources.add( new AbstractValueSource( false ) 171 { 172 public Object getValue( String expression ) 173 { 174 return config.getSystemProperties().getProperty( "env." + expression ); 175 } 176 } ); 177 178 valueSources.add( modelValueSource2 ); 179 180 return valueSources; 181 } 182 183 protected List<? extends InterpolationPostProcessor> createPostProcessors( final Model model, 184 final File projectDir, 185 final ModelBuildingRequest config ) 186 { 187 List<InterpolationPostProcessor> processors = new ArrayList<InterpolationPostProcessor>( 2 ); 188 if ( projectDir != null ) 189 { 190 processors.add( new PathTranslatingPostProcessor( PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS, 191 projectDir, pathTranslator ) ); 192 } 193 processors.add( new UrlNormalizingPostProcessor( urlNormalizer ) ); 194 return processors; 195 } 196 197 protected String interpolateInternal( String src, List<? extends ValueSource> valueSources, 198 List<? extends InterpolationPostProcessor> postProcessors, 199 ModelProblemCollector problems ) 200 { 201 if ( !src.contains( "${" ) ) 202 { 203 return src; 204 } 205 206 String result = src; 207 synchronized ( this ) 208 { 209 210 for ( ValueSource vs : valueSources ) 211 { 212 interpolator.addValueSource( vs ); 213 } 214 215 for ( InterpolationPostProcessor postProcessor : postProcessors ) 216 { 217 interpolator.addPostProcessor( postProcessor ); 218 } 219 220 try 221 { 222 try 223 { 224 result = interpolator.interpolate( result, recursionInterceptor ); 225 } 226 catch ( InterpolationException e ) 227 { 228 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 229 .setMessage( e.getMessage() ).setException( e ) ); 230 } 231 232 interpolator.clearFeedback(); 233 } 234 finally 235 { 236 for ( ValueSource vs : valueSources ) 237 { 238 interpolator.removeValuesSource( vs ); 239 } 240 241 for ( InterpolationPostProcessor postProcessor : postProcessors ) 242 { 243 interpolator.removePostProcessor( postProcessor ); 244 } 245 } 246 } 247 248 return result; 249 } 250 251 protected RecursionInterceptor getRecursionInterceptor() 252 { 253 return recursionInterceptor; 254 } 255 256 protected void setRecursionInterceptor( RecursionInterceptor recursionInterceptor ) 257 { 258 this.recursionInterceptor = recursionInterceptor; 259 } 260 261 protected abstract Interpolator createInterpolator(); 262 263 protected final Interpolator getInterpolator() 264 { 265 return interpolator; 266 } 267 268}