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