View Javadoc
1   package org.apache.maven.shared.filtering;
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.execution.MavenSession;
23  import org.apache.maven.project.MavenProject;
24  import org.apache.maven.settings.Settings;
25  import org.apache.maven.shared.utils.StringUtils;
26  import org.apache.maven.shared.utils.io.FileUtils;
27  import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
28  import org.codehaus.plexus.interpolation.Interpolator;
29  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
30  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
31  import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
32  import org.codehaus.plexus.interpolation.RecursionInterceptor;
33  import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
34  import org.codehaus.plexus.interpolation.SingleResponseValueSource;
35  import org.codehaus.plexus.interpolation.ValueSource;
36  import org.codehaus.plexus.interpolation.multi.MultiDelimiterStringSearchInterpolator;
37  import org.codehaus.plexus.logging.AbstractLogEnabled;
38  
39  import javax.annotation.Nonnull;
40  import java.io.File;
41  import java.io.IOException;
42  import java.io.Reader;
43  import java.util.ArrayList;
44  import java.util.Collections;
45  import java.util.LinkedHashSet;
46  import java.util.List;
47  import java.util.Properties;
48  
49  class BaseFilter
50      extends AbstractLogEnabled
51      implements DefaultFilterInfo
52  {
53  
54      @Nonnull
55      public List<FileUtils.FilterWrapper> getDefaultFilterWrappers( final MavenProject mavenProject,
56                                                                     List<String> filters,
57                                                                     final boolean escapedBackslashesInFilePath,
58                                                                     MavenSession mavenSession,
59                                                                     MavenResourcesExecution mavenResourcesExecution )
60                                                                         throws MavenFilteringException
61      {
62  
63          MavenResourcesExecution mre =
64              mavenResourcesExecution == null ? new MavenResourcesExecution() : mavenResourcesExecution.copyOf();
65  
66          mre.setMavenProject( mavenProject );
67          mre.setMavenSession( mavenSession );
68          mre.setFilters( filters );
69          mre.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
70  
71          return getDefaultFilterWrappers( mre );
72  
73      }
74  
75      @Nonnull
76      public List<FileUtils.FilterWrapper> getDefaultFilterWrappers( final AbstractMavenFilteringRequest req )
77          throws MavenFilteringException
78      {
79          // backup values
80          boolean supportMultiLineFiltering = req.isSupportMultiLineFiltering();
81  
82          // compensate for null parameter value.
83          final AbstractMavenFilteringRequest request = req == null ? new MavenFileFilterRequest() : req;
84  
85          request.setSupportMultiLineFiltering( supportMultiLineFiltering );
86  
87          // Here we build some properties which will be used to read some properties files
88          // to interpolate the expression ${ } in this properties file
89  
90          // Take a copy of filterProperties to ensure that evaluated filterTokens are not propagated
91          // to subsequent filter files. Note: this replicates current behaviour and seems to make sense.
92  
93          final Properties baseProps = new Properties();
94  
95          // Project properties
96          if ( request.getMavenProject() != null )
97          {
98              baseProps.putAll( request.getMavenProject().getProperties() == null ? Collections.emptyMap()
99                              : request.getMavenProject().getProperties() );
100         }
101         // TODO this is NPE free but do we consider this as normal
102         // or do we have to throw an MavenFilteringException with mavenSession cannot be null
103         if ( request.getMavenSession() != null )
104         {
105             // execution properties wins
106             baseProps.putAll( request.getMavenSession().getExecutionProperties() );
107         }
108 
109         // now we build properties to use for resources interpolation
110 
111         final Properties filterProperties = new Properties();
112 
113         File basedir = request.getMavenProject() != null ? request.getMavenProject().getBasedir() : new File( "." );
114 
115         loadProperties( filterProperties, basedir, request.getFileFilters(), baseProps );
116         if ( filterProperties.size() < 1 )
117         {
118             filterProperties.putAll( baseProps );
119         }
120 
121         if ( request.getMavenProject() != null )
122         {
123             if ( request.isInjectProjectBuildFilters() )
124             {
125                 List<String> buildFilters = new ArrayList<String>( request.getMavenProject().getBuild().getFilters() );
126 
127                 // JDK-8015656: (coll) unexpected NPE from removeAll
128                 if ( request.getFileFilters() != null )
129                 {
130                     buildFilters.removeAll( request.getFileFilters() );
131                 }
132 
133                 loadProperties( filterProperties, basedir, buildFilters, baseProps );
134             }
135 
136             // Project properties
137             filterProperties.putAll( request.getMavenProject().getProperties() == null ? Collections.emptyMap()
138                             : request.getMavenProject().getProperties() );
139         }
140         if ( request.getMavenSession() != null )
141         {
142             // execution properties wins
143             filterProperties.putAll( request.getMavenSession().getExecutionProperties() );
144         }
145 
146         if ( request.getAdditionalProperties() != null )
147         {
148             // additional properties wins
149             filterProperties.putAll( request.getAdditionalProperties() );
150         }
151 
152         List<FileUtils.FilterWrapper> defaultFilterWrappers =
153             request == null ? new ArrayList<FileUtils.FilterWrapper>( 1 )
154                             : new ArrayList<FileUtils.FilterWrapper>( request.getDelimiters().size() + 1 );
155 
156         if ( getLogger().isDebugEnabled() )
157         {
158             getLogger().debug( "properties used " + filterProperties );
159         }
160 
161         final ValueSource propertiesValueSource = new PropertiesBasedValueSource( filterProperties );
162 
163         if ( request != null )
164         {
165             FileUtils.FilterWrapper wrapper =
166                 new Wrapper( request.getDelimiters(), request.getMavenProject(), request.getMavenSession(),
167                              propertiesValueSource, request.getProjectStartExpressions(), request.getEscapeString(),
168                              request.isEscapeWindowsPaths(), request.isSupportMultiLineFiltering() );
169 
170             defaultFilterWrappers.add( wrapper );
171         }
172 
173         return defaultFilterWrappers;
174     }
175 
176     /**
177      * default visibility only for testing reason !
178      */
179     void loadProperties( Properties filterProperties, File basedir, List<String> propertiesFilePaths,
180                          Properties baseProps )
181                              throws MavenFilteringException
182     {
183         if ( propertiesFilePaths != null )
184         {
185             Properties workProperties = new Properties();
186             workProperties.putAll( baseProps );
187 
188             for ( String filterFile : propertiesFilePaths )
189             {
190                 if ( StringUtils.isEmpty( filterFile ) )
191                 {
192                     // skip empty file name
193                     continue;
194                 }
195                 try
196                 {
197                     File propFile = FileUtils.resolveFile( basedir, filterFile );
198                     Properties properties = PropertyUtils.loadPropertyFile( propFile, workProperties );
199                     filterProperties.putAll( properties );
200                     workProperties.putAll( properties );
201                 }
202                 catch ( IOException e )
203                 {
204                     throw new MavenFilteringException( "Error loading property file '" + filterFile + "'", e );
205                 }
206             }
207         }
208     }
209 
210     private static final class Wrapper
211         extends FileUtils.FilterWrapper
212     {
213 
214         private LinkedHashSet<String> delimiters;
215 
216         private MavenProject project;
217 
218         private ValueSource propertiesValueSource;
219 
220         private List<String> projectStartExpressions;
221 
222         private String escapeString;
223 
224         private boolean escapeWindowsPaths;
225 
226         private final MavenSession mavenSession;
227 
228         private boolean supportMultiLineFiltering;
229 
230         Wrapper( LinkedHashSet<String> delimiters, MavenProject project, MavenSession mavenSession,
231                  ValueSource propertiesValueSource, List<String> projectStartExpressions, String escapeString,
232                  boolean escapeWindowsPaths, boolean supportMultiLineFiltering )
233         {
234             super();
235             this.delimiters = delimiters;
236             this.project = project;
237             this.mavenSession = mavenSession;
238             this.propertiesValueSource = propertiesValueSource;
239             this.projectStartExpressions = projectStartExpressions;
240             this.escapeString = escapeString;
241             this.escapeWindowsPaths = escapeWindowsPaths;
242             this.supportMultiLineFiltering = supportMultiLineFiltering;
243         }
244 
245         public Reader getReader( Reader reader )
246         {
247             Interpolator interpolator = createInterpolator( delimiters, projectStartExpressions, propertiesValueSource,
248                                                             project, mavenSession, escapeString, escapeWindowsPaths );
249 
250             MultiDelimiterInterpolatorFilterReaderLineEnding filterReader =
251                 new MultiDelimiterInterpolatorFilterReaderLineEnding( reader, interpolator, supportMultiLineFiltering );
252 
253             final RecursionInterceptor ri;
254             if ( projectStartExpressions != null && !projectStartExpressions.isEmpty() )
255             {
256                 ri = new PrefixAwareRecursionInterceptor( projectStartExpressions, true );
257             }
258             else
259             {
260                 ri = new SimpleRecursionInterceptor();
261             }
262 
263             filterReader.setRecursionInterceptor( ri );
264             filterReader.setDelimiterSpecs( delimiters );
265 
266             filterReader.setInterpolateWithPrefixPattern( false );
267             filterReader.setEscapeString( escapeString );
268 
269             return filterReader;
270         }
271 
272     }
273 
274     private static Interpolator createInterpolator( LinkedHashSet<String> delimiters,
275                                                     List<String> projectStartExpressions,
276                                                     ValueSource propertiesValueSource, MavenProject project,
277                                                     MavenSession mavenSession, String escapeString,
278                                                     boolean escapeWindowsPaths )
279     {
280         MultiDelimiterStringSearchInterpolator interpolator = new MultiDelimiterStringSearchInterpolator();
281         interpolator.setDelimiterSpecs( delimiters );
282 
283         interpolator.addValueSource( propertiesValueSource );
284 
285         if ( project != null )
286         {
287             interpolator.addValueSource( new PrefixedObjectValueSource( projectStartExpressions, project, true ) );
288         }
289 
290         if ( mavenSession != null )
291         {
292             interpolator.addValueSource( new PrefixedObjectValueSource( "session", mavenSession ) );
293 
294             final Settings settings = mavenSession.getSettings();
295             if ( settings != null )
296             {
297                 interpolator.addValueSource( new PrefixedObjectValueSource( "settings", settings ) );
298                 interpolator.addValueSource( new SingleResponseValueSource( "localRepository",
299                                                                             settings.getLocalRepository() ) );
300             }
301         }
302 
303         interpolator.setEscapeString( escapeString );
304 
305         if ( escapeWindowsPaths )
306         {
307             interpolator.addPostProcessor( new InterpolationPostProcessor()
308             {
309                 public Object execute( String expression, Object value )
310                 {
311                     if ( value instanceof String )
312                     {
313                         return FilteringUtils.escapeWindowsPath( (String) value );
314                     }
315 
316                     return value;
317                 }
318             } );
319         }
320         return interpolator;
321     }
322 }