001 package 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 022 import org.apache.maven.model.Model; 023 import org.apache.maven.project.ProjectBuilderConfiguration; 024 import org.apache.maven.project.path.PathTranslator; 025 import org.codehaus.plexus.component.annotations.Component; 026 import org.codehaus.plexus.interpolation.InterpolationPostProcessor; 027 import org.codehaus.plexus.interpolation.Interpolator; 028 import org.codehaus.plexus.interpolation.StringSearchInterpolator; 029 import org.codehaus.plexus.interpolation.ValueSource; 030 import org.codehaus.plexus.logging.Logger; 031 032 import java.io.File; 033 import java.lang.reflect.Array; 034 import java.lang.reflect.Field; 035 import java.security.AccessController; 036 import java.security.PrivilegedAction; 037 import java.util.ArrayList; 038 import java.util.Collection; 039 import java.util.LinkedList; 040 import java.util.List; 041 import java.util.Map; 042 import java.util.WeakHashMap; 043 044 @Deprecated 045 @Component( role = ModelInterpolator.class ) 046 public class StringSearchModelInterpolator 047 extends AbstractStringBasedModelInterpolator 048 { 049 050 private static final Map<Class<?>, Field[]> fieldsByClass = new WeakHashMap<Class<?>, Field[]>(); 051 private static final Map<Class<?>, Boolean> fieldIsPrimitiveByClass = new WeakHashMap<Class<?>, Boolean>(); 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<Object>(); 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 ( int i = 0; i < fields.length; i++ ) 172 { 173 Class<?> type = fields[i].getType(); 174 if ( isQualifiedForInterpolation( fields[i], type ) ) 175 { 176 boolean isAccessible = fields[i].isAccessible(); 177 fields[i].setAccessible( true ); 178 try 179 { 180 try 181 { 182 if ( String.class == type ) 183 { 184 String value = (String) fields[i].get( target ); 185 if ( value != null ) 186 { 187 String interpolated = modelInterpolator.interpolateInternal( value, valueSources, postProcessors, debugEnabled ); 188 189 if ( !interpolated.equals( value ) ) 190 { 191 fields[i].set( target, interpolated ); 192 } 193 } 194 } 195 else if ( Collection.class.isAssignableFrom( type ) ) 196 { 197 Collection<Object> c = (Collection<Object>) fields[i].get( target ); 198 if ( c != null && !c.isEmpty() ) 199 { 200 List<Object> originalValues = new ArrayList<Object>( c ); 201 try 202 { 203 c.clear(); 204 } 205 catch ( UnsupportedOperationException e ) 206 { 207 if ( debugEnabled && logger != null ) 208 { 209 logger.debug( "Skipping interpolation of field: " + fields[i] + " in: " 210 + cls.getName() + "; it is an unmodifiable collection." ); 211 } 212 continue; 213 } 214 215 for ( Object value : originalValues ) 216 { 217 if ( value != null ) 218 { 219 if ( String.class == value.getClass() ) 220 { 221 String interpolated = 222 modelInterpolator.interpolateInternal( (String) value, 223 valueSources, 224 postProcessors, 225 debugEnabled ); 226 227 if ( !interpolated.equals( value ) ) 228 { 229 c.add( interpolated ); 230 } 231 else 232 { 233 c.add( value ); 234 } 235 } 236 else 237 { 238 c.add( value ); 239 if ( value.getClass().isArray() ) 240 { 241 evaluateArray( value ); 242 } 243 else 244 { 245 interpolationTargets.add( value ); 246 } 247 } 248 } 249 else 250 { 251 // add the null back in...not sure what else to do... 252 c.add( value ); 253 } 254 } 255 } 256 } 257 else if ( Map.class.isAssignableFrom( type ) ) 258 { 259 Map<Object, Object> m = (Map<Object, Object>) fields[i].get( target ); 260 if ( m != null && !m.isEmpty() ) 261 { 262 for ( Map.Entry<Object, Object> entry : m.entrySet() ) 263 { 264 Object value = entry.getValue(); 265 266 if ( value != null ) 267 { 268 if ( String.class == value.getClass() ) 269 { 270 String interpolated = 271 modelInterpolator.interpolateInternal( (String) value, 272 valueSources, 273 postProcessors, 274 debugEnabled ); 275 276 if ( !interpolated.equals( value ) ) 277 { 278 try 279 { 280 entry.setValue( interpolated ); 281 } 282 catch ( UnsupportedOperationException e ) 283 { 284 if ( debugEnabled && logger != null ) 285 { 286 logger.debug( "Skipping interpolation of field: " 287 + fields[i] + " (key: " + entry.getKey() + ") in: " 288 + cls.getName() 289 + "; it is an unmodifiable collection." ); 290 } 291 continue; 292 } 293 } 294 } 295 else 296 { 297 if ( value.getClass().isArray() ) 298 { 299 evaluateArray( value ); 300 } 301 else 302 { 303 interpolationTargets.add( value ); 304 } 305 } 306 } 307 } 308 } 309 } 310 else 311 { 312 Object value = fields[i].get( target ); 313 if ( value != null ) 314 { 315 if ( fields[i].getType().isArray() ) 316 { 317 evaluateArray( value ); 318 } 319 else 320 { 321 interpolationTargets.add( value ); 322 } 323 } 324 } 325 } 326 catch ( IllegalArgumentException e ) 327 { 328 throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i] 329 + " on class: " + cls.getName(), e ); 330 } 331 catch ( IllegalAccessException e ) 332 { 333 throw new ModelInterpolationException( "Failed to interpolate field: " + fields[i] 334 + " on class: " + cls.getName(), e ); 335 } 336 } 337 finally 338 { 339 fields[i].setAccessible( isAccessible ); 340 } 341 } 342 } 343 344 traverseObjectWithParents( cls.getSuperclass(), target ); 345 } 346 } 347 348 private boolean isQualifiedForInterpolation( Class<?> cls ) 349 { 350 return !cls.getPackage().getName().startsWith( "java" ); 351 } 352 353 private boolean isQualifiedForInterpolation( Field field, Class<?> fieldType ) 354 { 355 if ( !fieldIsPrimitiveByClass.containsKey( fieldType ) ) 356 { 357 fieldIsPrimitiveByClass.put( fieldType, Boolean.valueOf( fieldType.isPrimitive() ) ); 358 } 359 360 if ( fieldIsPrimitiveByClass.get( fieldType ).booleanValue() ) 361 { 362 return false; 363 } 364 365 // if ( fieldType.isPrimitive() ) 366 // { 367 // return false; 368 // } 369 370 if ( "parent".equals( field.getName() ) ) 371 { 372 return false; 373 } 374 375 return true; 376 } 377 378 private void evaluateArray( Object target ) 379 throws ModelInterpolationException 380 { 381 int len = Array.getLength( target ); 382 for ( int i = 0; i < len; i++ ) 383 { 384 Object value = Array.get( target, i ); 385 if ( value != null ) 386 { 387 if ( String.class == value.getClass() ) 388 { 389 String interpolated = 390 modelInterpolator.interpolateInternal( (String) value, valueSources, postProcessors, 391 debugEnabled ); 392 393 if ( !interpolated.equals( value ) ) 394 { 395 Array.set( target, i, interpolated ); 396 } 397 } 398 else 399 { 400 interpolationTargets.add( value ); 401 } 402 } 403 } 404 } 405 } 406 407 }