Coverage Report - org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator
 
Classes in this File Line Coverage Branch Coverage Complexity
FixedStringSearchInterpolator
85 %
72/84
75 %
45/60
3,571
FixedStringSearchInterpolator$1
0 %
0/4
N/A
3,571
 
 1  
 package org.codehaus.plexus.interpolation.fixed;
 2  
 
 3  
 /*
 4  
  * Copyright 2014 The Codehaus Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *      http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 
 20  
 import org.codehaus.plexus.interpolation.BasicInterpolator;
 21  
 import org.codehaus.plexus.interpolation.InterpolationException;
 22  
 import org.codehaus.plexus.interpolation.InterpolationPostProcessor;
 23  
 import org.codehaus.plexus.interpolation.RecursionInterceptor;
 24  
 
 25  
 import java.util.ArrayList;
 26  
 import java.util.List;
 27  
 
 28  
 /**
 29  
  * A fixed string search interpolator is permanently bound to a given set of value sources,
 30  
  * an is totally fixed and stateless over these value sources.
 31  
  * <p/>
 32  
  * The fixed interpolator is also a #StatelessValueSource and can be used as a source
 33  
  * for a different fixed interpolator, creating a scope chain.
 34  
  * <p/>
 35  
  * Once constructed, this interpolator will always point to the same set of objects (value sources),
 36  
  * in such a way that if the underlying object is fixed, expressions will always
 37  
  * evaluate to the same result.
 38  
  * <p/>
 39  
  * Th fixed interpolator can be shared among different clients and is thread safe to
 40  
  * the extent the underlying value sources can be accessed safely.
 41  
  * Since interpolation expressions cannot modify the objects, thread safety concerns
 42  
  * this will normally be limited to safe publication and memory model visibility of
 43  
  * underlying objects.
 44  
  * <p/>
 45  
  * <p/>
 46  
  * The fixed interpolator can be a valuesource
 47  
  */
 48  
 public class FixedStringSearchInterpolator
 49  
     implements FixedValueSource
 50  
 {
 51  
 
 52  
     private final FixedValueSource[] valueSources;
 53  
 
 54  
     private final InterpolationPostProcessor postProcessor;
 55  
 
 56  
     public static final String DEFAULT_START_EXPR = "${";
 57  
 
 58  
     public static final String DEFAULT_END_EXPR = "}";
 59  
 
 60  
     private final String startExpr;
 61  
 
 62  
     private final String endExpr;
 63  
 
 64  
     private final String escapeString;
 65  
 
 66  
     private FixedStringSearchInterpolator( String startExpr, String endExpr, String escapeString,
 67  
                                           InterpolationPostProcessor postProcessor, FixedValueSource... valueSources  )
 68  59
     {
 69  59
         this.startExpr = startExpr;
 70  59
         this.endExpr = endExpr;
 71  59
         this.escapeString = escapeString;
 72  59
         if ( valueSources == null )
 73  
         {
 74  0
             throw new IllegalArgumentException( "valueSources cannot be null" );
 75  
         }
 76  123
         for ( int i = 0; i < valueSources.length; i++ )
 77  
         {
 78  64
             if ( valueSources[i] == null )
 79  
             {
 80  0
                 throw new IllegalArgumentException( "valueSources[" + i + "] is null" );
 81  
             }
 82  
         }
 83  
 
 84  59
         this.valueSources = valueSources;
 85  59
         this.postProcessor = postProcessor;
 86  59
     }
 87  
 
 88  
     public static FixedStringSearchInterpolator create( String startExpr, String endExpr,
 89  
                                                         FixedValueSource... valueSources )
 90  
     {
 91  1
         return new FixedStringSearchInterpolator( startExpr, endExpr, null, null, valueSources );
 92  
     }
 93  
 
 94  
 
 95  
     public static FixedStringSearchInterpolator create( FixedValueSource... valueSources )
 96  
     {
 97  34
         return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, valueSources );
 98  
     }
 99  
 
 100  
     public static FixedStringSearchInterpolator createWithPermittedNulls( FixedValueSource... valueSources )
 101  
     {
 102  0
         List<FixedValueSource> nonnulls = new ArrayList<FixedValueSource>(  );
 103  0
         for ( FixedValueSource item : valueSources )
 104  
         {
 105  0
             if (item != null) nonnulls.add( item);
 106  
         }
 107  0
         return new FixedStringSearchInterpolator( DEFAULT_START_EXPR, DEFAULT_END_EXPR, null, null, nonnulls.toArray(new FixedValueSource[nonnulls.size()]) );
 108  
     }
 109  
 
 110  
     public FixedStringSearchInterpolator withExpressionMarkers( String startExpr, String endExpr )
 111  
     {
 112  13
         return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
 113  
     }
 114  
 
 115  
     public FixedStringSearchInterpolator withPostProcessor( InterpolationPostProcessor postProcessor )
 116  
     {
 117  2
         return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
 118  
     }
 119  
 
 120  
 
 121  
     public FixedStringSearchInterpolator withEscapeString( String escapeString )
 122  
     {
 123  9
         return new FixedStringSearchInterpolator( startExpr, endExpr, escapeString, postProcessor, valueSources );
 124  
     }
 125  
 
 126  
     public String interpolate( String input )
 127  
         throws InterpolationCycleException
 128  
     {
 129  33
         return interpolate( input, new InterpolationState() );
 130  
     }
 131  
 
 132  
     public static FixedStringSearchInterpolator empty(){
 133  0
         return create(  );
 134  
     }
 135  
 
 136  
     // Find out how to return null when we cannot interpolate this expression
 137  
     // At this point we should always be a ${expr}
 138  
     public Object getValue( String realExpr, InterpolationState interpolationState )
 139  
     {
 140  
 
 141  54
         interpolationState.recursionInterceptor.expressionResolutionStarted( realExpr );
 142  
 
 143  
         try
 144  
         {
 145  54
             Object value = null;
 146  
 
 147  60
             for ( FixedValueSource valueSource : valueSources )
 148  
             {
 149  57
                 value = valueSource.getValue( realExpr, interpolationState );
 150  54
                 if ( value != null )
 151  
                 {
 152  48
                     break;
 153  
                 }
 154  
             }
 155  
 
 156  51
             if ( value != null )
 157  
             {
 158  48
                 if ( interpolationState.root != null )
 159  
                 {
 160  47
                     value = interpolationState.root.interpolate( String.valueOf( value ), interpolationState );
 161  
                 }
 162  42
                 return String.valueOf( value );
 163  
             }
 164  
             else
 165  
             {
 166  3
                 return null;
 167  
             }
 168  
         }
 169  
         finally
 170  
         {
 171  45
             interpolationState.recursionInterceptor.expressionResolutionFinished( realExpr );
 172  
         }
 173  
     }
 174  
 
 175  
     public BasicInterpolator asBasicInterpolator()
 176  
     {
 177  0
         final InterpolationState is = new InterpolationState();
 178  0
         return new BasicInterpolator()
 179  0
         {
 180  
 
 181  
             public String interpolate( String input )
 182  
                 throws InterpolationException
 183  
             {
 184  0
                 return FixedStringSearchInterpolator.this.interpolate( input, is );
 185  
             }
 186  
 
 187  
             public String interpolate( String input, RecursionInterceptor recursionInterceptor )
 188  
                 throws InterpolationException
 189  
             {
 190  0
                 is.setRecursionInterceptor( recursionInterceptor );
 191  0
                 return FixedStringSearchInterpolator.this.interpolate( input, is );
 192  
             }
 193  
         };
 194  
     }
 195  
 
 196  
     public String interpolate( String input, InterpolationState interpolationState )
 197  
         throws InterpolationCycleException
 198  
     {
 199  113
         if ( interpolationState.root == null )
 200  
         {
 201  33
             interpolationState.root = this;
 202  
         }
 203  
 
 204  113
         if ( input == null )
 205  
         {
 206  
             // return empty String to prevent NPE too
 207  1
             return "";
 208  
         }
 209  112
         StringBuilder result = new StringBuilder( input.length() * 2 );
 210  
 
 211  
         int startIdx;
 212  112
         int endIdx = -1;
 213  150
         while ( ( startIdx = input.indexOf( startExpr, endIdx + 1 ) ) > -1 )
 214  
         {
 215  49
             result.append( input, endIdx + 1, startIdx );
 216  
 
 217  49
             endIdx = input.indexOf( endExpr, startIdx + 1 );
 218  49
             if ( endIdx < 0 )
 219  
             {
 220  1
                 break;
 221  
             }
 222  
 
 223  48
             final String wholeExpr = input.substring( startIdx, endIdx + endExpr.length() );
 224  48
             String realExpr = wholeExpr.substring( startExpr.length(), wholeExpr.length() - endExpr.length() );
 225  
 
 226  48
             if ( startIdx >= 0 && escapeString != null && escapeString.length() > 0 )
 227  
             {
 228  8
                 int startEscapeIdx = startIdx == 0 ? 0 : startIdx - escapeString.length();
 229  8
                 if ( startEscapeIdx >= 0 )
 230  
                 {
 231  8
                     String escape = input.substring( startEscapeIdx, startIdx );
 232  8
                     if ( escapeString.equals( escape ) )
 233  
                     {
 234  5
                         result.append( wholeExpr );
 235  5
                         result.replace( startEscapeIdx, startEscapeIdx + escapeString.length(), "" );
 236  5
                         continue;
 237  
                     }
 238  
                 }
 239  
             }
 240  
 
 241  43
             boolean resolved = false;
 242  43
             if ( !interpolationState.unresolvable.contains( wholeExpr ) )
 243  
             {
 244  43
                 if ( realExpr.startsWith( "." ) )
 245  
                 {
 246  0
                     realExpr = realExpr.substring( 1 );
 247  
                 }
 248  
 
 249  43
                 if ( interpolationState.recursionInterceptor.hasRecursiveExpression( realExpr ) )
 250  
                 {
 251  3
                     throw new InterpolationCycleException( interpolationState.recursionInterceptor, realExpr,
 252  
                                                            wholeExpr );
 253  
                 }
 254  
 
 255  40
                 Object value = getValue( realExpr, interpolationState );
 256  33
                 if ( value != null )
 257  
                 {
 258  33
                     value = interpolate( String.valueOf( value ), interpolationState );
 259  
 
 260  33
                     if ( postProcessor != null )
 261  
                     {
 262  2
                         Object newVal = postProcessor.execute( realExpr, value );
 263  2
                         if ( newVal != null )
 264  
                         {
 265  1
                             value = newVal;
 266  
                         }
 267  
                     }
 268  
 
 269  33
                     result.append( String.valueOf( value ) );
 270  33
                     resolved = true;
 271  
                 }
 272  
                 else
 273  
                 {
 274  0
                     interpolationState.unresolvable.add( wholeExpr );
 275  
                 }
 276  
             }
 277  
 
 278  33
             if ( !resolved )
 279  
             {
 280  0
                 result.append( wholeExpr );
 281  
             }
 282  
 
 283  33
             if ( endIdx > -1 )
 284  
             {
 285  33
                 endIdx += endExpr.length() - 1;
 286  
             }
 287  33
         }
 288  
 
 289  102
         if ( endIdx == -1 && startIdx > -1 )
 290  
         {
 291  1
             result.append( input, startIdx, input.length() );
 292  
         }
 293  101
         else if ( endIdx < input.length() )
 294  
         {
 295  101
             result.append( input, endIdx + 1, input.length() );
 296  
         }
 297  
 
 298  102
         return result.toString();
 299  
     }
 300  
 }
 301