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