Coverage Report - org.codehaus.plexus.interpolation.StringSearchInterpolator
 
Classes in this File Line Coverage Branch Coverage Complexity
StringSearchInterpolator
76 %
82/107
72 %
48/66
3,056
 
 1  
 package org.codehaus.plexus.interpolation;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2008 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  
 import java.util.ArrayList;
 20  
 import java.util.HashMap;
 21  
 import java.util.HashSet;
 22  
 import java.util.List;
 23  
 import java.util.Map;
 24  
 import java.util.Set;
 25  
 
 26  
 public class StringSearchInterpolator
 27  
     implements Interpolator
 28  
 {
 29  
 
 30  63
     private Map<String,Object> existingAnswers = new HashMap<String,Object>();
 31  
 
 32  63
     private List<ValueSource> valueSources = new ArrayList<ValueSource>();
 33  
 
 34  63
     private List<InterpolationPostProcessor> postProcessors = new ArrayList<InterpolationPostProcessor>();
 35  
 
 36  63
     private boolean cacheAnswers = false;
 37  
 
 38  
     public static final String DEFAULT_START_EXPR = "${";
 39  
 
 40  
     public static final String DEFAULT_END_EXPR = "}";
 41  
 
 42  
     private String startExpr;
 43  
 
 44  
     private String endExpr;
 45  
 
 46  
     private String escapeString;
 47  
 
 48  
     public StringSearchInterpolator()
 49  40
     {
 50  40
         this.startExpr = DEFAULT_START_EXPR;
 51  40
         this.endExpr = DEFAULT_END_EXPR;
 52  40
     }
 53  
 
 54  
     public StringSearchInterpolator( String startExpr, String endExpr )
 55  23
     {
 56  23
         this.startExpr = startExpr;
 57  23
         this.endExpr = endExpr;
 58  23
     }
 59  
 
 60  
 
 61  
     /**
 62  
      * {@inheritDoc}
 63  
      */
 64  
     public void addValueSource( ValueSource valueSource )
 65  
     {
 66  64
         valueSources.add( valueSource );
 67  64
     }
 68  
 
 69  
     /**
 70  
      * {@inheritDoc}
 71  
      */
 72  
     public void removeValuesSource( ValueSource valueSource )
 73  
     {
 74  0
         valueSources.remove( valueSource );
 75  0
     }
 76  
 
 77  
     /**
 78  
      * {@inheritDoc}
 79  
      */
 80  
     public void addPostProcessor( InterpolationPostProcessor postProcessor )
 81  
     {
 82  2
         postProcessors.add( postProcessor );
 83  2
     }
 84  
 
 85  
     /**
 86  
      * {@inheritDoc}
 87  
      */
 88  
     public void removePostProcessor( InterpolationPostProcessor postProcessor )
 89  
     {
 90  0
         postProcessors.remove( postProcessor );
 91  0
     }
 92  
 
 93  
     public String interpolate( String input, String thisPrefixPattern )
 94  
         throws InterpolationException
 95  
     {
 96  0
         return interpolate( input, new SimpleRecursionInterceptor() );
 97  
     }
 98  
 
 99  
     public String interpolate( String input, String thisPrefixPattern, RecursionInterceptor recursionInterceptor )
 100  
         throws InterpolationException
 101  
     {
 102  0
         return interpolate( input, recursionInterceptor );
 103  
     }
 104  
 
 105  
     public String interpolate( String input )
 106  
         throws InterpolationException
 107  
     {
 108  23
         return interpolate( input, new SimpleRecursionInterceptor() );
 109  
     }
 110  
 
 111  
     /**
 112  
      * Entry point for recursive resolution of an expression and all of its
 113  
      * nested expressions.
 114  
      *
 115  
      * @todo Ensure unresolvable expressions don't trigger infinite recursion.
 116  
      */
 117  
     public String interpolate( String input, RecursionInterceptor recursionInterceptor )
 118  
         throws InterpolationException
 119  
     {
 120  
         try
 121  
         {
 122  79
             return interpolate( input, recursionInterceptor, new HashSet<String>() );
 123  
         }
 124  
         finally
 125  
         {
 126  79
             if ( !cacheAnswers )
 127  
             {
 128  64
                 existingAnswers.clear();
 129  
             }
 130  
         }
 131  
     }
 132  
 
 133  
     private String interpolate( String input, RecursionInterceptor recursionInterceptor, Set<String> unresolvable )
 134  
         throws InterpolationException
 135  
     {
 136  146
         if ( input == null )
 137  
         {
 138  
             // return empty String to prevent NPE too
 139  1
             return "";
 140  
         }
 141  145
         StringBuilder result = new StringBuilder( input.length() * 2 );
 142  
 
 143  
         int startIdx;
 144  145
         int endIdx = -1;
 145  223
         while ( ( startIdx = input.indexOf( startExpr, endIdx + 1 ) ) > -1 )
 146  
         {
 147  83
             result.append( input, endIdx + 1, startIdx );
 148  
 
 149  83
             endIdx = input.indexOf( endExpr, startIdx + 1 );
 150  83
             if ( endIdx < 0 )
 151  
             {
 152  1
                 break;
 153  
             }
 154  
 
 155  82
             final String wholeExpr = input.substring( startIdx, endIdx + endExpr.length() );
 156  82
             String realExpr = wholeExpr.substring( startExpr.length(), wholeExpr.length() - endExpr.length() );
 157  
 
 158  82
             if ( startIdx >= 0 && escapeString != null && escapeString.length() > 0 )
 159  
             {
 160  10
                 int startEscapeIdx = startIdx == 0 ? 0 : startIdx - escapeString.length();
 161  10
                 if ( startEscapeIdx >= 0 )
 162  
                 {
 163  10
                     String escape = input.substring( startEscapeIdx, startIdx );
 164  10
                     if ( escapeString.equals( escape ) )
 165  
                     {
 166  5
                         result.append( wholeExpr );
 167  5
                         result.replace( startEscapeIdx, startEscapeIdx + escapeString.length(), "" );
 168  5
                         continue;
 169  
                     }
 170  
                 }
 171  
             }
 172  
 
 173  77
             boolean resolved = false;
 174  77
             if ( !unresolvable.contains( wholeExpr ) )
 175  
             {
 176  77
                 if ( realExpr.startsWith( "." ) )
 177  
                 {
 178  0
                     realExpr = realExpr.substring( 1 );
 179  
                 }
 180  
 
 181  77
                 if ( recursionInterceptor.hasRecursiveExpression( realExpr ) )
 182  
                 {
 183  1
                     throw new InterpolationCycleException( recursionInterceptor, realExpr, wholeExpr );
 184  
                 }
 185  
 
 186  76
                 recursionInterceptor.expressionResolutionStarted( realExpr );
 187  
                 try
 188  
                 {
 189  76
                     Object value = existingAnswers.get( realExpr );
 190  76
                     Object bestAnswer = null;
 191  
 
 192  76
                     for ( ValueSource valueSource : valueSources )
 193  
                     {
 194  78
                         if ( value != null )
 195  
                         {
 196  1
                             break;
 197  
                         }
 198  77
                         value = valueSource.getValue( realExpr );
 199  
 
 200  76
                         if ( value != null && value.toString().contains( wholeExpr ) )
 201  
                         {
 202  0
                             bestAnswer = value;
 203  0
                             value = null;
 204  
                         }
 205  76
                     }
 206  
 
 207  
                     // this is the simplest recursion check to catch exact recursion
 208  
                     // (non synonym), and avoid the extra effort of more string
 209  
                     // searching.
 210  75
                     if ( value == null && bestAnswer != null )
 211  
                     {
 212  0
                         throw new InterpolationCycleException( recursionInterceptor, realExpr, wholeExpr );
 213  
                     }
 214  
 
 215  75
                     if ( value != null )
 216  
                     {
 217  67
                         value = interpolate( String.valueOf( value ), recursionInterceptor, unresolvable );
 218  
 
 219  65
                         if ( postProcessors != null && !postProcessors.isEmpty() )
 220  
                         {
 221  2
                             for ( InterpolationPostProcessor postProcessor : postProcessors )
 222  
                             {
 223  2
                                 Object newVal = postProcessor.execute( realExpr, value );
 224  2
                                 if ( newVal != null )
 225  
                                 {
 226  1
                                     value = newVal;
 227  1
                                     break;
 228  
                                 }
 229  1
                             }
 230  
                         }
 231  
 
 232  
                         // could use:
 233  
                         // result = matcher.replaceFirst( stringValue );
 234  
                         // but this could result in multiple lookups of stringValue, and replaceAll is not correct
 235  
                         // behaviour
 236  65
                         result.append( String.valueOf( value ) );
 237  65
                         resolved = true;
 238  
                     }
 239  
                     else
 240  
                     {
 241  8
                         unresolvable.add( wholeExpr );
 242  
                     }
 243  
                 }
 244  
                 finally
 245  
                 {
 246  76
                     recursionInterceptor.expressionResolutionFinished( realExpr );
 247  73
                 }
 248  
             }
 249  
 
 250  73
             if ( !resolved )
 251  
             {
 252  8
                 result.append( wholeExpr );
 253  
             }
 254  
 
 255  73
             if ( endIdx > -1 )
 256  
             {
 257  73
                 endIdx += endExpr.length() - 1;
 258  
             }
 259  73
         }
 260  
 
 261  141
         if ( endIdx == -1 && startIdx > -1 )
 262  
         {
 263  1
             result.append( input, startIdx, input.length());
 264  
         }
 265  140
         else if ( endIdx < input.length() )
 266  
         {
 267  140
             result.append( input, endIdx + 1, input.length() );
 268  
         }
 269  
 
 270  141
         return result.toString();
 271  
     }
 272  
 
 273  
     /**
 274  
      * Return any feedback messages and errors that were generated - but
 275  
      * suppressed - during the interpolation process. Since unresolvable
 276  
      * expressions will be left in the source string as-is, this feedback is
 277  
      * optional, and will only be useful for debugging interpolation problems.
 278  
      *
 279  
      * @return a {@link List} that may be interspersed with {@link String} and
 280  
      *         {@link Throwable} instances.
 281  
      */
 282  
     public List getFeedback()
 283  
     {
 284  0
         List<?> messages = new ArrayList();
 285  0
         for ( ValueSource vs : valueSources )
 286  
         {
 287  0
             List feedback = vs.getFeedback();
 288  0
             if ( feedback != null && !feedback.isEmpty() )
 289  
             {
 290  0
                 messages.addAll( feedback );
 291  
             }
 292  0
         }
 293  
 
 294  0
         return messages;
 295  
     }
 296  
 
 297  
     /**
 298  
      * Clear the feedback messages from previous interpolate(..) calls.
 299  
      */
 300  
     public void clearFeedback()
 301  
     {
 302  0
         for ( ValueSource vs : valueSources )
 303  
         {
 304  0
             vs.clearFeedback();
 305  0
         }
 306  0
     }
 307  
 
 308  
     public boolean isCacheAnswers()
 309  
     {
 310  0
         return cacheAnswers;
 311  
     }
 312  
 
 313  
     public void setCacheAnswers( boolean cacheAnswers )
 314  
     {
 315  14
         this.cacheAnswers = cacheAnswers;
 316  14
     }
 317  
 
 318  
     public void clearAnswers()
 319  
     {
 320  0
         existingAnswers.clear();
 321  0
     }
 322  
 
 323  
     public String getEscapeString()
 324  
     {
 325  0
         return escapeString;
 326  
     }
 327  
 
 328  
     public void setEscapeString( String escapeString )
 329  
     {
 330  10
         this.escapeString = escapeString;
 331  10
     }
 332  
 
 333  
 }