001package org.apache.maven.project.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 org.apache.maven.model.Model; 023import org.apache.maven.project.ProjectBuilderConfiguration; 024import org.apache.maven.project.path.PathTranslator; 025import org.codehaus.plexus.component.annotations.Component; 026import org.codehaus.plexus.interpolation.InterpolationPostProcessor; 027import org.codehaus.plexus.interpolation.Interpolator; 028import org.codehaus.plexus.interpolation.StringSearchInterpolator; 029import org.codehaus.plexus.interpolation.ValueSource; 030import org.codehaus.plexus.logging.Logger; 031 032import java.io.File; 033import java.lang.reflect.Array; 034import java.lang.reflect.Field; 035import java.security.AccessController; 036import java.security.PrivilegedAction; 037import java.util.ArrayList; 038import java.util.Collection; 039import java.util.LinkedList; 040import java.util.List; 041import java.util.Map; 042import java.util.WeakHashMap; 043 044@Deprecated 045@Component( role = ModelInterpolator.class ) 046public class StringSearchModelInterpolator 047 extends AbstractStringBasedModelInterpolator 048{ 049 050 private static final Map<Class<?>, Field[]> fieldsByClass = new WeakHashMap<>(); 051 private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = new WeakHashMap<>(); 052 053 public StringSearchModelInterpolator() 054 { 055 } 056 057 public StringSearchModelInterpolator( PathTranslator pathTranslator ) 058 { 059 super( pathTranslator ); 060 } 061 062 public Model interpolate( Model model, File projectDir, ProjectBuilderConfiguration config, boolean debugEnabled ) 063 throws ModelInterpolationException 064 { 065 interpolateObject( model, model, projectDir, config, debugEnabled ); 066 067 return model; 068 } 069 070 protected void interpolateObject( Object obj, Model model, File projectDir, ProjectBuilderConfiguration config, 071 boolean debugEnabled ) 072 throws ModelInterpolationException 073 { 074 try 075 { 076 List<ValueSource> valueSources = createValueSources( model, projectDir, config ); 077 List<InterpolationPostProcessor> postProcessors = createPostProcessors( model, projectDir, config ); 078 079 InterpolateObjectAction action = 080 new InterpolateObjectAction( obj, valueSources, postProcessors, debugEnabled, 081 this, getLogger() ); 082 083 ModelInterpolationException error = AccessController.doPrivileged( action ); 084 085 if ( error != null ) 086 { 087 throw error; 088 } 089 } 090 finally 091 { 092 getInterpolator().clearAnswers(); 093 } 094 } 095 096 protected Interpolator createInterpolator() 097 { 098 StringSearchInterpolator interpolator = new StringSearchInterpolator(); 099 interpolator.setCacheAnswers( true ); 100 101 return interpolator; 102 } 103 104 private static final class InterpolateObjectAction implements PrivilegedAction<ModelInterpolationException> 105 { 106 107 private final boolean debugEnabled; 108 private final LinkedList<Object> interpolationTargets; 109 private final StringSearchModelInterpolator modelInterpolator; 110 private final Logger logger; 111 private final List<ValueSource> valueSources; 112 private final List<InterpolationPostProcessor> postProcessors; 113 114 public InterpolateObjectAction( Object target, List<ValueSource> valueSources, 115 List<InterpolationPostProcessor> postProcessors, boolean debugEnabled, 116 StringSearchModelInterpolator modelInterpolator, Logger logger ) 117 { 118 this.valueSources = valueSources; 119 this.postProcessors = postProcessors; 120 this.debugEnabled = debugEnabled; 121 122 this.interpolationTargets = new LinkedList<>(); 123 interpolationTargets.add( target ); 124 125 this.modelInterpolator = modelInterpolator; 126 this.logger = logger; 127 } 128 129 public ModelInterpolationException run() 130 { 131 while ( !interpolationTargets.isEmpty() ) 132 { 133 Object obj = interpolationTargets.removeFirst(); 134 135 try 136 { 137 traverseObjectWithParents( obj.getClass(), obj ); 138 } 139 catch ( ModelInterpolationException e ) 140 { 141 return e; 142 } 143 } 144 145 return null; 146 } 147 148 @SuppressWarnings( "unchecked" ) 149 private void traverseObjectWithParents( Class<?> cls, Object target ) 150 throws ModelInterpolationException 151 { 152 if ( cls == null ) 153 { 154 return; 155 } 156 157 158 if ( cls.isArray() ) 159 { 160 evaluateArray( target ); 161 } 162 else if ( isQualifiedForInterpolation( cls ) ) 163 { 164 Field[] fields = fieldsByClass.get( cls ); 165 if ( fields == null ) 166 { 167 fields = cls.getDeclaredFields(); 168 fieldsByClass.put( cls, fields ); 169 } 170 171 for ( Field field : fields ) 172 { 173 Class<?> type = field.getType(); 174 if ( isQualifiedForInterpolation( field, type ) ) 175 { 176 boolean isAccessible = field.isAccessible(); 177 field.setAccessible( true ); 178 try 179 { 180 try 181 { 182 if ( String.class == type ) 183 { 184 String value = (String) field.get( target ); 185 if ( value != null ) 186 { 187 String interpolated = 188 modelInterpolator.interpolateInternal( value, valueSources, postProcessors, 189 debugEnabled ); 190 191 if ( !interpolated.equals( value ) ) 192 { 193 field.set( target, interpolated ); 194 } 195 } 196 } 197 else if ( Collection.class.isAssignableFrom( type ) ) 198 { 199 Collection<Object> c = (Collection<Object>) field.get( target ); 200 if ( c != null && !c.isEmpty() ) 201 { 202 List<Object> originalValues = new ArrayList<>( c ); 203 try 204 { 205 c.clear(); 206 } 207 catch ( UnsupportedOperationException e ) 208 { 209 if ( debugEnabled && logger != null ) 210 { 211 logger.debug( "Skipping interpolation of field: " + field + " in: " 212 + cls.getName() 213 + "; it is an unmodifiable collection." ); 214 } 215 continue; 216 } 217 218 for ( Object value : originalValues ) 219 { 220 if ( value != null ) 221 { 222 if ( String.class == value.getClass() ) 223 { 224 String interpolated = 225 modelInterpolator.interpolateInternal( (String) value, 226 valueSources, 227 postProcessors, 228 debugEnabled ); 229 230 if ( !interpolated.equals( value ) ) 231 { 232 c.add( interpolated ); 233 } 234 else 235 { 236 c.add( value ); 237 } 238 } 239 else 240 { 241 c.add( value ); 242 if ( value.getClass().isArray() ) 243 { 244 evaluateArray( value ); 245 } 246 else 247 { 248 interpolationTargets.add( value ); 249 } 250 } 251 } 252 else 253 { 254 // add the null back in...not sure what else to do... 255 c.add( value ); 256 } 257 } 258 } 259 } 260 else if ( Map.class.isAssignableFrom( type ) ) 261 { 262 Map<Object, Object> m = (Map<Object, Object>) field.get( target ); 263 if ( m != null && !m.isEmpty() ) 264 { 265 for ( Map.Entry<Object, Object> entry : m.entrySet() ) 266 { 267 Object value = entry.getValue(); 268 269 if ( value != null ) 270 { 271 if ( String.class == value.getClass() ) 272 { 273 String interpolated = 274 modelInterpolator.interpolateInternal( (String) value, 275 valueSources, 276 postProcessors, 277 debugEnabled ); 278 279 if ( !interpolated.equals( value ) ) 280 { 281 try 282 { 283 entry.setValue( interpolated ); 284 } 285 catch ( UnsupportedOperationException e ) 286 { 287 if ( debugEnabled && logger != null ) 288 { 289 logger.debug( 290 "Skipping interpolation of field: " + field 291 + " (key: " + entry.getKey() + ") in: " 292 + cls.getName() 293 + "; it is an unmodifiable collection." ); 294 } 295 } 296 } 297 } 298 else 299 { 300 if ( value.getClass().isArray() ) 301 { 302 evaluateArray( value ); 303 } 304 else 305 { 306 interpolationTargets.add( value ); 307 } 308 } 309 } 310 } 311 } 312 } 313 else 314 { 315 Object value = field.get( target ); 316 if ( value != null ) 317 { 318 if ( field.getType().isArray() ) 319 { 320 evaluateArray( value ); 321 } 322 else 323 { 324 interpolationTargets.add( value ); 325 } 326 } 327 } 328 } 329 catch ( IllegalArgumentException | IllegalAccessException e ) 330 { 331 throw new ModelInterpolationException( 332 "Failed to interpolate field: " + field + " on class: " + cls.getName(), e ); 333 } 334 } 335 finally 336 { 337 field.setAccessible( isAccessible ); 338 } 339 } 340 } 341 342 traverseObjectWithParents( cls.getSuperclass(), target ); 343 } 344 } 345 346 private boolean isQualifiedForInterpolation( Class<?> cls ) 347 { 348 return !cls.getPackage().getName().startsWith( "java" ); 349 } 350 351 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType ) 352 { 353 if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) ) 354 { 355 fieldIsPrimitiveByClass.put( fieldType, fieldType.isPrimitive() ); 356 } 357 358 if ( fieldIsPrimitiveByClass.get( fieldType ) ) 359 { 360 return false; 361 } 362 363// if ( fieldType.isPrimitive() ) 364// { 365// return false; 366// } 367 368 if ( "parent".equals( field.getName() ) ) 369 { 370 return false; 371 } 372 373 return true; 374 } 375 376 private void evaluateArray( Object target ) 377 throws ModelInterpolationException 378 { 379 int len = Array.getLength( target ); 380 for ( int i = 0; i < len; i++ ) 381 { 382 Object value = Array.get( target, i ); 383 if ( value != null ) 384 { 385 if ( String.class == value.getClass() ) 386 { 387 String interpolated = 388 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, 389 debugEnabled ); 390 391 if ( !interpolated.equals( value ) ) 392 { 393 Array.set( target, i, interpolated ); 394 } 395 } 396 else 397 { 398 interpolationTargets.add( value ); 399 } 400 } 401 } 402 } 403 } 404 405}