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