Coverage Report - org.codehaus.plexus.interpolation.InterpolatorFilterReader
 
Classes in this File Line Coverage Branch Coverage Complexity
InterpolatorFilterReader
67%
81/120
62%
56/90
4.6
 
 1  
 /*
 2  
  * The Apache Software License, Version 1.1
 3  
  *
 4  
  * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
 5  
  * reserved.
 6  
  *
 7  
  * Redistribution and use in source and binary forms, with or without
 8  
  * modification, are permitted provided that the following conditions
 9  
  * are met:
 10  
  *
 11  
  * 1. Redistributions of source code must retain the above copyright
 12  
  *    notice, this list of conditions and the following disclaimer.
 13  
  *
 14  
  * 2. Redistributions in binary form must reproduce the above copyright
 15  
  *    notice, this list of conditions and the following disclaimer in
 16  
  *    the documentation and/or other materials provided with the
 17  
  *    distribution.
 18  
  *
 19  
  * 3. The end-user documentation included with the redistribution, if
 20  
  *    any, must include the following acknowlegement:
 21  
  *       "This product includes software developed by the
 22  
  *        Apache Software Foundation (http://www.codehaus.org/)."
 23  
  *    Alternately, this acknowlegement may appear in the software itself,
 24  
  *    if and wherever such third-party acknowlegements normally appear.
 25  
  *
 26  
  * 4. The names "Ant" and "Apache Software
 27  
  *    Foundation" must not be used to endorse or promote products derived
 28  
  *    from this software without prior written permission. For written
 29  
  *    permission, please contact codehaus@codehaus.org.
 30  
  *
 31  
  * 5. Products derived from this software may not be called "Apache"
 32  
  *    nor may "Apache" appear in their names without prior written
 33  
  *    permission of the Apache Group.
 34  
  *
 35  
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36  
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38  
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39  
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40  
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41  
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42  
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44  
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45  
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46  
  * SUCH DAMAGE.
 47  
  * ====================================================================
 48  
  *
 49  
  * This software consists of voluntary contributions made by many
 50  
  * individuals on behalf of the Apache Software Foundation.  For more
 51  
  * information on the Apache Software Foundation, please see
 52  
  * <http://www.codehaus.org/>.
 53  
  */
 54  
 
 55  
 package org.codehaus.plexus.interpolation;
 56  
 
 57  
 import java.io.FilterReader;
 58  
 import java.io.IOException;
 59  
 import java.io.Reader;
 60  
 
 61  
 /**
 62  
  * A FilterReader implementation, that works with Interpolator interface instead of it's own interpolation
 63  
  * implementation. This implementation is heavily based on org.codehaus.plexus.util.InterpolationFilterReader.
 64  
  *
 65  
  * @author cstamas
 66  
  */
 67  
 public class InterpolatorFilterReader
 68  
     extends FilterReader
 69  
 {
 70  
 
 71  
     /** Interpolator used to interpolate */
 72  
     private Interpolator interpolator;
 73  
     
 74  
     /**
 75  
      * @since 1.12
 76  
      */
 77  
     private RecursionInterceptor recursionInterceptor;
 78  
 
 79  
     /** replacement text from a token */
 80  14
     private String replaceData = null;
 81  
 
 82  
     /** Index into replacement data */
 83  14
     private int replaceIndex = -1;
 84  
 
 85  
     /** Index into previous data */
 86  14
     private int previousIndex = -1;
 87  
 
 88  
     /** Default begin token. */
 89  
     public static final String DEFAULT_BEGIN_TOKEN = "${";
 90  
 
 91  
     /** Default end token. */
 92  
     public static final String DEFAULT_END_TOKEN = "}";
 93  
     
 94  
     private String beginToken;
 95  
     
 96  
     private String orginalBeginToken;
 97  
     
 98  
     private String endToken;
 99  
     
 100  
     /** true by default to preserve backward comp */
 101  14
     private boolean interpolateWithPrefixPattern = true;
 102  
 
 103  
     private String escapeString;
 104  
     
 105  14
     private boolean useEscape = false;
 106  
     
 107  
     /** if true escapeString will be preserved \{foo} -> \{foo} */
 108  14
     private boolean preserveEscapeString = false;
 109  
     
 110  
     /**
 111  
      * this constructor use default begin token ${ and default end token } 
 112  
      * @param in reader to use
 113  
      * @param interpolator interpolator instance to use
 114  
      */
 115  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator )
 116  
     {
 117  8
         this( in, interpolator, DEFAULT_BEGIN_TOKEN, DEFAULT_END_TOKEN );
 118  8
     }
 119  
     
 120  
     /**
 121  
      * @param in reader to use
 122  
      * @param interpolator interpolator instance to use
 123  
      * @param beginToken start token to use
 124  
      * @param endToken end token to use
 125  
      */
 126  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, String beginToken, String endToken )
 127  
     {
 128  12
         this( in, interpolator, beginToken, endToken, new SimpleRecursionInterceptor() );
 129  12
     }    
 130  
 
 131  
     /**
 132  
      * this constructor use default begin token ${ and default end token } 
 133  
      * @param in reader to use
 134  
      * @param interpolator interpolator instance to use
 135  
      * @param ri The {@link RecursionInterceptor} to use to prevent recursive expressions.
 136  
      * @since 1.12
 137  
      */
 138  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, RecursionInterceptor ri )
 139  
     {
 140  2
         this( in, interpolator, DEFAULT_BEGIN_TOKEN, DEFAULT_END_TOKEN, ri );
 141  2
     }
 142  
     
 143  
     /**
 144  
      * @param in reader to use
 145  
      * @param interpolator interpolator instance to use
 146  
      * @param beginToken start token to use
 147  
      * @param endToken end token to use
 148  
      * @param ri The {@link RecursionInterceptor} to use to prevent recursive expressions.
 149  
      * @since 1.12
 150  
      */
 151  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, String beginToken, String endToken, RecursionInterceptor ri )
 152  
     {
 153  14
         super( in );
 154  
 
 155  14
         this.interpolator = interpolator;
 156  
         
 157  14
         this.beginToken = beginToken;
 158  
         
 159  14
         this.endToken = endToken;
 160  
         
 161  14
         recursionInterceptor = ri;
 162  
         
 163  14
         this.orginalBeginToken = this.beginToken;
 164  14
     }    
 165  
 
 166  
     /**
 167  
      * Skips characters. This method will block until some characters are available, an I/O error occurs, or the end of
 168  
      * the stream is reached.
 169  
      *
 170  
      * @param n The number of characters to skip
 171  
      * @return the number of characters actually skipped
 172  
      * @exception IllegalArgumentException If <code>n</code> is negative.
 173  
      * @exception IOException If an I/O error occurs
 174  
      */
 175  
     public long skip( long n )
 176  
         throws IOException
 177  
     {
 178  0
         if ( n < 0L )
 179  
         {
 180  0
             throw new IllegalArgumentException( "skip value is negative" );
 181  
         }
 182  
 
 183  0
         for ( long i = 0; i < n; i++ )
 184  
         {
 185  0
             if ( read() == -1 )
 186  
             {
 187  0
                 return i;
 188  
             }
 189  
         }
 190  0
         return n;
 191  
     }
 192  
 
 193  
     /**
 194  
      * Reads characters into a portion of an array. This method will block until some input is available, an I/O error
 195  
      * occurs, or the end of the stream is reached.
 196  
      *
 197  
      * @param cbuf Destination buffer to write characters to. Must not be <code>null</code>.
 198  
      * @param off Offset at which to start storing characters.
 199  
      * @param len Maximum number of characters to read.
 200  
      * @return the number of characters read, or -1 if the end of the stream has been reached
 201  
      * @exception IOException If an I/O error occurs
 202  
      */
 203  
     public int read( char cbuf[], int off, int len )
 204  
         throws IOException
 205  
     {
 206  323
         for ( int i = 0; i < len; i++ )
 207  
         {
 208  323
             int ch = read();
 209  323
             if ( ch == -1 )
 210  
             {
 211  28
                 if ( i == 0 )
 212  
                 {
 213  14
                     return -1;
 214  
                 }
 215  
                 else
 216  
                 {
 217  14
                     return i;
 218  
                 }
 219  
             }
 220  295
             cbuf[off + i] = (char) ch;
 221  
         }
 222  0
         return len;
 223  
     }
 224  
 
 225  
     /**
 226  
      * Returns the next character in the filtered stream, replacing tokens from the original stream.
 227  
      *
 228  
      * @return the next character in the resulting stream, or -1 if the end of the resulting stream has been reached
 229  
      * @exception IOException if the underlying stream throws an IOException during reading
 230  
      */
 231  
     public int read()
 232  
         throws IOException
 233  
     {
 234  344
         if ( replaceIndex != -1 && replaceIndex < replaceData.length() )
 235  
         {
 236  183
             int ch = replaceData.charAt( replaceIndex++ );
 237  183
             if ( replaceIndex >= replaceData.length() )
 238  
             {
 239  23
                 replaceIndex = -1;
 240  
             }
 241  183
             return ch;
 242  
         }
 243  
 
 244  161
         int ch = -1;
 245  161
         if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 246  
         {
 247  0
             ch = this.endToken.charAt( previousIndex++ );
 248  
         }
 249  
         else
 250  
         {
 251  161
             ch = in.read();
 252  
         }
 253  
         
 254  161
         if ( ch == this.beginToken.charAt( 0 ) || ( useEscape && ch == this.orginalBeginToken.charAt( 0 ) ) )
 255  
         {
 256  23
             StringBuilder key = new StringBuilder( );
 257  
 
 258  23
             key.append( (char) ch );
 259  
 
 260  23
             int beginTokenMatchPos = 1;
 261  
 
 262  
             do
 263  
             {
 264  165
                 if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 265  
                 {
 266  0
                     ch = this.endToken.charAt( previousIndex++ );
 267  
                 }
 268  
                 else
 269  
                 {
 270  165
                     ch = in.read();
 271  
                 }
 272  165
                 if ( ch != -1 )
 273  
                 {
 274  163
                     key.append( (char) ch );
 275  163
                     if ( ( beginTokenMatchPos < this.beginToken.length() )
 276  33
                         && ( ch != this.beginToken.charAt( beginTokenMatchPos++ ) )
 277  15
                         && ( useEscape && this.orginalBeginToken.length() > ( beginTokenMatchPos - 1 ) && ch != this.orginalBeginToken
 278  6
                             .charAt( beginTokenMatchPos - 1 ) ) )
 279  
                     {
 280  0
                         ch = -1; // not really EOF but to trigger code below
 281  0
                         break;
 282  
                     }
 283  
                 }
 284  
                 else
 285  
                 {
 286  
                     break;
 287  
                 }
 288  
                 // MSHARED-81 olamy : we must take care of token with length 1, escaping and same char : \@foo@
 289  
                 // here ch == endToken == beginToken -> not going to next char : bad :-)
 290  163
                 if ( useEscape && this.orginalBeginToken == this.endToken && key.toString().startsWith( this.beginToken ) )
 291  
                 {
 292  3
                     ch = in.read();
 293  3
                     key.append( (char) ch );
 294  
                 }
 295  
             }
 296  163
             while ( ch != this.endToken.charAt( 0 ) );
 297  
 
 298  
             // now test endToken
 299  23
             if ( ch != -1 && this.endToken.length() > 1 )
 300  
             {
 301  0
                 int endTokenMatchPos = 1;
 302  
 
 303  
                 do
 304  
                 {
 305  0
                     if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 306  
                     {
 307  0
                         ch = this.endToken.charAt( previousIndex++ );
 308  
                     }
 309  
                     else
 310  
                     {
 311  0
                         ch = in.read();
 312  
                     }
 313  
 
 314  0
                     if ( ch != -1 )
 315  
                     {
 316  0
                         key.append( (char) ch );
 317  
 
 318  0
                         if ( ch != this.endToken.charAt( endTokenMatchPos++ ) )
 319  
                         {
 320  0
                             ch = -1; // not really EOF but to trigger code below
 321  0
                             break;
 322  
                         }
 323  
 
 324  
                     }
 325  
                     else
 326  
                     {
 327  
                         break;
 328  
                     }
 329  
                 }
 330  0
                 while ( endTokenMatchPos < this.endToken.length() );
 331  
             }
 332  
 
 333  
             // There is nothing left to read so we have the situation where the begin/end token
 334  
             // are in fact the same and as there is nothing left to read we have got ourselves
 335  
             // end of a token boundary so let it pass through.
 336  23
             if ( ch == -1 )
 337  
             {
 338  2
                 replaceData = key.toString();
 339  2
                 replaceIndex = 1;
 340  2
                 return replaceData.charAt( 0 );
 341  
             }
 342  
 
 343  21
             String value = null;
 344  
             try
 345  
             {
 346  21
                 boolean escapeFound = false;
 347  21
                 if ( useEscape )
 348  
                 {
 349  14
                     if ( key.toString().startsWith( escapeString + orginalBeginToken ) )
 350  
                     {
 351  5
                         String keyStr = key.toString();
 352  5
                         if ( !preserveEscapeString )
 353  
                         {
 354  5
                             value = keyStr.substring( escapeString.length(), keyStr.length() );
 355  
                         }
 356  
                         else
 357  
                         {
 358  0
                             value = keyStr;
 359  
                         }
 360  5
                         escapeFound = true;
 361  
                     }
 362  
                 }
 363  21
                 if ( !escapeFound )
 364  
                 {
 365  16
                     if ( interpolateWithPrefixPattern )
 366  
                     {
 367  0
                         value = interpolator.interpolate( key.toString(), "", recursionInterceptor );
 368  
                     }
 369  
                     else
 370  
                     {
 371  16
                         value = interpolator.interpolate( key.toString(), recursionInterceptor );
 372  
                     }
 373  
                 }
 374  
             }
 375  0
             catch ( InterpolationException e )
 376  
             {
 377  0
                 IllegalArgumentException error = new IllegalArgumentException( e.getMessage() );
 378  0
                 error.initCause( e );
 379  
 
 380  0
                 throw error;
 381  21
             }
 382  
 
 383  21
             if ( value != null )
 384  
             {
 385  21
                 if ( value.length() != 0 )
 386  
                 {
 387  21
                     replaceData = value;
 388  21
                     replaceIndex = 0;
 389  
                 }
 390  21
                 return read();
 391  
             }
 392  
             else
 393  
             {
 394  0
                 previousIndex = 0;
 395  0
                 replaceData = key.substring( 0, key.length() - this.endToken.length() );
 396  0
                 replaceIndex = 0;
 397  0
                 return this.beginToken.charAt( 0 );
 398  
             }
 399  
         }
 400  
 
 401  138
         return ch;
 402  
     }
 403  
 
 404  
     public boolean isInterpolateWithPrefixPattern()
 405  
     {
 406  0
         return interpolateWithPrefixPattern;
 407  
     }
 408  
 
 409  
     public void setInterpolateWithPrefixPattern( boolean interpolateWithPrefixPattern )
 410  
     {
 411  14
         this.interpolateWithPrefixPattern = interpolateWithPrefixPattern;
 412  14
     }
 413  
     public String getEscapeString()
 414  
     {
 415  0
         return escapeString;
 416  
     }
 417  
 
 418  
     public void setEscapeString( String escapeString )
 419  
     {
 420  
         // TODO NPE if escapeString is null ?
 421  8
         if ( escapeString != null && escapeString.length() >= 1 )
 422  
         {
 423  8
             this.escapeString = escapeString;
 424  8
             this.orginalBeginToken = beginToken;
 425  8
             this.beginToken = escapeString + beginToken;
 426  8
             this.useEscape = escapeString != null && escapeString.length() >= 1;
 427  
         }
 428  8
     }
 429  
 
 430  
     public boolean isPreserveEscapeString()
 431  
     {
 432  0
         return preserveEscapeString;
 433  
     }
 434  
 
 435  
     public void setPreserveEscapeString( boolean preserveEscapeString )
 436  
     {
 437  0
         this.preserveEscapeString = preserveEscapeString;
 438  0
     }
 439  
 
 440  
     public RecursionInterceptor getRecursionInterceptor()
 441  
     {
 442  0
         return recursionInterceptor;
 443  
     }
 444  
 
 445  
     public InterpolatorFilterReader setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
 446  
     {
 447  0
         this.recursionInterceptor = recursionInterceptor;
 448  0
         return this;
 449  
     }
 450  
 }