Author: akarasulu Date: Sun Nov 7 15:15:56 2004 New Revision: 56875 Added: incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/util/PropertiesUtils.java Modified: incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParser.java incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParserImpl.java Log: Changes ...
o added new utility file for managing properties o partially cleaned up LDIF parser wrt formating and exceptions thrown Modified: incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParser.java ============================================================================== --- incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParser.java (original) +++ incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParser.java Sun Nov 7 15:15:56 2004 @@ -16,14 +16,9 @@ package org.apache.ldap.common.ldif ; -import java.text.ParseException ; - import javax.naming.NamingException ; import javax.naming.directory.Attributes ; -import org.apache.ldap.common.util.MultiMap; - - /** * Parses an ldif into a multimap or an JNDI Attributes instance of attribute @@ -35,55 +30,32 @@ * be accessed and removed from the MultiMap or Attributes instance if need be * according to the specific context in which this parser is used. * - * @author <a href="mailto:[EMAIL PROTECTED]">Alex Karasulu</a> - * @author $Author: akarasulu $ - * @version $Revision: 1.7 $ + * @author <a href="mailto:[EMAIL PROTECTED]">Apache Directory Project</a> + * @version $Rev$ */ public interface LdifParser { /** * Parses an String representing an entry in LDAP Data Interchange Format - * (LDIF) storing its name and attributes in the supplied MultiMap instance. - * - * @param a_mmap the MultiMap instance to populate with LDIF attributes - * including the DN of the entry represented by the LDIF. - * @param an_ldif the entry in LDAP Data Interchange Format - * @deprecated - * @throws ParseException if the LDIF is malformed - * @throws NamingException if a naming exception results while the LDIF is - * being parsed - * TODO investigate if we need to throw a NamingException here - */ - void parse( MultiMap a_mmap, String an_ldif ) - throws ParseException, NamingException ; - - /** - * Parses an String representing an entry in LDAP Data Interchange Format * (LDIF) storing its attributes in the supplied Attributes instance. * - * @param an_attributes the Attributes instance to populate with LDIF + * @param attributes the Attributes instance to populate with LDIF * attributes including the DN of the entry represented by the LDIF. - * @param an_ldif the entry in LDAP Data Interchange Format - * @throws ParseException if the LDIF is malformed + * @param ldif the entry in LDAP Data Interchange Format * @throws NamingException if a naming exception results while the LDIF is * being parsed - * TODO investigate if we need to throw a NamingException here */ - void parse( Attributes an_attributes, String an_ldif ) - throws ParseException, NamingException ; + void parse( Attributes attributes, String ldif ) throws NamingException ; /** * Parses an LDIF into a special LdifEntry structure that tracks control * attributes within an LDIF. * - * @param an_ldif the LDIF to parse + * @param ldif the LDIF to parse * @return the LdifEntry parsed - * @throws ParseException if the LDIF is malformed * @throws NamingException if a naming exception results while the LDIF is * being parsed - * TODO investigate if we need to throw a NamingException here */ - LdifEntry parse( String an_ldif ) - throws ParseException, NamingException ; + LdifEntry parse( String ldif ) throws NamingException ; } Modified: incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParserImpl.java ============================================================================== --- incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParserImpl.java (original) +++ incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/ldif/LdifParserImpl.java Sun Nov 7 15:15:56 2004 @@ -13,21 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.ldap.common.ldif ; - +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ldap.common.ldif; -import java.text.ParseException ; -import java.io.IOException ; -import java.io.StringReader ; -import java.io.BufferedReader ; +import java.io.IOException; +import java.io.StringReader; +import java.io.BufferedReader; -import javax.naming.NamingException ; -import javax.naming.directory.Attributes ; -import javax.naming.directory.DirContext ; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; -import org.apache.ldap.common.util.Base64 ; -import org.apache.ldap.common.util.MultiMap; +import org.apache.ldap.common.util.Base64; +import org.apache.ldap.common.message.ResultCodeEnum; +import org.apache.eve.exception.EveNamingException; /** @@ -38,108 +52,22 @@ * Attributes instance. Until they are the populated container cannot be deemed * representative of an entry. * - * @task Get the RFC for LDIF syntax in this javadoc. + * @todo Get the RFC for LDIF syntax in this javadoc. * @see <a href="http://www.faqs.org/rfcs/rfc2849.html"> RFC 2849 </a> - * @author <a href="mailto:[EMAIL PROTECTED]">Alex Karasulu</a> - * @author $Author: jmachols $ - * @version $Revision: 1.12 $ + * @author <a href="mailto:[EMAIL PROTECTED]">Apache Directory Project</a> + * @version $Rev$ */ -public class LdifParserImpl - implements LdifParser +public class LdifParserImpl implements LdifParser { /** - * Parses an LDIF String populating a multimap with both single valued and - * multivalued attributes. - * - * @param a_attrHash the hash of attributes to values. - * @param an_ldif the LDIF as a String. - * @deprecated - * @throws ParseException if an_ldif violates LDIF syntax. - */ - public void parse( MultiMap a_attrHash, String an_ldif ) - throws ParseException - { - boolean l_isBase64Encoded = false ; - int l_lineCount = 0 ; - int l_index ; - String l_line ; - String l_attrName ; - String l_attrValue ; - StringReader l_strIn = new StringReader( an_ldif ) ; - BufferedReader l_in = new BufferedReader( l_strIn ) ; - - try - { - while ( ( l_line = l_in.readLine() ) != null ) - { - // Try to advance to ':' if one exists. - if ( ( l_index = l_line.indexOf( ':' ) ) == -1 ) - { - throw new ParseException( "Line " + l_lineCount + " [" - + l_line + "] does not correspond to an LDIF entry " - + "attribute value pair.\n{" + an_ldif + "}", - l_lineCount ) ; - } - - // Capture data while at first colon. - l_attrName = l_line.substring( 0, l_index ).trim() ; - - // Consume next char and check if it's a colon for binary attr. - if ( l_line.charAt( ++l_index ) == ':' ) - { - l_isBase64Encoded = true ; - } - - // Advance index past whitespace to the first char of the value. - try - { - while ( l_line.charAt( ++l_index ) == ' ' ) - { - ; // Nothing - } - - // Capture attribute value from first char till end of line. - l_attrValue = l_line.substring( l_index ) ; - } - catch ( StringIndexOutOfBoundsException e ) - { - l_attrValue = "" ; - } - - /* - * We need to construct an attribute yet we may not know if it - * is single valued or multi valued. Our best bet is to just - * cover all possibilities using a basic attribute instance that - * the basic attrubutes instance creates automatically. - */ - if ( l_isBase64Encoded && ( l_attrValue != null ) ) - { - a_attrHash.put( l_attrName, base64decode( l_attrValue ) ) ; - l_isBase64Encoded = false ; - } - else - { - a_attrHash.put( l_attrName, l_attrValue ) ; - } - } - } - catch ( IOException e ) - { - // Does not really occur: we follow form by transforming w/ rethrow - throw new ParseException( e.getMessage(), l_lineCount ) ; - } - } - - - /** * Decodes an encoded string in base64 into a byte array. * - * @param a_attrValue the value of a encoded binary attribute. + * @param attrValue the value of a encoded binary attribute. * @return the decoded binary data as a byte array. */ - public byte [] base64decode( String a_attrValue ) + public byte [] base64decode( String attrValue ) { - return ( Base64.decode( a_attrValue.toCharArray() ) ) ; + return ( Base64.decode( attrValue.toCharArray() ) ); } @@ -147,60 +75,58 @@ * Parses an String representing an entry in LDAP Data Interchange Format * (LDIF) storing its attributes in the supplied Attributes instance. * - * @param an_attributes the attributes from the LDIF + * @param attributes the attributes from the LDIF * including the DN of the entry represented by the LDIF. - * @param an_ldif the entry in LDAP Data Interchange Format - * @throws ParseException if an_ldif violates LDIF syntax. - * @throws NamingException TODO doc me + * @param ldif the entry in LDAP Data Interchange Format + * @throws NamingException if there any failures while parsing the LDIF and + * populating the attirubutes */ - public void parse( Attributes an_attributes, String an_ldif ) - throws ParseException, NamingException + public void parse( Attributes attributes, String ldif ) throws NamingException { - boolean l_isBase64Encoded = false ; - int l_lineCount = 0 ; - int l_index ; - String l_line ; - String l_attrName ; - String l_attrValue ; - StringReader l_strIn = new StringReader( an_ldif ) ; - BufferedReader l_in = new BufferedReader( l_strIn ) ; + boolean isBase64Encoded = false; + int lineCount = 0; + int index; + String line; + String attrName; + String attrValue; + StringReader strIn = new StringReader( ldif ); + BufferedReader in = new BufferedReader( strIn ); try { - while ( ( l_line = l_in.readLine() ) != null ) + while ( ( line = in.readLine() ) != null ) { // Try to advance to ':' if one exists. - if ( ( l_index = l_line.indexOf( ':' ) ) == -1 ) + if ( ( index = line.indexOf( ':' ) ) == -1 ) { - throw new ParseException( "Line " + l_lineCount + " [" - + l_line + "] does not correspond to an LDIF entry " - + "attribute value pair.\n{" + an_ldif + "}", - l_lineCount ) ; + throw new EveNamingException( "Line " + lineCount + " [" + + line + "] does not correspond to an LDIF entry " + + "attribute value pair.\n{" + ldif + "}", + ResultCodeEnum.OTHER ); } // Capture data while at first colon. - l_attrName = l_line.substring( 0, l_index ).trim() ; + attrName = line.substring( 0, index ).trim(); // Consume next char and check if it's a colon for binary attr. - if ( l_line.charAt( ++l_index ) == ':' ) + if ( line.charAt( ++index ) == ':' ) { - l_isBase64Encoded = true ; + isBase64Encoded = true; } // Advance index past whitespace to the first char of the value. try { - while ( l_line.charAt( ++l_index ) == ' ' ) - { - ; // Does nothing! + while ( line.charAt( ++index ) == ' ' ) + {; // Does nothing! } // Capture attribute value from first char till end of line. - l_attrValue = l_line.substring( l_index ) ; + attrValue = line.substring( index ); } catch ( StringIndexOutOfBoundsException e ) { - l_attrValue = "" ; + attrValue = ""; } /* @@ -209,22 +135,21 @@ * cover all possibilities using a basic attribute instance that * the basic attrubutes instance creates automatically. */ - if ( l_isBase64Encoded && ( l_attrValue != null ) ) + if ( isBase64Encoded && ( attrValue != null ) ) { - an_attributes.put( l_attrName, - base64decode( l_attrValue ) ) ; - l_isBase64Encoded = false ; + attributes.put( attrName, base64decode( attrValue ) ); + isBase64Encoded = false; } else { - an_attributes.put( l_attrName, l_attrValue ) ; + attributes.put( attrName, attrValue ); } } } catch ( IOException e ) { // Does not really occur: we follow form by transforming w/ rethrow - throw new ParseException( e.getMessage(), l_lineCount ) ; + throw new EveNamingException( ResultCodeEnum.OTHER ); } } @@ -235,23 +160,23 @@ * * @param an_ldif the entry in LDAP Data Interchange Format * @return the LdifEntry parsed from the LDIF string - * @throws ParseException if an_ldif violates LDIF syntax - * @throws NamingException TODO document me + * @throws NamingException if there any failures while parsing the LDIF and + * populating the attirubutes */ public LdifEntry parse( String an_ldif ) - throws ParseException, NamingException + throws NamingException { - boolean l_isBase64Encoded = false ; - int l_lineCount = 0 ; - int l_index ; - String l_line ; - String l_attrName = new String () ; - String l_attrValue = new String () ; - String l_prevAttrValue = null ; - StringReader l_strIn = new StringReader( an_ldif ) ; - BufferedReader l_in = new BufferedReader( l_strIn ) ; - LdifEntry l_entry = new LdifEntry() ; - int l_currentModOp = -1 ; + boolean l_isBase64Encoded = false; + int l_lineCount = 0; + int l_index; + String l_line; + String l_attrName = new String (); + String l_attrValue = new String (); + String l_prevAttrValue = null; + StringReader l_strIn = new StringReader( an_ldif ); + BufferedReader l_in = new BufferedReader( l_strIn ); + LdifEntry l_entry = new LdifEntry(); + int l_currentModOp = -1; try { @@ -265,20 +190,21 @@ { if ( l_currentModOp == -1 ) { - throw new ParseException( "A modification" + throw new EveNamingException( "A modification" + " type must be supplied for a change " - + "type of modify", l_lineCount ) ; + + "type of modify", + ResultCodeEnum.OTHER ); } l_entry.addModificationItem( l_currentModOp, - l_attrName, l_attrValue ) ; + l_attrName, l_attrValue ); } else { if ( l_isBase64Encoded && ( l_attrValue != null ) ) { l_entry.addAttribute( l_attrName, - base64decode( l_attrValue ) ) ; - l_isBase64Encoded = false ; + base64decode( l_attrValue ) ); + l_isBase64Encoded = false; } else { @@ -286,27 +212,26 @@ } } } - l_currentModOp = -1 ; - l_prevAttrValue = null ; - continue ; + l_currentModOp = -1; + l_prevAttrValue = null; + continue; } // Try to advance to ':' if one exists. if ( ( l_index = l_line.indexOf( ':' ) ) == -1 ) { - throw new ParseException( "Line " + l_lineCount + " [" + throw new EveNamingException( "Line " + l_lineCount + " [" + l_line + "] does not correspond to an LDIF entry " + "attribute value pair.\n{" + an_ldif + "}", - l_lineCount ) ; - + ResultCodeEnum.OTHER ); } // Capture data while at first colon. - l_attrName = l_line.substring( 0, l_index ).trim() ; + l_attrName = l_line.substring( 0, l_index ).trim(); // Consume next char and check if it's a colon for binary attr. if ( l_line.charAt( ++l_index ) == ':' ) { - l_isBase64Encoded = true ; + l_isBase64Encoded = true; } // Advance index past whitespace to the first char of the value. @@ -314,15 +239,15 @@ { while ( l_line.charAt( ++l_index ) == ' ' ) { - ; // Does nothing! + ; // Does nothing! } // Capture attribute value from first char till end of line. - l_attrValue = l_line.substring( l_index ) ; + l_attrValue = l_line.substring( l_index ); } catch ( StringIndexOutOfBoundsException e ) { - l_attrValue = "" ; + l_attrValue = ""; } /* @@ -331,55 +256,58 @@ */ if ( l_attrName.equalsIgnoreCase( "dn" ) ) { - l_entry.setDn( l_attrValue ) ; + l_entry.setDn( l_attrValue ); } else if ( l_attrName.equalsIgnoreCase( "version" ) ) { - l_entry.setVersion( Integer.parseInt( l_attrValue ) ) ; + l_entry.setVersion( Integer.parseInt( l_attrValue ) ); } else if ( l_attrName.equalsIgnoreCase( "control" ) ) { - ; // Not implemented + ; // Not implemented } else if ( l_attrName.equalsIgnoreCase( "changetype" ) ) { - l_entry.setModType( l_attrValue ) ; + l_entry.setModType( l_attrValue ); } else if ( l_attrName.equalsIgnoreCase( "add" ) ) { if ( !l_entry.getModType().equalsIgnoreCase( "modify" ) ) { - throw new ParseException( "Cannot use modification " + throw new EveNamingException( "Cannot use modification " + l_attrName + " identifier on " + l_entry.getModType() - + " change type", l_lineCount ) ; + + " change type", + ResultCodeEnum.OTHER ); } - l_currentModOp = DirContext.ADD_ATTRIBUTE ; + l_currentModOp = DirContext.ADD_ATTRIBUTE ; } else if ( l_attrName.equalsIgnoreCase( "replace" ) ) { if ( !l_entry.getModType().equalsIgnoreCase( "modify" ) ) { - throw new ParseException( "Cannot use modification " + throw new EveNamingException( "Cannot use modification " + l_attrName + " identifier on " + l_entry.getModType() - + " change type", l_lineCount ) ; + + " change type", + ResultCodeEnum.OTHER ); } - l_currentModOp = DirContext.REPLACE_ATTRIBUTE ; + l_currentModOp = DirContext.REPLACE_ATTRIBUTE; } else if ( l_attrName.equalsIgnoreCase( "delete" ) ) { if ( !l_entry.getModType().equalsIgnoreCase( "modify" ) ) { - throw new ParseException( "Cannot use modification " + throw new EveNamingException( "Cannot use modification " + l_attrName + " identifier on " + l_entry.getModType() - + " change type", l_lineCount ) ; + + " change type", + ResultCodeEnum.OTHER ); } - l_currentModOp = DirContext.REMOVE_ATTRIBUTE ; + l_currentModOp = DirContext.REMOVE_ATTRIBUTE; if ( l_attrValue != null ) { - l_prevAttrValue = l_attrValue ; + l_prevAttrValue = l_attrValue; } } else @@ -388,34 +316,34 @@ { if ( l_currentModOp == -1 ) { - throw new ParseException( "A modification type must" + throw new EveNamingException( "A modification type must" + " be supplied for a change type of modify", - l_lineCount ) ; + ResultCodeEnum.OTHER ); } l_entry.addModificationItem( l_currentModOp, l_attrName, - l_attrValue ) ; + l_attrValue ); } else { if ( l_isBase64Encoded && ( l_attrValue != null ) ) { l_entry.addAttribute( l_attrName, - base64decode( l_attrValue ) ) ; - l_isBase64Encoded = false ; + base64decode( l_attrValue ) ); + l_isBase64Encoded = false; } else { - l_entry.addAttribute( l_attrName, l_attrValue ) ; + l_entry.addAttribute( l_attrName, l_attrValue ); } } } } - return l_entry ; + return l_entry; } catch ( IOException e ) { // Does not really occur: we follow form by transforming w/ rethrow - throw new ParseException( e.getMessage(), l_lineCount ) ; + throw new EveNamingException( e.getMessage(), ResultCodeEnum.OTHER ); } } } Added: incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/util/PropertiesUtils.java ============================================================================== --- (empty file) +++ incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common/util/PropertiesUtils.java Sun Nov 7 15:15:56 2004 @@ -0,0 +1,608 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.ldap.common.util; + + +import java.util.*; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.io.FileInputStream; + +import javax.naming.directory.Attributes; +import javax.naming.NamingException; + +import org.apache.ldap.common.NotImplementedException; +import org.apache.ldap.common.ldif.LdifParserImpl; +import org.apache.ldap.common.message.LockableAttributesImpl; + + +/** + * A utility class used for accessing, finding, merging and macro expanding + * properties, on disk, via URLS or as resources. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Apache Directory Project</a> + * @version $Rev$ + */ +public class PropertiesUtils +{ + /** default properties file extension */ + private static final String DOTPROPERTIES = ".properties"; + + + // ------------------------------------------------------------------------ + // Utilities for discovering Properties + // ------------------------------------------------------------------------ + + + /** + * Loads a properties object in a properties file if it exists relative to + * the filename ${user.home}. If the file ${user.home}/[filename] does not + * exist then one last attempt to find the file is made if filename does not + * have a .properties extension. If so and ${user.home}/[filename].properties + * exists then it is loaded. + * + * @param filename the properties file name with or without an extension + * @return the user properties object + */ + public static Properties findUserProperties( String filename ) + { + return findProperties( new File( System.getProperty( "user.home" ) ), filename ) ; + } + + + /** + * Create a new properties object and load the properties file if it exists + * relative to [dir]/[filename] or [dir]/[filename].properties. + * + * @param dir the base directory + * @param filename the full fine name or the base name w/o the extension + * @return the loaded properties object + */ + public static Properties findProperties( File dir, String filename ) + { + final File asis = new File( dir, filename ); + + if ( asis.exists() ) + { + return getProperties( asis ); + } + + if ( filename.endsWith( DOTPROPERTIES ) ) + { + String noExt = filename.substring( 0, filename.length() - 11 ); + if ( new File( dir, noExt).exists() ) + { + return getProperties( new File( dir, noExt) ); + } + + return new Properties(); + } + + File withExt = new File( dir, filename + DOTPROPERTIES ); + if ( withExt.exists() ) + { + return getProperties( withExt ); + } + + return new Properties(); + } + + + /** + * Load a properties from a resource relative to a supplied class. First + * an attempt is made to locate a property file colocated with the class + * with the name [class].properties. If this cannot be found or errors + * result an empty Properties file is returned. + * + * @param ref a class to use for relative path references + * @return the static properties + */ + public static Properties getStaticProperties( Class ref ) + { + final Properties properties = new Properties(); + final String address = ref.toString().replace( '.','/' ); + final String path = address + ".properties"; + InputStream input = ref.getResourceAsStream( path ); + + if( null != input ) + { + try + { + properties.load( input ); + } + catch ( IOException e ) + { + return properties; + } + } + + return properties; + } + + + /** + * Load properties from a resource relative to a supplied class and path. + * + * @param ref a class to use for relative path references + * @param path the relative path to the resoruce + * @return the static properties + */ + public static Properties getStaticProperties( Class ref, String path ) + { + Properties properties = new Properties(); + InputStream input = ref.getResourceAsStream( path ); + + if( input == null ) + { + return properties; + } + + try + { + properties.load( input ); + } + catch ( IOException e ) + { + return properties; + } + + return properties; + } + + + /** + * Creates a properties object and loads the properties in the file otherwise + * and empty property object will be returned. + * + * @param file the properties file + * @return the properties object + */ + public static Properties getProperties( File file ) + { + Properties properties = new Properties(); + + if( null == file ) + { + return properties; + } + + if( file.exists() ) + { + try + { + properties.load( new FileInputStream( file ) ); + } + catch ( IOException e ) + { + return properties; + } + } + + return properties; + } + + + /** + * Loads a properties file as a CL resource if it exists and returns an + * empty Properties object otherwise. + * + * @param classloader the loader to use for the resources + * @param path the path to the resource + * @return the loaded or new Properties + */ + public static Properties getProperties( ClassLoader classloader, String path ) + { + Properties properties = new Properties(); + InputStream input = classloader.getResourceAsStream( path ); + + if( input != null ) + { + try + { + properties.load( input ); + } + catch ( IOException e ) + { + return properties; + } + } + + return properties; + } + + + /** + * Loads a properties file as a class resource if it exists and returns an + * empty Properties object otherwise. + * + * @param clazz the class to use for resolving the resources + * @param path the relative path to the resource + * @return the loaded or new Properties + */ + public static Properties getProperties( Class clazz, String path ) + { + Properties properties = new Properties(); + InputStream input = clazz.getResourceAsStream( path ); + + if( input != null ) + { + try + { + properties.load( input ); + } + catch ( IOException e ) + { + return properties; + } + } + + return properties; + } + + + // ------------------------------------------------------------------------ + // Utilities for operating on or setting Properties values + // ------------------------------------------------------------------------ + + + /** + * Expands out a set of property key macros in the following format + * ${foo.bar} where foo.bar is a property key, by dereferencing the value + * of the key using the original source Properties and other optional + * Properties. + * + * If the original expanded Properties contain the value for the macro key, + * foo.bar, then dereferencing stops by using the value in the expanded + * Properties: the other optional Properties are NOT used at all. + * + * If the original expanded Properties do NOT contain the value for the + * macro key, then the optional Properties are used in order. The first of + * the optionals to contain the value for the macro key (foo.bar) shorts the + * search. Hence the first optional Properties in the array to contain a + * value for the macro key (foo.bar) is used to set the expanded value. + * + * If a macro cannot be expanded because it's key was not defined within the + * expanded Properties or one of the optional Properties then it is left as + * is. + * + * @param expanded the Properties to perform the macro expansion upon + * @param optionals null or an optional set of Properties to use for + * dereferencing macro keys (foo.bar) + */ + public static void macroExpand( Properties expanded, Properties [] optionals ) + { + // Handle null optionals + if ( null == optionals ) + { + optionals = new Properties [ 0 ]; + } + + Enumeration list = expanded.propertyNames(); + while ( list.hasMoreElements() ) + { + String key = ( String ) list.nextElement(); + String macro = expanded.getProperty( key ); + + int n = macro.indexOf( "${" ); + if( n < 0 ) + { + continue; + } + + int m = macro.indexOf( "}", n+2 ); + if( m < 0 ) + { + continue; + } + + final String symbol = macro.substring( n+2, m ); + + if ( expanded.containsKey( symbol ) ) + { + final String value = expanded.getProperty( symbol ); + final String head = macro.substring( 0, n ); + final String tail = macro.substring( m+1 ); + final String resolved = head + value + tail; + expanded.put( key, resolved ); + continue; + } + + /* + * Check if the macro key exists within the array of optional + * Properties. Set expanded value to first Properties with the + * key and break out of the loop. + */ + for ( int ii = 0; ii < optionals.length; ii++ ) + { + if ( optionals[ii].containsKey( symbol ) ) + { + final String value = optionals[ii].getProperty( symbol ); + final String head = macro.substring( 0, n ); + final String tail = macro.substring( m+1 ); + final String resolved = head + value + tail; + expanded.put( key, resolved ); + break; + } + } + } + } + + + /** + * Discovers a value within a set of Properties either halting on the first + * time the property is discovered or continuing on to take the last value + * found for the property key. + * + * @param key a property key + * @param sources a set of source Properties + * @param haltOnDiscovery true if we stop on finding a value, false + * otherwise + * @return the value found or null + */ + public static String discover( String key, Properties[] sources, boolean haltOnDiscovery ) + { + String retval = null; + + for( int ii = 0; ii < sources.length; ii++ ) + { + if ( sources[ii].containsKey( key ) ) + { + retval = sources[ii].getProperty( key ); + + if ( haltOnDiscovery ) + { + break; + } + } + } + + return retval; + } + + + /** + * Merges a set of properties from source Properties into a target + * properties instance containing keys. This method does not allow null + * overrides. + * + * @param keys the keys to discover values for + * @param sources the sources to search + * @param haltOnDiscovery true to halt on first find or false to continue + * to last find + */ + public static void discover( Properties keys, Properties[] sources, boolean haltOnDiscovery ) + { + if ( null == sources || null == keys ) { return; } + + /* + * H A N D L E S I N G L E V A L U E D K E Y S + */ + Iterator list = keys.keySet().iterator(); + while ( list.hasNext() ) + { + String key = ( String ) list.next(); + String value = discover( key, sources, haltOnDiscovery ); + + if ( value != null ) + { + keys.setProperty( key, value ); + } + } + } + + + // ------------------------------------------------------------------------ + // Various Property Accessors + // ------------------------------------------------------------------------ + + /** + * Gets a String property as a boolean returning a defualt if the key is + * not present. In any case, true, on, 1 and yes strings return true and + * everything else returns + * + * @param props the properties to get the value from + * @param key the property key + * @param defaultValue the default value to return if key is not present + * @return true defaultValue if property does not exist, else return true + * if the String value is one of 'true', 'on', '1', 'yes', otherwise false + * is returned + */ + public static boolean get( Properties props, String key, boolean defaultValue ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return defaultValue; + } + + String val = props.getProperty( key ).trim().toLowerCase(); + return val.equals( "true" ) || val.equals( "on" ) || val.equals( "1" ) || val.equals( "yes" ); + } + + + public static int get( Properties props, String key, int defaultValue ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return defaultValue; + } + + throw new NotImplementedException(); + } + + + public static long get( Properties props, String key, long defaultValue ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return defaultValue; + } + + throw new NotImplementedException(); + } + + + public static byte get( Properties props, String key, byte defaultValue ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return defaultValue; + } + + throw new NotImplementedException(); + } + + + public static char get( Properties props, String key, char defaultValue ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return defaultValue; + } + + throw new NotImplementedException(); + } + + + /** + * Fills a set with the space delimited values of a property. If values is + * null a new Set is created and returned. + * + * @param props the properties to get the property values from + * @param key the key of the multivalued property + * @param values the values to populate + * @return the values set so it can be filled then used + */ + public static Set fill( Properties props, String key, Set values ) + { + if ( values == null ) { values = new HashSet(); } + return ( Set ) fillCollection( props, key, values, " " ); + } + + + /** + * Fills a set with the delimited values of a property. If values is + * null a new Set is created and returned. + * + * @param props the properties to get the property values from + * @param key the key of the multivalued property + * @param values the values to populate + * @param delimiter the delimiter string to split the property with + * @return the values set so it can be filled then used + */ + public static Set fill( Properties props, String key, Set values, String delimiter ) + { + if ( values == null ) { values = new HashSet(); } + return ( Set ) fillCollection( props, key, values, delimiter ); + } + + + /** + * Fills a list with the space delimited values of a property. The list + * maintains the order of values in the multivalued property. If values is + * null a new List is created and returned. + * + * @param props the properties to get the property values from + * @param key the key of the multivalued property + * @param values the values to populate + * @return the values list so it can be filled then used + */ + public static List fill( Properties props, String key, List values ) + { + if ( values == null ) { values = new ArrayList(); } + return ( List ) fillCollection( props, key, values, " " ); + } + + + /** + * Fills a list with the space delimited values of a property. The list + * maintains the order of values in the multivalued property. If values is + * null a new List is created and returned. + * + * @param props the properties to get the property values from + * @param key the key of the multivalued property + * @param values the values to populate + * @param delimiter the delimiter string to split the property with + * @return the values list so it can be filled then used + */ + public static List fill( Properties props, String key, List values, String delimiter ) + { + if ( values == null ) { values = new ArrayList(); } + return ( List ) fillCollection( props, key, values, delimiter ); + } + + + /** + * Fills a collection with the delimited values of a property. + * + * @param props the properties to get the property values from + * @param key the key of the multivalued property + * @param values the values to populate + * @param delimiter the delimiter string to split the property with + * @return the values collection so it can be filled then used + */ + public static Collection fillCollection( Properties props, String key, Collection values, String delimiter ) + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + return values; + } + + String[] items = props.getProperty( key ).trim().split( delimiter ); + for ( int ii = 0; ii < items.length; ii++ ) + { + values.add( items[ii] ); + } + + return values; + } + + + /** + * Creates, fills and returns an Attributes instance using the LDIF encoded + * within the property value. The LDIF should use '*' (asterisk) characters + * as line delimiters within the property value. These are replaced with + * newlines and fed to the LDIF parser. Also note that the LdifParser + * deposites the DN as a property within the attributes object. + * + * @param props the properties to get the ldif property from + * @param key the key for the LDIF property + * @return the attributes for the encoded LDIF entry + */ + public static Attributes fillAttributes( Properties props, String key, Attributes values ) throws NamingException + { + if ( props == null || ! props.containsKey( key ) || props.getProperty( key ) == null ) + { + if ( values == null ) + { + return new LockableAttributesImpl(); + } + + return values; + } + + if ( values == null ) + { + values = new LockableAttributesImpl(); + } + + String ldif = props.getProperty( key ).trim().replace( '*', '\n' ); + ( new LdifParserImpl() ).parse( values, ldif ); + return values; + } +}
