Coverage Report - org.apache.maven.shared.filtering.PropertyUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyUtils
95%
45/47
92%
24/26
5
 
 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.util.IOUtil;
 23  
 import org.codehaus.plexus.util.StringUtils;
 24  
 
 25  
 import java.io.File;
 26  
 import java.io.FileInputStream;
 27  
 import java.io.FileNotFoundException;
 28  
 import java.io.IOException;
 29  
 import java.util.Iterator;
 30  
 import java.util.Properties;
 31  
 
 32  
 
 33  
 /**
 34  
  * @author <a href="mailto:kenney@neonics.com">Kenney Westerhof</a>
 35  
  * @author William Ferguson
 36  
  *
 37  
  */
 38  
 public final class PropertyUtils
 39  
 {
 40  
     /**
 41  
      * Private empty constructor to prevent instantiation.
 42  
      */
 43  
     private PropertyUtils()
 44  0
     {
 45  
         // prevent instantiation
 46  0
     }
 47  
 
 48  
     /**
 49  
      * Reads a property file, resolving all internal variables, using the supplied base properties.
 50  
      * <p>
 51  
      * The properties are resolved iteratively, so if the value of property A refers to property B, 
 52  
      * then after resolution the value of property B will contain the value of property B.
 53  
      * </p>
 54  
      * 
 55  
      * @param propFile The property file to load.
 56  
      * @param baseProps Properties containing the initial values to substitute into the properties file.
 57  
      * @return Properties object containing the properties in the file with their values fully resolved.
 58  
      * @throws IOException if profile does not exist, or cannot be read.
 59  
      */
 60  
     public static Properties loadPropertyFile( File propFile, Properties baseProps )
 61  
         throws IOException
 62  
     {
 63  76
         if ( !propFile.exists() )
 64  
         {
 65  2
             throw new FileNotFoundException( propFile.toString() );
 66  
         }
 67  
 
 68  74
         final Properties fileProps = new Properties();
 69  74
         final FileInputStream inStream = new FileInputStream( propFile );
 70  
         try
 71  
         {
 72  74
             fileProps.load( inStream );
 73  
         }
 74  
         finally
 75  
         {
 76  74
             IOUtil.close( inStream );
 77  74
         }
 78  
 
 79  74
         final Properties combinedProps = new Properties();
 80  74
         combinedProps.putAll( baseProps == null ? new Properties() : baseProps );
 81  74
         combinedProps.putAll( fileProps );
 82  
 
 83  
         // The algorithm iterates only over the fileProps which is all that is required to resolve
 84  
         // the properties defined within the file. This is slightly different to current, however
 85  
         // I suspect that this was the actual original intent.
 86  
         // 
 87  
         // The difference is that #loadPropertyFile(File, boolean, boolean) also resolves System properties
 88  
         // whose values contain expressions. I believe this is unexpected and is not validated by the test cases,
 89  
         // as can be verified by replacing the implementation of #loadPropertyFile(File, boolean, boolean)
 90  
         // with the commented variant I have provided that reuses this method.
 91  
 
 92  74
         for ( Iterator iter = fileProps.keySet().iterator(); iter.hasNext(); )
 93  
         {
 94  302
             final String k = (String) iter.next();
 95  302
             final String propValue = getPropertyValue( k, combinedProps );
 96  302
             fileProps.setProperty( k, propValue );
 97  302
         }
 98  
 
 99  74
         return fileProps;
 100  
     }
 101  
 
 102  
     /**
 103  
      * Reads a property file, resolving all internal variables.
 104  
      *
 105  
      * @param propfile The property file to load
 106  
      * @param fail whether to throw an exception when the file cannot be loaded or to return null
 107  
      * @param useSystemProps whether to incorporate System.getProperties settings into the returned Properties object.
 108  
      * @return the loaded and fully resolved Properties object
 109  
      * @throws IOException if profile does not exist, or cannot be read.
 110  
      */
 111  
     public static Properties loadPropertyFile( File propfile, boolean fail, boolean useSystemProps )
 112  
         throws IOException
 113  
     {
 114  
         
 115  6
         final Properties baseProps = new Properties();
 116  
 
 117  6
         if ( useSystemProps )
 118  
         {
 119  2
             baseProps.putAll( System.getProperties() );
 120  
         }
 121  
 
 122  6
         final Properties resolvedProps = new Properties();
 123  
         try
 124  
         {
 125  6
             resolvedProps.putAll( loadPropertyFile( propfile, baseProps ) );
 126  
         }
 127  2
         catch ( FileNotFoundException e )
 128  
         {
 129  2
             if ( fail )
 130  
             {
 131  2
                 throw new FileNotFoundException( propfile.toString() );
 132  
             }
 133  4
         }
 134  
 
 135  4
         if ( useSystemProps )
 136  
         {
 137  2
             resolvedProps.putAll( baseProps );
 138  
         }
 139  
 
 140  4
         return resolvedProps;
 141  
     }
 142  
 
 143  
 
 144  
     /**
 145  
      * Retrieves a property value, replacing values like ${token}
 146  
      * using the Properties to look them up.
 147  
      *
 148  
      * It will leave unresolved properties alone, trying for System
 149  
      * properties, and implements reparsing (in the case that
 150  
      * the value of a property contains a key), and will
 151  
      * not loop endlessly on a pair like
 152  
      * test = ${test}.
 153  
      *
 154  
      * @param k
 155  
      * @param p
 156  
      * @return The filtered property value.
 157  
      */
 158  
     private static String getPropertyValue( String k, Properties p )
 159  
     {
 160  
         // This can also be done using InterpolationFilterReader,
 161  
         // but it requires reparsing the file over and over until
 162  
         // it doesn't change.
 163  
 
 164  302
         String v = p.getProperty( k );
 165  302
         String ret = "";
 166  
         int idx, idx2;
 167  
 
 168  424
         while ( ( idx = v.indexOf( "${" ) ) >= 0 )
 169  
         {
 170  
             // append prefix to result
 171  134
             ret += v.substring( 0, idx );
 172  
 
 173  
             // strip prefix from original
 174  134
             v = v.substring( idx + 2 );
 175  
 
 176  
             // if no matching } then bail
 177  134
             if ( ( idx2 = v.indexOf( '}' ) ) < 0 )
 178  
             {
 179  12
                 break;
 180  
             }
 181  
 
 182  
             // strip out the key and resolve it
 183  
             // resolve the key/value for the ${statement}
 184  122
             String nk = v.substring( 0, idx2 );
 185  122
             v = v.substring( idx2 + 1 );
 186  122
             String nv = p.getProperty( nk );
 187  
 
 188  
             // try global environment..
 189  122
             if ( nv == null && !StringUtils.isEmpty( nk ) )
 190  
             {
 191  62
                 nv = System.getProperty( nk );
 192  
             }
 193  
 
 194  
             // if the key cannot be resolved,
 195  
             // leave it alone ( and don't parse again )
 196  
             // else prefix the original string with the
 197  
             // resolved property ( so it can be parsed further )
 198  
             // taking recursion into account.
 199  122
             if ( nv == null || nv.equals( k ) || k.equals( nk ) )
 200  
             {
 201  102
                 ret += "${" + nk + "}";
 202  
             }
 203  
             else
 204  
             {
 205  20
                 v = nv + v;
 206  
             }
 207  122
         }
 208  302
         return ret + v;
 209  
     }
 210  
 }