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<>(); 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<>( 9 ); 131 132 if ( projectDir != null ) 133 { 134 ValueSource basedirValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ) 135 { 136 @Override 137 public Object getValue( String expression ) 138 { 139 if ( "basedir".equals( expression ) ) 140 { 141 return projectDir.getAbsolutePath(); 142 } 143 return null; 144 } 145 }, PROJECT_PREFIXES, true ); 146 valueSources.add( basedirValueSource ); 147 148 ValueSource baseUriValueSource = new PrefixedValueSourceWrapper( new AbstractValueSource( false ) 149 { 150 @Override 151 public Object getValue( String expression ) 152 { 153 if ( "baseUri".equals( expression ) ) 154 { 155 return projectDir.getAbsoluteFile().toURI().toString(); 156 } 157 return null; 158 } 159 }, PROJECT_PREFIXES, false ); 160 valueSources.add( baseUriValueSource ); 161 valueSources.add( new BuildTimestampValueSource( config.getBuildStartTime(), modelProperties ) ); 162 } 163 164 valueSources.add( modelValueSource1 ); 165 166 valueSources.add( new MapBasedValueSource( config.getUserProperties() ) ); 167 168 valueSources.add( new MapBasedValueSource( modelProperties ) ); 169 170 valueSources.add( new MapBasedValueSource( config.getSystemProperties() ) ); 171 172 valueSources.add( new AbstractValueSource( false ) 173 { 174 @Override 175 public Object getValue( String expression ) 176 { 177 return config.getSystemProperties().getProperty( "env." + expression ); 178 } 179 } ); 180 181 valueSources.add( modelValueSource2 ); 182 183 return valueSources; 184 } 185 186 protected List<? extends InterpolationPostProcessor> createPostProcessors( final Model model, 187 final File projectDir, 188 final ModelBuildingRequest config ) 189 { 190 List<InterpolationPostProcessor> processors = new ArrayList<>( 2 ); 191 if ( projectDir != null ) 192 { 193 processors.add( new PathTranslatingPostProcessor( PROJECT_PREFIXES, TRANSLATED_PATH_EXPRESSIONS, 194 projectDir, pathTranslator ) ); 195 } 196 processors.add( new UrlNormalizingPostProcessor( urlNormalizer ) ); 197 return processors; 198 } 199 200 protected String interpolateInternal( String src, List<? extends ValueSource> valueSources, 201 List<? extends InterpolationPostProcessor> postProcessors, 202 ModelProblemCollector problems ) 203 { 204 if ( !src.contains( "${" ) ) 205 { 206 return src; 207 } 208 209 String result = src; 210 synchronized ( this ) 211 { 212 213 for ( ValueSource vs : valueSources ) 214 { 215 interpolator.addValueSource( vs ); 216 } 217 218 for ( InterpolationPostProcessor postProcessor : postProcessors ) 219 { 220 interpolator.addPostProcessor( postProcessor ); 221 } 222 223 try 224 { 225 try 226 { 227 result = interpolator.interpolate( result, recursionInterceptor ); 228 } 229 catch ( InterpolationException e ) 230 { 231 problems.add( new ModelProblemCollectorRequest( Severity.ERROR, Version.BASE ) 232 .setMessage( e.getMessage() ).setException( e ) ); 233 } 234 235 interpolator.clearFeedback(); 236 } 237 finally 238 { 239 for ( ValueSource vs : valueSources ) 240 { 241 interpolator.removeValuesSource( vs ); 242 } 243 244 for ( InterpolationPostProcessor postProcessor : postProcessors ) 245 { 246 interpolator.removePostProcessor( postProcessor ); 247 } 248 } 249 } 250 251 return result; 252 } 253 254 protected RecursionInterceptor getRecursionInterceptor() 255 { 256 return recursionInterceptor; 257 } 258 259 protected void setRecursionInterceptor( RecursionInterceptor recursionInterceptor ) 260 { 261 this.recursionInterceptor = recursionInterceptor; 262 } 263 264 protected abstract Interpolator createInterpolator(); 265 266 protected final Interpolator getInterpolator() 267 { 268 return interpolator; 269 } 270 271}