You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2004/11/08 00:15:58 UTC

svn commit: rev 56875 - in incubator/directory/ldap/trunk/common/src/java/org/apache/ldap/common: ldif util

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:aok123@bellsouth.net">Alex Karasulu</a>
- * @author $Author: akarasulu $
- * @version $Revision: 1.7 $
+ * @author <a href="mailto:directory-dev@incubator.apache.org">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:aok123@bellsouth.net">Alex Karasulu</a>
- * @author $Author: jmachols $
- * @version $Revision: 1.12 $
+ * @author <a href="mailto:directory-dev@incubator.apache.org">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:directory-dev@incubator.apache.org">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;
+    }
+}