View Javadoc

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.codehaus.plexus.logging.AbstractLogEnabled;
26  import org.codehaus.plexus.logging.Logger;
27  import org.codehaus.plexus.util.StringUtils;
28  import org.codehaus.plexus.util.cli.CommandLineUtils;
29  import org.codehaus.plexus.util.introspection.ReflectionValueExtractor;
30  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
31  
32  import java.io.IOException;
33  import java.io.StringReader;
34  import java.io.StringWriter;
35  import java.util.Map;
36  import java.util.Properties;
37  import java.util.regex.Matcher;
38  import java.util.regex.Pattern;
39  
40  /**
41   * Use a regular expression search to find and resolve expressions within the POM.
42   *
43   * @author jdcasey Created on Feb 3, 2005
44   * @version $Id: RegexBasedModelInterpolator.java 688884 2008-08-25 21:11:19Z jdcasey $
45   * @todo Consolidate this logic with the PluginParameterExpressionEvaluator, minus deprecations/bans.
46   */
47  public class RegexBasedModelInterpolator
48      extends AbstractLogEnabled
49      implements ModelInterpolator
50  {
51      private static final Pattern EXPRESSION_PATTERN = Pattern.compile( "\\$\\{(pom\\.|project\\.|env\\.)?([^}]+)\\}" );
52  
53      private Properties envars;
54  
55      public RegexBasedModelInterpolator( Properties envars )
56      {
57          this.envars = envars;
58      }
59  
60      public RegexBasedModelInterpolator()
61          throws IOException
62      {
63          envars = CommandLineUtils.getSystemEnvVars();
64      }
65  
66      public Model interpolate( Model model, Map context )
67          throws ModelInterpolationException
68      {
69          return interpolate( model, context, true );
70      }
71  
72      /**
73       * Serialize the inbound Model instance to a StringWriter, perform the regex replacement to resolve
74       * POM expressions, then re-parse into the resolved Model instance.
75       * <br/>
76       * <b>NOTE:</b> This will result in a different instance of Model being returned!!!
77       *
78       * @param model   The inbound Model instance, to serialize and reference for expression resolution
79       * @param context The other context map to be used during resolution
80       * @return The resolved instance of the inbound Model. This is a different instance!
81       */
82      public Model interpolate( Model model, Map context, boolean strict )
83          throws ModelInterpolationException
84      {
85          StringWriter sWriter = new StringWriter();
86  
87          MavenXpp3Writer writer = new MavenXpp3Writer();
88          try
89          {
90              writer.write( sWriter, model );
91          }
92          catch ( IOException e )
93          {
94              throw new ModelInterpolationException( "Cannot serialize project model for interpolation.", e );
95          }
96  
97          String serializedModel = sWriter.toString();
98          serializedModel = interpolateInternal( serializedModel, model, context );
99  
100         StringReader sReader = new StringReader( serializedModel );
101 
102         MavenXpp3Reader modelReader = new MavenXpp3Reader();
103         try
104         {
105             model = modelReader.read( sReader );
106         }
107         catch ( IOException e )
108         {
109             throw new ModelInterpolationException(
110                 "Cannot read project model from interpolating filter of serialized version.", e );
111         }
112         catch ( XmlPullParserException e )
113         {
114             throw new ModelInterpolationException(
115                 "Cannot read project model from interpolating filter of serialized version.", e );
116         }
117 
118         return model;
119     }
120 
121     private String interpolateInternal( String src, Model model, Map context )
122         throws ModelInterpolationException
123     {
124         String result = src;
125         Matcher matcher = EXPRESSION_PATTERN.matcher( result );
126         while ( matcher.find() )
127         {
128             String wholeExpr = matcher.group( 0 );
129             String realExpr = matcher.group( 2 );
130 
131             Object value = context.get( realExpr );
132 
133             if ( value == null )
134             {
135                 // This may look out of place, but its here for the MNG-2124/MNG-1927 fix described in the project builder
136                 if ( context.containsKey( realExpr ) )
137                 {
138                     // It existed, but was null. Leave it alone.
139                     continue;
140                 }
141 
142                 value = model.getProperties().getProperty( realExpr );
143             }
144 
145             if ( value == null )
146             {
147                 try
148                 {
149                     // NOTE: We've already trimmed off any leading expression parts like 'project.'
150                     // or 'pom.', and now we have to ensure that the ReflectionValueExtractor
151                     // doesn't try to do it again.
152                     value = ReflectionValueExtractor.evaluate( realExpr, model, false );
153                 }
154                 catch ( Exception e )
155                 {
156                     Logger logger = getLogger();
157                     if ( logger != null )
158                     {
159                         logger.debug( "POM interpolation cannot proceed with expression: " + wholeExpr + ". Skipping...", e );
160                     }
161                 }
162             }
163 
164             if ( value == null )
165             {
166                 value = envars.getProperty( realExpr );
167             }
168 
169             // if the expression refers to itself, skip it.
170             if ( String.valueOf( value ).indexOf( wholeExpr ) > -1 )
171             {
172                 throw new ModelInterpolationException( wholeExpr, "Expression value '" + value + "' references itself in '" + model.getId() + "'." );
173             }
174 
175             if ( value != null )
176             {
177                 result = StringUtils.replace( result, wholeExpr, String.valueOf( value ) );
178                 // could use:
179                 // result = matcher.replaceFirst( stringValue );
180                 // but this could result in multiple lookups of stringValue, and replaceAll is not correct behaviour
181                 matcher.reset( result );
182             }
183 /*
184         // This is the desired behaviour, however there are too many crappy poms in the repo and an issue with the
185         // timing of executing the interpolation
186 
187             else
188             {
189                 throw new ModelInterpolationException(
190                     "Expression '" + wholeExpr + "' did not evaluate to anything in the model" );
191             }
192 */
193         }
194 
195         return result;
196     }
197 
198 }