Coverage Report - org.apache.maven.shared.filtering.InterpolatorFilterReaderLineEnding
 
Classes in this File Line Coverage Branch Coverage Complexity
InterpolatorFilterReaderLineEnding
62%
81/130
41%
37/90
4.929
 
 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 org.codehaus.plexus.interpolation.InterpolationException;
 23  
 import org.codehaus.plexus.interpolation.Interpolator;
 24  
 import org.codehaus.plexus.interpolation.RecursionInterceptor;
 25  
 import org.codehaus.plexus.interpolation.SimpleRecursionInterceptor;
 26  
 
 27  
 import java.io.BufferedReader;
 28  
 import java.io.FilterReader;
 29  
 import java.io.IOException;
 30  
 import java.io.Reader;
 31  
 
 32  
 /**
 33  
  * A FilterReader implementation, that works with Interpolator interface instead of it's own interpolation
 34  
  * implementation. This implementation is heavily based on org.codehaus.plexus.util.InterpolationFilterReader.
 35  
  *
 36  
  * @author cstamas
 37  
  * @author Olivier Lamy
 38  
  *
 39  
  * @since 1.0
 40  
  */
 41  
 public class InterpolatorFilterReaderLineEnding
 42  
     extends FilterReader
 43  
 {
 44  
 
 45  
     /**
 46  
      * Interpolator used to interpolate
 47  
      */
 48  
     private Interpolator interpolator;
 49  
 
 50  
     private RecursionInterceptor recursionInterceptor;
 51  
 
 52  
     /**
 53  
      * replacement text from a token
 54  
      */
 55  7
     private String replaceData = null;
 56  
 
 57  
     /**
 58  
      * Index into replacement data
 59  
      */
 60  7
     private int replaceIndex = 0;
 61  
 
 62  
     /**
 63  
      * Default begin token.
 64  
      */
 65  
     public static final String DEFAULT_BEGIN_TOKEN = "${";
 66  
 
 67  
     /**
 68  
      * Default end token.
 69  
      */
 70  
     public static final String DEFAULT_END_TOKEN = "}";
 71  
 
 72  
     private String beginToken;
 73  
 
 74  
     private String endToken;
 75  
 
 76  
     /**
 77  
      * true by default to preserve backward comp
 78  
      */
 79  7
     private boolean interpolateWithPrefixPattern = true;
 80  
 
 81  
     private String escapeString;
 82  
 
 83  7
     private boolean useEscape = false;
 84  
 
 85  
     /**
 86  
      * if true escapeString will be preserved \{foo} -> \{foo}
 87  
      */
 88  7
     private boolean preserveEscapeString = false;
 89  
 
 90  
     private boolean supportMultiLineFiltering;
 91  
 
 92  
     /**
 93  
      * must always be bigger than escape string plus delimiters, but doesn't need to be exact
 94  
      */
 95  7
     private int markLength = 16;
 96  
 
 97  7
     private boolean eof = false;
 98  
 
 99  
     /**
 100  
      * @param in                        reader to use
 101  
      * @param interpolator              interpolator instance to use
 102  
      * @param beginToken                start token to use
 103  
      * @param endToken                  end token to use
 104  
      * @param supportMultiLineFiltering If multi line filtering is allowed
 105  
      */
 106  
     public InterpolatorFilterReaderLineEnding( Reader in, Interpolator interpolator, String beginToken, String endToken,
 107  
                                                boolean supportMultiLineFiltering )
 108  
     {
 109  7
         this( in, interpolator, beginToken, endToken, new SimpleRecursionInterceptor(), supportMultiLineFiltering );
 110  7
     }
 111  
 
 112  
     /**
 113  
      * @param in                        reader to use
 114  
      * @param interpolator              interpolator instance to use
 115  
      * @param beginToken                start token to use
 116  
      * @param endToken                  end token to use
 117  
      * @param ri                        The {@link RecursionInterceptor} to use to prevent recursive expressions.
 118  
      * @param supportMultiLineFiltering If multi line filtering is allowed
 119  
      */
 120  
     private InterpolatorFilterReaderLineEnding( Reader in, Interpolator interpolator, String beginToken,
 121  
                                                 String endToken, RecursionInterceptor ri,
 122  
                                                 boolean supportMultiLineFiltering )
 123  
     {
 124  
         // wrap our own buffer, so we can use mark/reset safely.
 125  7
         super( new BufferedReader( in ) );
 126  
 
 127  7
         this.interpolator = interpolator;
 128  
 
 129  7
         this.beginToken = beginToken;
 130  
 
 131  7
         this.endToken = endToken;
 132  
 
 133  7
         recursionInterceptor = ri;
 134  
 
 135  7
         this.supportMultiLineFiltering = supportMultiLineFiltering;
 136  
 
 137  7
         calculateMarkLength();
 138  
 
 139  7
     }
 140  
 
 141  
     /**
 142  
      * Skips characters. This method will block until some characters are available, an I/O error occurs, or the end of
 143  
      * the stream is reached.
 144  
      *
 145  
      * @param n The number of characters to skip
 146  
      * @return the number of characters actually skipped
 147  
      * @throws IllegalArgumentException If <code>n</code> is negative.
 148  
      * @throws IOException              If an I/O error occurs
 149  
      */
 150  
     public long skip( long n )
 151  
         throws IOException
 152  
     {
 153  0
         if ( n < 0L )
 154  
         {
 155  0
             throw new IllegalArgumentException( "skip value is negative" );
 156  
         }
 157  
 
 158  0
         for ( long i = 0; i < n; i++ )
 159  
         {
 160  0
             if ( read() == -1 )
 161  
             {
 162  0
                 return i;
 163  
             }
 164  
         }
 165  0
         return n;
 166  
     }
 167  
 
 168  
     /**
 169  
      * Reads characters into a portion of an array. This method will block until some input is available, an I/O error
 170  
      * occurs, or the end of the stream is reached.
 171  
      *
 172  
      * @param cbuf Destination buffer to write characters to. Must not be <code>null</code>.
 173  
      * @param off  Offset at which to start storing characters.
 174  
      * @param len  Maximum number of characters to read.
 175  
      * @return the number of characters read, or -1 if the end of the stream has been reached
 176  
      * @throws IOException If an I/O error occurs
 177  
      */
 178  
     public int read( char cbuf[], int off, int len )
 179  
         throws IOException
 180  
     {
 181  2021
         for ( int i = 0; i < len; i++ )
 182  
         {
 183  2021
             int ch = read();
 184  2021
             if ( ch == -1 )
 185  
             {
 186  15
                 if ( i == 0 )
 187  
                 {
 188  11
                     return -1;
 189  
                 }
 190  
                 else
 191  
                 {
 192  4
                     return i;
 193  
                 }
 194  
             }
 195  2006
             cbuf[off + i] = (char) ch;
 196  
         }
 197  0
         return len;
 198  
     }
 199  
 
 200  
     /**
 201  
      * Returns the next character in the filtered stream, replacing tokens from the original stream.
 202  
      *
 203  
      * @return the next character in the resulting stream, or -1 if the end of the resulting stream has been reached
 204  
      * @throws IOException if the underlying stream throws an IOException during reading
 205  
      */
 206  
     public int read()
 207  
         throws IOException
 208  
     {
 209  2025
         if ( replaceIndex > 0 )
 210  
         {
 211  34
             return replaceData.charAt( replaceData.length() - ( replaceIndex-- ) );
 212  
         }
 213  1991
         if ( eof )
 214  
         {
 215  0
             return -1;
 216  
         }
 217  
 
 218  1991
         in.mark( markLength );
 219  
 
 220  1991
         int ch = in.read();
 221  1991
         if ( ( ch == -1 ) || ( ch == '\n' && !supportMultiLineFiltering ) )
 222  
         {
 223  67
             return ch;
 224  
         }
 225  
 
 226  1924
         boolean inEscape = ( useEscape && ch == escapeString.charAt( 0 ) );
 227  
 
 228  1924
         StringBuffer key = new StringBuffer();
 229  
 
 230  
         // have we found an escape string?
 231  1924
         if ( inEscape )
 232  
         {
 233  0
             for ( int i = 0; i < escapeString.length(); i++ )
 234  
             {
 235  0
                 key.append( (char) ch );
 236  
 
 237  0
                 if ( ch != escapeString.charAt( i ) || ch == -1 || ( ch == '\n' && !supportMultiLineFiltering ) )
 238  
                 {
 239  
                     // mismatch, EOF or EOL, no escape string here
 240  0
                     in.reset();
 241  0
                     inEscape = false;
 242  0
                     key.setLength( 0 );
 243  0
                     break;
 244  
                 }
 245  
 
 246  0
                 ch = in.read();
 247  
 
 248  
             }
 249  
 
 250  
         }
 251  
 
 252  
         // have we found a delimiter?
 253  1924
         boolean foundToken = false;
 254  1928
         for ( int i = 0; i < beginToken.length(); i++ )
 255  
         {
 256  1924
             if ( ch != beginToken.charAt( i ) || ch == -1 || ( ch == '\n' && !supportMultiLineFiltering ) )
 257  
             {
 258  
                 // mismatch, EOF or EOL, no match
 259  0
                 break;
 260  
             }
 261  
 
 262  4
             if ( i == beginToken.length() - 1 )
 263  
             {
 264  
 
 265  4
                 foundToken = true;
 266  
 
 267  
             }
 268  
 
 269  4
             ch = in.read();
 270  
 
 271  
         }
 272  
 
 273  1924
         in.reset();
 274  1924
         in.skip( key.length() );
 275  1924
         ch = in.read();
 276  
 
 277  
         // escape means no luck, prevent parsing of the escaped character, and return
 278  1924
         if ( inEscape )
 279  
         {
 280  
 
 281  0
             if ( beginToken != null )
 282  
             {
 283  0
                 if ( !preserveEscapeString )
 284  
                 {
 285  0
                     key.setLength( 0 );
 286  
                 }
 287  
             }
 288  
 
 289  0
             key.append( (char) ch );
 290  
 
 291  0
             replaceData = key.toString();
 292  0
             replaceIndex = key.length();
 293  
 
 294  0
             return read();
 295  
 
 296  
         }
 297  
 
 298  
         // no match means no luck, reset and return
 299  1924
         if ( !foundToken )
 300  
         {
 301  
 
 302  1920
             in.reset();
 303  1920
             return in.read();
 304  
 
 305  
         }
 306  
 
 307  
         // we're committed, find the end token, EOL or EOF
 308  
 
 309  4
         key.append( beginToken );
 310  4
         in.reset();
 311  4
         in.skip( beginToken.length() );
 312  4
         ch = in.read();
 313  
 
 314  4
         int end = endToken.length();
 315  
         do
 316  
         {
 317  28
             if ( ch == -1 )
 318  
             {
 319  0
                 break;
 320  
             }
 321  28
             else if ( ch == '\n' && !supportMultiLineFiltering )
 322  
             {
 323  
                 // EOL
 324  0
                 key.append( (char) ch );
 325  0
                 break;
 326  
             }
 327  
 
 328  28
             key.append( (char) ch );
 329  
 
 330  28
             if ( ch == this.endToken.charAt( end - 1 ) )
 331  
             {
 332  4
                 end--;
 333  4
                 if ( end == 0 )
 334  
                 {
 335  4
                     break;
 336  
                 }
 337  
             }
 338  
             else
 339  
             {
 340  24
                 end = endToken.length();
 341  
             }
 342  
 
 343  24
             ch = in.read();
 344  
         }
 345  24
         while ( true );
 346  
 
 347  
         // found endtoken? interpolate our key resolved above
 348  4
         String value = null;
 349  4
         if ( end == 0 )
 350  
         {
 351  
             try
 352  
             {
 353  4
                 if ( interpolateWithPrefixPattern )
 354  
                 {
 355  0
                     value = interpolator.interpolate( key.toString(), "", recursionInterceptor );
 356  
                 }
 357  
                 else
 358  
                 {
 359  4
                     value = interpolator.interpolate( key.toString(), recursionInterceptor );
 360  
                 }
 361  
             }
 362  0
             catch ( InterpolationException e )
 363  
             {
 364  0
                 IllegalArgumentException error = new IllegalArgumentException( e.getMessage() );
 365  0
                 error.initCause( e );
 366  
 
 367  0
                 throw error;
 368  4
             }
 369  
         }
 370  
 
 371  
         // write away the value if present, otherwise the key unmodified
 372  4
         if ( value != null )
 373  
         {
 374  4
             replaceData = value;
 375  4
             replaceIndex = value.length();
 376  
         }
 377  
         else
 378  
         {
 379  0
             replaceData = key.toString();
 380  0
             replaceIndex = key.length();
 381  
         }
 382  
 
 383  4
         if ( ch == -1 )
 384  
         {
 385  0
             eof = true;
 386  
         }
 387  4
         return read();
 388  
 
 389  
     }
 390  
 
 391  
     public boolean isInterpolateWithPrefixPattern()
 392  
     {
 393  0
         return interpolateWithPrefixPattern;
 394  
     }
 395  
 
 396  
     public void setInterpolateWithPrefixPattern( boolean interpolateWithPrefixPattern )
 397  
     {
 398  7
         this.interpolateWithPrefixPattern = interpolateWithPrefixPattern;
 399  7
     }
 400  
 
 401  
     public String getEscapeString()
 402  
     {
 403  0
         return escapeString;
 404  
     }
 405  
 
 406  
     public void setEscapeString( String escapeString )
 407  
     {
 408  
         // TODO NPE if escapeString is null ?
 409  0
         if ( escapeString != null && escapeString.length() >= 1 )
 410  
         {
 411  0
             this.escapeString = escapeString;
 412  0
             this.useEscape = escapeString != null && escapeString.length() >= 1;
 413  0
             calculateMarkLength();
 414  
         }
 415  0
     }
 416  
 
 417  
     public boolean isPreserveEscapeString()
 418  
     {
 419  0
         return preserveEscapeString;
 420  
     }
 421  
 
 422  
     public void setPreserveEscapeString( boolean preserveEscapeString )
 423  
     {
 424  0
         this.preserveEscapeString = preserveEscapeString;
 425  0
     }
 426  
 
 427  
     public RecursionInterceptor getRecursionInterceptor()
 428  
     {
 429  0
         return recursionInterceptor;
 430  
     }
 431  
 
 432  
     public InterpolatorFilterReaderLineEnding setRecursionInterceptor( RecursionInterceptor recursionInterceptor )
 433  
     {
 434  0
         this.recursionInterceptor = recursionInterceptor;
 435  0
         return this;
 436  
     }
 437  
 
 438  
     private void calculateMarkLength()
 439  
     {
 440  7
         markLength = 16;
 441  
 
 442  7
         if ( escapeString != null )
 443  
         {
 444  0
             markLength += escapeString.length();
 445  
         }
 446  
 
 447  7
         if ( beginToken != null )
 448  
         {
 449  7
             markLength += beginToken.length();
 450  
         }
 451  
 
 452  7
         if ( endToken != null )
 453  
         {
 454  7
             markLength += endToken.length();
 455  
         }
 456  
 
 457  7
     }
 458  
 
 459  
 }