Coverage Report - org.codehaus.plexus.interpolation.InterpolatorFilterReader
 
Classes in this File Line Coverage Branch Coverage Complexity
InterpolatorFilterReader
66 %
78/117
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  
  * @version $Id$
 67  
  */
 68  
 public class InterpolatorFilterReader
 69  
     extends FilterReader
 70  
 {
 71  
 
 72  
     /** Interpolator used to interpolate */
 73  
     private Interpolator interpolator;
 74  
     
 75  
     /**
 76  
      * @since 1.12
 77  
      */
 78  
     private RecursionInterceptor recursionInterceptor;
 79  
 
 80  
     /** replacement text from a token */
 81  14
     private String replaceData = null;
 82  
 
 83  
     /** Index into replacement data */
 84  14
     private int replaceIndex = -1;
 85  
 
 86  
     /** Index into previous data */
 87  14
     private int previousIndex = -1;
 88  
 
 89  
     /** Default begin token. */
 90  
     public static final String DEFAULT_BEGIN_TOKEN = "${";
 91  
 
 92  
     /** Default end token. */
 93  
     public static final String DEFAULT_END_TOKEN = "}";
 94  
     
 95  
     private String beginToken;
 96  
     
 97  
     private String orginalBeginToken;
 98  
     
 99  
     private String endToken;
 100  
     
 101  
     /** true by default to preserve backward comp */
 102  14
     private boolean interpolateWithPrefixPattern = true;
 103  
 
 104  
     private String escapeString;
 105  
     
 106  14
     private boolean useEscape = false;
 107  
     
 108  
     /** if true escapeString will be preserved \{foo} -> \{foo} */
 109  14
     private boolean preserveEscapeString = false;
 110  
     
 111  
     /**
 112  
      * this constructor use default begin token ${ and default end token } 
 113  
      * @param in reader to use
 114  
      * @param interpolator interpolator instance to use
 115  
      */
 116  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator )
 117  
     {
 118  8
         this( in, interpolator, DEFAULT_BEGIN_TOKEN, DEFAULT_END_TOKEN );
 119  8
     }
 120  
     
 121  
     /**
 122  
      * @param in reader to use
 123  
      * @param interpolator interpolator instance to use
 124  
      * @param beginToken start token to use
 125  
      * @param endToken end token to use
 126  
      */
 127  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, String beginToken, String endToken )
 128  
     {
 129  12
         this( in, interpolator, beginToken, endToken, new SimpleRecursionInterceptor() );
 130  12
     }    
 131  
 
 132  
     /**
 133  
      * this constructor use default begin token ${ and default end token } 
 134  
      * @param in reader to use
 135  
      * @param interpolator interpolator instance to use
 136  
      * @param ri The {@link RecursionInterceptor} to use to prevent recursive expressions.
 137  
      * @since 1.12
 138  
      */
 139  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, RecursionInterceptor ri )
 140  
     {
 141  2
         this( in, interpolator, DEFAULT_BEGIN_TOKEN, DEFAULT_END_TOKEN, new SimpleRecursionInterceptor() );
 142  2
     }
 143  
     
 144  
     /**
 145  
      * @param in reader to use
 146  
      * @param interpolator interpolator instance to use
 147  
      * @param beginToken start token to use
 148  
      * @param endToken end token to use
 149  
      * @param ri The {@link RecursionInterceptor} to use to prevent recursive expressions.
 150  
      * @since 1.12
 151  
      */
 152  
     public InterpolatorFilterReader( Reader in, Interpolator interpolator, String beginToken, String endToken, RecursionInterceptor ri )
 153  
     {
 154  14
         super( in );
 155  
 
 156  14
         this.interpolator = interpolator;
 157  
         
 158  14
         this.beginToken = beginToken;
 159  
         
 160  14
         this.endToken = endToken;
 161  
         
 162  14
         recursionInterceptor = ri;
 163  
         
 164  14
         this.orginalBeginToken = this.beginToken;
 165  14
     }    
 166  
 
 167  
     /**
 168  
      * Skips characters. This method will block until some characters are available, an I/O error occurs, or the end of
 169  
      * the stream is reached.
 170  
      *
 171  
      * @param n The number of characters to skip
 172  
      * @return the number of characters actually skipped
 173  
      * @exception IllegalArgumentException If <code>n</code> is negative.
 174  
      * @exception IOException If an I/O error occurs
 175  
      */
 176  
     public long skip( long n )
 177  
         throws IOException
 178  
     {
 179  0
         if ( n < 0L )
 180  
         {
 181  0
             throw new IllegalArgumentException( "skip value is negative" );
 182  
         }
 183  
 
 184  0
         for ( long i = 0; i < n; i++ )
 185  
         {
 186  0
             if ( read() == -1 )
 187  
             {
 188  0
                 return i;
 189  
             }
 190  
         }
 191  0
         return n;
 192  
     }
 193  
 
 194  
     /**
 195  
      * Reads characters into a portion of an array. This method will block until some input is available, an I/O error
 196  
      * occurs, or the end of the stream is reached.
 197  
      *
 198  
      * @param cbuf Destination buffer to write characters to. Must not be <code>null</code>.
 199  
      * @param off Offset at which to start storing characters.
 200  
      * @param len Maximum number of characters to read.
 201  
      * @return the number of characters read, or -1 if the end of the stream has been reached
 202  
      * @exception IOException If an I/O error occurs
 203  
      */
 204  
     public int read( char cbuf[], int off, int len )
 205  
         throws IOException
 206  
     {
 207  323
         for ( int i = 0; i < len; i++ )
 208  
         {
 209  323
             int ch = read();
 210  323
             if ( ch == -1 )
 211  
             {
 212  28
                 if ( i == 0 )
 213  
                 {
 214  14
                     return -1;
 215  
                 }
 216  
                 else
 217  
                 {
 218  14
                     return i;
 219  
                 }
 220  
             }
 221  295
             cbuf[off + i] = (char) ch;
 222  
         }
 223  0
         return len;
 224  
     }
 225  
 
 226  
     /**
 227  
      * Returns the next character in the filtered stream, replacing tokens from the original stream.
 228  
      *
 229  
      * @return the next character in the resulting stream, or -1 if the end of the resulting stream has been reached
 230  
      * @exception IOException if the underlying stream throws an IOException during reading
 231  
      */
 232  
     public int read()
 233  
         throws IOException
 234  
     {
 235  344
         if ( replaceIndex != -1 && replaceIndex < replaceData.length() )
 236  
         {
 237  183
             int ch = replaceData.charAt( replaceIndex++ );
 238  183
             if ( replaceIndex >= replaceData.length() )
 239  
             {
 240  23
                 replaceIndex = -1;
 241  
             }
 242  183
             return ch;
 243  
         }
 244  
 
 245  161
         int ch = -1;
 246  161
         if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 247  
         {
 248  0
             ch = this.endToken.charAt( previousIndex++ );
 249  
         }
 250  
         else
 251  
         {
 252  161
             ch = in.read();
 253  
         }
 254  
         
 255  161
         if ( ch == this.beginToken.charAt( 0 ) || ( useEscape && ch == this.orginalBeginToken.charAt( 0 ) ) )
 256  
         {
 257  23
             StringBuilder key = new StringBuilder( );
 258  
 
 259  23
             key.append( (char) ch );
 260  
 
 261  23
             int beginTokenMatchPos = 1;
 262  
 
 263  
             do
 264  
             {
 265  165
                 if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 266  
                 {
 267  0
                     ch = this.endToken.charAt( previousIndex++ );
 268  
                 }
 269  
                 else
 270  
                 {
 271  165
                     ch = in.read();
 272  
                 }
 273  165
                 if ( ch != -1 )
 274  
                 {
 275  163
                     key.append( (char) ch );
 276  163
                     if ( ( beginTokenMatchPos < this.beginToken.length() )
 277  
                         && ( ch != this.beginToken.charAt( beginTokenMatchPos++ ) )
 278  
                         && ( useEscape && this.orginalBeginToken.length() > ( beginTokenMatchPos - 1 ) && ch != this.orginalBeginToken
 279  
                             .charAt( beginTokenMatchPos - 1 ) ) )
 280  
                     {
 281  0
                         ch = -1; // not really EOF but to trigger code below
 282  0
                         break;
 283  
                     }
 284  
                 }
 285  
                 else
 286  
                 {
 287  
                     break;
 288  
                 }
 289  
                 // MSHARED-81 olamy : we must take care of token with length 1, escaping and same char : \@foo@
 290  
                 // here ch == endToken == beginToken -> not going to next char : bad :-)
 291  163
                 if ( useEscape && this.orginalBeginToken == this.endToken && key.toString().startsWith( this.beginToken ) )
 292  
                 {
 293  3
                     ch = in.read();
 294  3
                     key.append( (char) ch );
 295  
                 }
 296  
             }
 297  163
             while ( ch != this.endToken.charAt( 0 ) );
 298  
 
 299  
             // now test endToken
 300  23
             if ( ch != -1 && this.endToken.length() > 1 )
 301  
             {
 302  0
                 int endTokenMatchPos = 1;
 303  
 
 304  
                 do
 305  
                 {
 306  0
                     if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 307  
                     {
 308  0
                         ch = this.endToken.charAt( previousIndex++ );
 309  
                     }
 310  
                     else
 311  
                     {
 312  0
                         ch = in.read();
 313  
                     }
 314  
 
 315  0
                     if ( ch != -1 )
 316  
                     {
 317  0
                         key.append( (char) ch );
 318  
 
 319  0
                         if ( ch != this.endToken.charAt( endTokenMatchPos++ ) )
 320  
                         {
 321  0
                             ch = -1; // not really EOF but to trigger code below
 322  0
                             break;
 323  
                         }
 324  
 
 325  
                     }
 326  
                     else
 327  
                     {
 328  
                         break;
 329  
                     }
 330  
                 }
 331  0
                 while ( endTokenMatchPos < this.endToken.length() );
 332  
             }
 333  
 
 334  
             // There is nothing left to read so we have the situation where the begin/end token
 335  
             // are in fact the same and as there is nothing left to read we have got ourselves
 336  
             // end of a token boundary so let it pass through.
 337  23
             if ( ch == -1 )
 338  
             {
 339  2
                 replaceData = key.toString();
 340  2
                 replaceIndex = 1;
 341  2
                 return replaceData.charAt( 0 );
 342  
             }
 343  
 
 344  21
             String value = null;
 345  
             try
 346  
             {
 347  21
                 boolean escapeFound = false;
 348  21
                 if ( useEscape )
 349  
                 {
 350  14
                     if ( key.toString().startsWith( escapeString + orginalBeginToken ) )
 351  
                     {
 352  5
                         String keyStr = key.toString();
 353  5
                         if ( !preserveEscapeString )
 354  
                         {
 355  5
                             value = keyStr.substring( escapeString.length(), keyStr.length() );
 356  
                         }
 357  
                         else
 358  
                         {
 359  0
                             value = keyStr;
 360  
                         }
 361  5
                         escapeFound = true;
 362  
                     }
 363  
                 }
 364  21
                 if ( !escapeFound )
 365  
                 {
 366  16
                     if ( interpolateWithPrefixPattern )
 367  
                     {
 368  0
                         value = interpolator.interpolate( key.toString(), "", recursionInterceptor );
 369  
                     }
 370  
                     else
 371  
                     {
 372  16
                         value = interpolator.interpolate( key.toString(), recursionInterceptor );
 373  
                     }
 374  
                 }
 375  
             }
 376  0
             catch ( InterpolationException e )
 377  
             {
 378  0
                 IllegalArgumentException error = new IllegalArgumentException( e.getMessage() );
 379  0
                 error.initCause( e );
 380  
 
 381  0
                 throw error;
 382  21
             }
 383  
 
 384  21
             if ( value != null )
 385  
             {
 386  21
                 if ( value.length() != 0 )
 387  
                 {
 388  21
                     replaceData = value;
 389  21
                     replaceIndex = 0;
 390  
                 }
 391  21
                 return read();
 392  
             }
 393  
             else
 394  
             {
 395  0
                 previousIndex = 0;
 396  0
                 replaceData = key.substring( 0, key.length() - this.endToken.length() );
 397  0
                 replaceIndex = 0;
 398  0
                 return this.beginToken.charAt( 0 );
 399  
             }
 400  
         }
 401  
 
 402  138
         return ch;
 403  
     }
 404  
 
 405  
     public boolean isInterpolateWithPrefixPattern()
 406  
     {
 407  0
         return interpolateWithPrefixPattern;
 408  
     }
 409  
 
 410  
     public void setInterpolateWithPrefixPattern( boolean interpolateWithPrefixPattern )
 411  
     {
 412  14
         this.interpolateWithPrefixPattern = interpolateWithPrefixPattern;
 413  14
     }
 414  
     public String getEscapeString()
 415  
     {
 416  0
         return escapeString;
 417  
     }
 418  
 
 419  
     public void setEscapeString( String escapeString )
 420  
     {
 421  
         // TODO NPE if escapeString is null ?
 422  8
         if ( escapeString != null && escapeString.length() >= 1 )
 423  
         {
 424  8
             this.escapeString = escapeString;
 425  8
             this.orginalBeginToken = beginToken;
 426  8
             this.beginToken = escapeString + beginToken;
 427  8
             this.useEscape = escapeString != null && escapeString.length() >= 1;
 428  
         }
 429  8
     }
 430  
 
 431  
     public boolean isPreserveEscapeString()
 432  
     {
 433  0
         return preserveEscapeString;
 434  
     }
 435  
 
 436  
     public void setPreserveEscapeString( boolean preserveEscapeString )
 437  
     {
 438  0
         this.preserveEscapeString = preserveEscapeString;
 439  0
     }
 440  
 
 441  
     public RecursionInterceptor getRecursionInterceptor()
 442  
     {
 443  0
         return recursionInterceptor;
 444  
     }
 445  
 
 446  
     public InterpolatorFilterReader setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
 447  
     {
 448  0
         this.recursionInterceptor = recursionInterceptor;
 449  0
         return this;
 450  
     }
 451  
 }