Coverage Report - org.apache.maven.shared.filtering.InterpolatorFilterReaderLineEnding
 
Classes in this File Line Coverage Branch Coverage Complexity
InterpolatorFilterReaderLineEnding
47%
55/117
28%
29/102
4,154
 
 1  
 package org.apache.maven.shared.filtering;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *    http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.FilterReader;
 23  
 import java.io.IOException;
 24  
 import java.io.Reader;
 25  
 
 26  
 import org.codehaus.plexus.interpolation.InterpolationException;
 27  
 import org.codehaus.plexus.interpolation.Interpolator;
 28  
 import org.codehaus.plexus.interpolation.RecursionInterceptor;
 29  
 import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
 30  
 
 31  
 /**
 32  
  * A FilterReader implementation, that works with Interpolator interface instead of it's own interpolation
 33  
  * implementation. This implementation is heavily based on org.codehaus.plexus.util.InterpolationFilterReader.
 34  
  *
 35  
  * @author cstamas
 36  
  * @author Olivier Lamy
 37  
  * @version $Id: InterpolatorFilterReaderLineEnding.java 1057398 2011-01-10 22:18:08Z dennisl $
 38  
  * @since 1.0
 39  
  */
 40  
 public class InterpolatorFilterReaderLineEnding
 41  
     extends FilterReader
 42  
 {
 43  
 
 44  
     /** Interpolator used to interpolate */
 45  
     private Interpolator interpolator;
 46  
 
 47  
     private RecursionInterceptor recursionInterceptor;
 48  
 
 49  
     /** replacement text from a token */
 50  7
     private String replaceData = null;
 51  
 
 52  
     /** Index into replacement data */
 53  7
     private int replaceIndex = -1;
 54  
 
 55  
     /** Index into previous data */
 56  7
     private int previousIndex = -1;
 57  
 
 58  
     /** Default begin token. */
 59  
     public static final String DEFAULT_BEGIN_TOKEN = "${";
 60  
 
 61  
     /** Default end token. */
 62  
     public static final String DEFAULT_END_TOKEN = "}";
 63  
     
 64  
     private String beginToken;
 65  
     
 66  
     private String orginalBeginToken;
 67  
     
 68  
     private String endToken;
 69  
     
 70  
     /** true by default to preserve backward comp */
 71  7
     private boolean interpolateWithPrefixPattern = true;
 72  
 
 73  
     private String escapeString;
 74  
     
 75  7
     private boolean useEscape = false;
 76  
     
 77  
     /** if true escapeString will be preserved \{foo} -> \{foo} */
 78  7
     private boolean preserveEscapeString = false;
 79  
     
 80  
     private boolean supportMultiLineFiltering;
 81  
         
 82  
     /**
 83  
      * @param in reader to use
 84  
      * @param interpolator interpolator instance to use
 85  
      * @param beginToken start token to use
 86  
      * @param endToken end token to use
 87  
      * @param supportMultiLineFiltering If multi line filtering is allowed
 88  
      */
 89  
     public InterpolatorFilterReaderLineEnding( Reader in, Interpolator interpolator, String beginToken, String endToken,
 90  
                                                boolean supportMultiLineFiltering )
 91  
     {
 92  7
         this( in, interpolator, beginToken, endToken, new SimpleRecursionInterceptor(), supportMultiLineFiltering );
 93  7
     }    
 94  
     
 95  
     /**
 96  
      * @param in reader to use
 97  
      * @param interpolator interpolator instance to use
 98  
      * @param beginToken start token to use
 99  
      * @param endToken end token to use
 100  
      * @param ri The {@link RecursionInterceptor} to use to prevent recursive expressions.
 101  
      * @param supportMultiLineFiltering If multi line filtering is allowed
 102  
      */
 103  
     private InterpolatorFilterReaderLineEnding( Reader in, Interpolator interpolator, String beginToken,
 104  
                                                 String endToken, RecursionInterceptor ri,
 105  
                                                 boolean supportMultiLineFiltering )
 106  
     {
 107  7
         super( in );
 108  
 
 109  7
         this.interpolator = interpolator;
 110  
         
 111  7
         this.beginToken = beginToken;
 112  
         
 113  7
         this.endToken = endToken;
 114  
         
 115  7
         recursionInterceptor = ri;
 116  
         
 117  7
         this.orginalBeginToken = this.beginToken;
 118  
         
 119  7
         this.supportMultiLineFiltering = supportMultiLineFiltering;
 120  7
     }    
 121  
 
 122  
     /**
 123  
      * Skips characters. This method will block until some characters are available, an I/O error occurs, or the end of
 124  
      * the stream is reached.
 125  
      *
 126  
      * @param n The number of characters to skip
 127  
      * @return the number of characters actually skipped
 128  
      * @exception IllegalArgumentException If <code>n</code> is negative.
 129  
      * @exception IOException If an I/O error occurs
 130  
      */
 131  
     public long skip( long n )
 132  
         throws IOException
 133  
     {
 134  0
         if ( n < 0L )
 135  
         {
 136  0
             throw new IllegalArgumentException( "skip value is negative" );
 137  
         }
 138  
 
 139  0
         for ( long i = 0; i < n; i++ )
 140  
         {
 141  0
             if ( read() == -1 )
 142  
             {
 143  0
                 return i;
 144  
             }
 145  
         }
 146  0
         return n;
 147  
     }
 148  
 
 149  
     /**
 150  
      * Reads characters into a portion of an array. This method will block until some input is available, an I/O error
 151  
      * occurs, or the end of the stream is reached.
 152  
      *
 153  
      * @param cbuf Destination buffer to write characters to. Must not be <code>null</code>.
 154  
      * @param off Offset at which to start storing characters.
 155  
      * @param len Maximum number of characters to read.
 156  
      * @return the number of characters read, or -1 if the end of the stream has been reached
 157  
      * @exception IOException If an I/O error occurs
 158  
      */
 159  
     public int read( char cbuf[], int off, int len )
 160  
         throws IOException
 161  
     {
 162  0
         for ( int i = 0; i < len; i++ )
 163  
         {
 164  0
             int ch = read();
 165  0
             if ( ch == -1 )
 166  
             {
 167  0
                 if ( i == 0 )
 168  
                 {
 169  0
                     return -1;
 170  
                 }
 171  
                 else
 172  
                 {
 173  0
                     return i;
 174  
                 }
 175  
             }
 176  0
             cbuf[off + i] = (char) ch;
 177  
         }
 178  0
         return len;
 179  
     }
 180  
 
 181  
     /**
 182  
      * Returns the next character in the filtered stream, replacing tokens from the original stream.
 183  
      *
 184  
      * @return the next character in the resulting stream, or -1 if the end of the resulting stream has been reached
 185  
      * @exception IOException if the underlying stream throws an IOException during reading
 186  
      */
 187  
     public int read()
 188  
         throws IOException
 189  
     {
 190  2073
         if ( replaceIndex != -1 && replaceIndex < replaceData.length() )
 191  
         {
 192  34
             int ch = replaceData.charAt( replaceIndex++ );
 193  34
             if ( replaceIndex >= replaceData.length() )
 194  
             {
 195  4
                 replaceIndex = -1;
 196  
             }
 197  34
             return ch;
 198  
         }
 199  
 
 200  2039
         int ch = -1;
 201  2039
         if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 202  
         {
 203  0
             ch = this.endToken.charAt( previousIndex++ );
 204  
         }
 205  
         else
 206  
         {
 207  2039
             ch = in.read();
 208  
         }
 209  
         
 210  2039
         if ( ch == '\n' && !supportMultiLineFiltering )
 211  
         {
 212  52
             previousIndex = -1;
 213  52
             return ch;
 214  
         }        
 215  
         
 216  1987
         if ( ch == this.beginToken.charAt( 0 ) || ( useEscape && ch == this.orginalBeginToken.charAt( 0 ) ) )
 217  
         {
 218  4
             StringBuffer key = new StringBuffer( );
 219  
 
 220  4
             key.append( (char) ch );
 221  
 
 222  4
             int beginTokenMatchPos = 1;
 223  
 
 224  
             do
 225  
             {
 226  28
                 if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 227  
                 {
 228  0
                     ch = this.endToken.charAt( previousIndex++ );
 229  
                 }
 230  
                 else
 231  
                 {
 232  28
                     ch = in.read();
 233  
                 }
 234  28
                 if ( ch != -1 && ( ch != '\n' && !supportMultiLineFiltering ) )
 235  
                 {
 236  28
                     key.append( (char) ch );
 237  28
                     if ( ( beginTokenMatchPos < this.beginToken.length() )
 238  
                         && ( ch != this.beginToken.charAt( beginTokenMatchPos++ ) )
 239  
                         && ( useEscape && this.orginalBeginToken.length() > ( beginTokenMatchPos - 1 )
 240  
                         && ch != this.orginalBeginToken.charAt( beginTokenMatchPos - 1 ) ) )
 241  
                     {
 242  0
                         ch = -1; // not really EOF but to trigger code below
 243  0
                         break;
 244  
                     }
 245  
                 }
 246  
                 else
 247  
                 {
 248  
                     break;
 249  
                 }
 250  
                 // MSHARED-81 olamy : we must take care of token with length 1, escaping and same char : \@foo@
 251  
                 // here ch == endToken == beginToken -> not going to next char : bad :-)
 252  28
                 if ( useEscape
 253  
                     && this.orginalBeginToken == this.endToken && key.toString().startsWith( this.beginToken ) )
 254  
                 {
 255  0
                     ch = in.read();
 256  0
                     key.append( (char) ch );
 257  
                 }
 258  
             }
 259  28
             while ( ch != this.endToken.charAt( 0 ) );
 260  
 
 261  
             // now test endToken
 262  4
             if ( ch != -1 && this.endToken.length() > 1 )
 263  
             {
 264  0
                 int endTokenMatchPos = 1;
 265  
 
 266  
                 do
 267  
                 {
 268  0
                     if ( previousIndex != -1 && previousIndex < this.endToken.length() )
 269  
                     {
 270  0
                         ch = this.endToken.charAt( previousIndex++ );
 271  
                     }
 272  
                     else
 273  
                     {
 274  0
                         ch = in.read();
 275  
                     }
 276  
 
 277  0
                     if ( ch != -1 )
 278  
                     {
 279  0
                         key.append( (char) ch );
 280  
 
 281  0
                         if ( ch != this.endToken.charAt( endTokenMatchPos++ ) )
 282  
                         {
 283  0
                             ch = -1; // not really EOF but to trigger code below
 284  0
                             break;
 285  
                         }
 286  
 
 287  
                     }
 288  
                     else
 289  
                     {
 290  
                         break;
 291  
                     }
 292  
                 }
 293  0
                 while ( endTokenMatchPos < this.endToken.length() );
 294  
             }
 295  
 
 296  
             // There is nothing left to read so we have the situation where the begin/end token
 297  
             // are in fact the same and as there is nothing left to read we have got ourselves
 298  
             // end of a token boundary so let it pass through.
 299  4
             if ( ch == -1 || ( ch == '\n' && !supportMultiLineFiltering ) )
 300  
             {
 301  0
                 replaceData = key.toString();
 302  0
                 replaceIndex = 1;
 303  0
                 return replaceData.charAt( 0 );
 304  
             }
 305  
 
 306  4
             String value = null;
 307  
             try
 308  
             {
 309  4
                 boolean escapeFound = false;
 310  4
                 if ( useEscape )
 311  
                 {
 312  0
                     if ( key.toString().startsWith( escapeString + orginalBeginToken ) )
 313  
                     {
 314  0
                         String keyStr = key.toString();
 315  0
                         if ( !preserveEscapeString )
 316  
                         {
 317  0
                             value = keyStr.substring( escapeString.length(), keyStr.length() );
 318  
                         }
 319  
                         else
 320  
                         {
 321  0
                             value = keyStr;
 322  
                         }
 323  0
                         escapeFound = true;
 324  
                     }
 325  
                 }
 326  4
                 if ( !escapeFound )
 327  
                 {
 328  4
                     if ( interpolateWithPrefixPattern )
 329  
                     {
 330  0
                         value = interpolator.interpolate( key.toString(), "", recursionInterceptor );
 331  
                     }
 332  
                     else
 333  
                     {
 334  4
                         value = interpolator.interpolate( key.toString(), recursionInterceptor );
 335  
                     }
 336  
                 }
 337  
             }
 338  0
             catch ( InterpolationException e )
 339  
             {
 340  0
                 IllegalArgumentException error = new IllegalArgumentException( e.getMessage() );
 341  0
                 error.initCause( e );
 342  
 
 343  0
                 throw error;
 344  4
             }
 345  
 
 346  4
             if ( value != null )
 347  
             {
 348  4
                 if ( value.length() != 0 )
 349  
                 {
 350  4
                     replaceData = value;
 351  4
                     replaceIndex = 0;
 352  
                 }
 353  4
                 return read();
 354  
             }
 355  
             else
 356  
             {
 357  0
                 previousIndex = 0;
 358  0
                 replaceData = key.substring( 0, key.length() - this.endToken.length() );
 359  0
                 replaceIndex = 0;
 360  0
                 return this.beginToken.charAt( 0 );
 361  
             }
 362  
         }
 363  
 
 364  1983
         return ch;
 365  
     }
 366  
 
 367  
     public boolean isInterpolateWithPrefixPattern()
 368  
     {
 369  0
         return interpolateWithPrefixPattern;
 370  
     }
 371  
 
 372  
     public void setInterpolateWithPrefixPattern( boolean interpolateWithPrefixPattern )
 373  
     {
 374  7
         this.interpolateWithPrefixPattern = interpolateWithPrefixPattern;
 375  7
     }
 376  
     public String getEscapeString()
 377  
     {
 378  0
         return escapeString;
 379  
     }
 380  
 
 381  
     public void setEscapeString( String escapeString )
 382  
     {
 383  
         // TODO NPE if escapeString is null ?
 384  0
         if ( escapeString != null && escapeString.length() >= 1 )
 385  
         {
 386  0
             this.escapeString = escapeString;
 387  0
             this.orginalBeginToken = beginToken;
 388  0
             this.beginToken = escapeString + beginToken;
 389  0
             this.useEscape = escapeString != null && escapeString.length() >= 1;
 390  
         }
 391  0
     }
 392  
 
 393  
     public boolean isPreserveEscapeString()
 394  
     {
 395  0
         return preserveEscapeString;
 396  
     }
 397  
 
 398  
     public void setPreserveEscapeString( boolean preserveEscapeString )
 399  
     {
 400  0
         this.preserveEscapeString = preserveEscapeString;
 401  0
     }
 402  
 
 403  
     public RecursionInterceptor getRecursionInterceptor()
 404  
     {
 405  0
         return recursionInterceptor;
 406  
     }
 407  
 
 408  
     public InterpolatorFilterReaderLineEnding setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
 409  
     {
 410  0
         this.recursionInterceptor = recursionInterceptor;
 411  0
         return this;
 412  
     }
 413  
 }