You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2005/09/27 08:52:46 UTC

svn commit: r291833 - in /directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives: LdapRDN.java NameComponent.java

Author: elecharny
Date: Mon Sep 26 23:52:40 2005
New Revision: 291833

URL: http://svn.apache.org/viewcvs?rev=291833&view=rev
Log:
Added the two classes

Added:
    directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapRDN.java
    directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/NameComponent.java

Added: directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapRDN.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapRDN.java?rev=291833&view=auto
==============================================================================
--- directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapRDN.java (added)
+++ directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/LdapRDN.java Mon Sep 26 23:52:40 2005
@@ -0,0 +1,1290 @@
+/*
+ *   Copyright 2005 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.asn1new.ldap.codec.primitives;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.naming.InvalidNameException;
+
+import org.apache.asn1new.ldap.codec.utils.DNUtils;
+import org.apache.asn1new.util.MutableString;
+import org.apache.asn1new.util.StringUtils;
+
+/**
+ * This class store the name-component part or the following BNF grammar (as of RFC2253, par. 3, 
+ * and RFC1779, fig. 1) : <br>
+ * -    &lt;name-component&gt;         ::= &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; <br>
+ * -    &lt;attributeTypeAndValues&gt; ::= &lt;spaces&gt; '+' &lt;spaces&gt; &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; | e <br>
+ * -    &lt;attributeType&gt;          ::= [a-zA-Z] &lt;keychars&gt; | &lt;oidPrefix&gt; [0-9] &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt; <br>
+ * -    &lt;keychars&gt;               ::= [a-zA-Z] &lt;keychars&gt; | [0-9] &lt;keychars&gt; | '-' &lt;keychars&gt; | e <br>
+ * -    &lt;oidPrefix&gt;              ::= 'OID.' | 'oid.' | e <br>
+ * -    &lt;oids&gt;                   ::= '.' [0-9] &lt;digits&gt; &lt;oids&gt; | e <br>
+ * -    &lt;attributeValue&gt;         ::= &lt;pairs-or-strings&gt; | '#' &lt;hexstring&gt; |'"' &lt;quotechar-or-pairs&gt; '"' <br>
+ * -    &lt;pairs-or-strings&gt;       ::= '\' &lt;pairchar&gt; &lt;pairs-or-strings&gt; | &lt;stringchar&gt; &lt;pairs-or-strings&gt; | e <br>
+ * -    &lt;quotechar-or-pairs&gt;     ::= &lt;quotechar&gt; &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt; &lt;quotechar-or-pairs&gt; | e <br>
+ * -    &lt;pairchar&gt;               ::= ',' | '=' | '+' | '&lt;' | '&gt;' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F]  <br>
+ * -    &lt;hexstring&gt;              ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; <br>
+ * -    &lt;hexpairs&gt;               ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; | e <br>
+ * -    &lt;digits&gt;                 ::= [0-9] &lt;digits&gt; | e <br>
+ * -    &lt;stringchar&gt;             ::= [0x00-0xFF] - [,=+&lt;&gt;#;\"\n\r] <br>
+ * -    &lt;quotechar&gt;              ::= [0x00-0xFF] - [\"] <br>
+ * -    &lt;separator&gt;              ::= ',' | ';' <br>
+ * -    &lt;spaces&gt;                 ::= ' ' &lt;spaces&gt; | e <br>
+ *<br>
+ * A RDN is a part of a DN. It can be composed of many types, as in the RDN 
+ * following RDN :<br>
+ *   ou=value + cn=other value<br>
+ * <br>  
+ * In this case, we have to store an 'ou' and a 'cn' in the RDN.<br>
+ * <br>
+ * The types are case insensitive. <br>
+ * Spaces before and after types and values are not stored.<br>  
+ * Spaces before and after '+' are not stored.<br>
+ * <br>
+ * Thus, we can consider that the following RDNs are equals :<br>
+ * <br>
+ * 'ou=test 1'<br>
+ * '  ou=test 1'<br>
+ * 'ou  =test 1'<br>
+ * 'ou=  test 1'<br>
+ * 'ou=test 1 '<br>
+ * '  ou  =  test 1 '<br>
+ * <br>
+ * So are the following :<br>
+ * <br>
+ * 'ou=test 1+cn=test 2'<br>
+ * 'ou = test 1 + cn = test 2'<br>
+ * '  ou =test 1+  cn =test 2  ' <br>
+ * 'cn = test 2   +ou = test 1'<br>
+ * <br>
+ * but the following are not equal :<br>
+ * 'ou=test  1' <br>
+ * 'ou=test    1'<br> 
+ * because we have more than one spaces inside the value.<br>
+ * <br>
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ *
+ */
+public class LdapRDN implements Cloneable
+{
+    /** 
+     * Stores all couple type = value. We may have more than one type,
+     * if the '+' character appears in the NameComponent. The key is
+     * the type, the value is a NameComponent.
+     */
+    private Map nameComponents;
+    
+    /** 
+     * A simple NameComponent is used to store the LdapRDN for the simple
+     * case where we only have a single type=value. This will be 99.99%
+     * the case. This avoids the creation of a HashMap.
+     */
+    private NameComponent nameComponent;
+    
+    private transient MutableString normalizedName;
+    
+    /**
+     * A empty constructor.
+     */
+    public LdapRDN()
+    {
+        // Don't waste space... This is not so often we have multiple
+        // name-components in a RDN... So we won't initialize the Map.
+        nameComponents = null;
+        nameComponent = null;
+        normalizedName = null;
+    }
+    
+    /**
+     * A constructor that parse a String RDN
+     * 
+     * @param rdn The String containing the RDN to parse
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public LdapRDN( String rdn ) throws InvalidNameException
+    {
+        parseNameComponent( rdn, this );
+    }
+    
+    /**
+     * A constructor that parse a RDN
+     * 
+     * @param rdn The String containing the RDN to parse
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    
+    public LdapRDN( byte[] bytes ) throws InvalidNameException
+    {
+        parseNameComponent( bytes, 0, this );
+    }
+    
+    /**
+     * A constructor that constructs a RDN from a type and a value
+     * @param type The type of the RDN
+     * @param value The value of the RDN
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public LdapRDN( String type, String value ) throws InvalidNameException
+    {
+        // Don't waste space... This is not so often we have multiple
+        // name-components in a RDN... So we won't initialize the Map.
+        nameComponents = null;
+        nameComponent = new NameComponent( type, value );
+    }
+
+    /**
+     * Add a NameComponent to the current RDN
+     * @param type The type of the added RDN
+     * @param value The value of the added RDN
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public void addNameComponent( String type, String value) throws InvalidNameException
+    {
+        MutableString typeMs = null;
+        
+        try
+        {
+            typeMs = new MutableString( type );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "Type '" + type + "' is not a valid UTF-8 string");
+        }
+
+        MutableString valueMs = null;
+        
+        try
+        {
+            valueMs = new MutableString( value );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "Value '" + value + "' is not a valid UTF-8 string");
+        }
+
+        addNameComponent( typeMs, valueMs );
+    }
+    
+    /**
+     * Add a NameComponent to the current RDN
+     * @param type The type of the added RDN
+     * @param value The value of the added RDN
+     * @throws InvalidNameException If the RDN is invalid
+     */
+    public void addNameComponent( MutableString type, MutableString value) throws InvalidNameException
+    {
+        // First, check if we already have a NameComponent in this RDN
+        if ( ( nameComponent != null ) || ( nameComponents != null ) )
+        {
+            // Ok, we already have one NameComponent, at least.
+            // Do we have to create the HashMap ?
+            if ( nameComponents == null )
+            {
+                // First, create the HashMap if it's empty,
+                // and store the existing NameComponent into it.
+                nameComponents = new LinkedHashMap();
+                
+                nameComponents.put( nameComponent.getType(), nameComponent );
+            }
+
+            nameComponent = new NameComponent( type, value );
+                
+            // add a new NameComponent
+            nameComponents.put( nameComponent.getType(), nameComponent );
+        }
+        else
+        {
+            // This is the first NameComponent. Just stores it.
+            nameComponent = new NameComponent( type, value );
+        }
+    }
+
+    /**
+     * Remove a Name from a RDN
+     * 
+     * @param type The nome to remove
+     * @throws InvalidNameException If the name does not exists or if the RDN is empty
+     */
+    public void removeNameComponent( String type ) throws InvalidNameException
+    {
+        if ( StringUtils.isEmpty( type ) )
+        {
+            return;
+        }
+        
+        if ( nameComponents == null )
+        {
+            if ( nameComponent == null ) 
+            {
+                throw new InvalidNameException( "Cannot remove a nameComponent form an empty RDN" );
+            }
+            else if ( type.equals( nameComponent.getType() ) )
+            {
+                nameComponent = null;
+            }
+            else
+            {
+                throw new InvalidNameException( "Name '" + type + "' is not valid for the RDN '" + this.toString() + "'");
+            }
+        }
+        else
+        {
+            if ( nameComponents.containsKey( type ) )
+            {
+                nameComponents.remove( type );
+            }
+            else
+            {
+                throw new InvalidNameException( "Name '" + type + "' is not valid for the RDN '" + this.toString() + "'");
+            }
+        }
+    }
+    
+    /**
+     * Clear the RDN, removing all the NameComponents.
+     */
+    public void clear()
+    {
+        nameComponent = null;
+        nameComponents = null;
+    }
+    
+    public int getLength()
+    {
+        return 
+    }
+    
+    /**
+     * Get the Value of the NameComponent which type is given as an argument.
+     * 
+     * @param type The type of the NameArgument
+     * @return The Value to be returned, or null if none found.
+     */
+    public MutableString getValue( String type ) throws InvalidNameException
+    {
+        MutableString typeMs = null;
+        
+        try
+        {
+            typeMs = new MutableString( type );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "Type '" + type + "' is not a valid UTF-8 string");
+        }
+        
+        // First, let's normalize the type
+        MutableString normalizedType = MutableString.trim( typeMs );
+        normalizedType.toLowerCase();
+        
+        if ( nameComponents != null )
+        {
+            if ( nameComponents.containsKey( normalizedType ) )
+            {
+                return ( (NameComponent)nameComponents.get( normalizedType ) ).getValue();
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        {
+            if ( MutableString.equals( nameComponent.getType(), normalizedType ) )
+            {
+                return nameComponent.getValue();
+            }
+            else
+            {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Get the NameComponent which type is given as an argument.
+     * 
+     * @param type The type of the NameArgument to be returned
+     * @return The NameComponent, of null if none is found.
+     */
+    public NameComponent getNameComponent( String type )
+    {
+        // First, let's normalize the type
+        String normalizedType = StringUtils.lowerCase( StringUtils.trim( type ) );
+
+        if ( nameComponents != null )
+        {
+            if ( nameComponents.containsKey( normalizedType ) )
+            {
+                return (NameComponent)nameComponents.get( normalizedType );
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        {
+            if ( nameComponent.getType().equals( normalizedType ) )
+            {
+                return nameComponent;
+            }
+            else
+            {
+                return null;
+            }
+        }
+    }
+    
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;oidPrefix&gt; ::= 'OID.' | 'oid.' | e
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the char array
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array
+     */
+    private static int parseOidPrefix( char[] chars, int pos )
+    {
+        if ( ( StringUtils.areEquals( chars, pos, DNUtils.OID_LOWER ) == DNUtils.PARSING_ERROR ) &&
+             ( StringUtils.areEquals( chars, pos, DNUtils.OID_UPPER ) == DNUtils.PARSING_ERROR ) )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+            pos += DNUtils.OID_LOWER.length;
+
+            return pos;
+        }
+    }
+    
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;oidPrefix&gt; ::= 'OID.' | 'oid.' | e
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+     */
+    private static int parseOidPrefix( byte[] bytes, int pos )
+    {
+
+        if ( ( StringUtils.areEquals( bytes, pos, DNUtils.OID_LOWER_BYTES ) == DNUtils.PARSING_ERROR ) &&
+             ( StringUtils.areEquals( bytes, pos, DNUtils.OID_UPPER_BYTES ) == DNUtils.PARSING_ERROR ) )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+            pos += DNUtils.OID_LOWER_BYTES.length;
+
+            return pos;
+        }
+    }
+    
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;oidValue&gt; ::= [0-9] &lt;digits&gt; &lt;oids&gt; 
+     * </p>
+     * 
+     * @param chars The char array to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array
+     */
+    private static int parseOidValue(char[] chars, int pos)
+    {
+        // <attributType> ::= [0-9] <digits> <oids>
+        if ( StringUtils.isDigit( chars, pos ) == false )
+        {
+            // Nope... An error
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+            // Let's process an oid
+            pos++;
+
+            while ( StringUtils.isDigit( chars, pos ) )
+            {
+                pos++;
+            }
+
+            // <oids> ::= '.' [0-9] <digits> <oids> | e
+            if ( StringUtils.isCharASCII( chars, pos, '.' ) == false )
+            {
+                return pos;
+            }
+            else
+            {
+                do
+                {
+                    pos++;
+
+                    if ( StringUtils.isDigit( chars, pos ) == false )
+                    {
+                        return DNUtils.PARSING_ERROR;
+                    }
+                    else
+                    {
+                        pos++;
+
+                        while ( StringUtils.isDigit( chars, pos ) )
+                        {
+                            pos++;
+                        }
+                    }
+                }
+                while ( StringUtils.isCharASCII( chars, pos, '.' ) );
+
+                return pos;
+            }
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;oidValue&gt; ::= [0-9] &lt;digits&gt; &lt;oids&gt; 
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+     */
+    private static int parseOidValue(byte[] bytes, int pos)
+    {
+        // <attributType> ::= [0-9] <digits> <oids>
+        if ( StringUtils.isDigit( bytes, pos ) == false )
+        {
+
+            // Nope... An error
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+
+            // Let's process an oid
+            pos++;
+
+            while ( StringUtils.isDigit( bytes, pos ) )
+            {
+                pos++;
+            }
+
+            // <oids> ::= '.' [0-9] <digits> <oids> | e
+            if ( StringUtils.isCharASCII( bytes, pos, '.' ) == false )
+            {
+
+                return pos;
+            }
+            else
+            {
+
+                do
+                {
+                    pos++;
+
+                    if ( StringUtils.isDigit( bytes, pos ) == false )
+                    {
+
+                        return DNUtils.PARSING_ERROR;
+                    }
+                    else
+                    {
+                        pos++;
+
+                        while ( StringUtils.isDigit( bytes, pos ) )
+                        {
+                            pos++;
+                        }
+                    }
+                }
+                while ( StringUtils.isCharASCII( bytes, pos, '.' ) );
+
+                return pos;
+            }
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributType&gt; ::= [a-zA-Z] &lt;keychars&gt; | 
+     *                          &lt;oidPrefix&gt; [0-9] &lt;digits&gt; &lt;oids&gt; | 
+     *                          [0-9] &lt;digits&gt; &lt;oids&gt;
+     * </p>
+     * 
+     * The string *MUST* be an ASCII string, not an unicode string.
+     * 
+     * @param chars The char array to parse
+     * @param pos The current position in the char array
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array
+     */
+    private static int parseAttributeType( char[] chars, int pos )
+    {
+        // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids>
+        
+        if ( StringUtils.isAlphaASCII( chars, pos ))
+        {
+            // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> 
+
+            // We have got an Alpha char, it may be the begining of an OID ?
+            int oldPos = pos;
+            
+            if ( ( pos = parseOidPrefix( chars, oldPos ) ) != DNUtils.PARSING_ERROR ) 
+            {
+                return parseOidValue(chars, pos);
+            }
+            else 
+            {
+                // It's not an oid, it's a String (ASCII)
+                // <attributType> ::= [a-zA-Z] <keychars>
+                // <keychars>       ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-' <keychar> | e
+                pos = oldPos + 1;
+
+                while ( StringUtils.isAlphaDigitMinus( chars, pos ) )
+                {
+                    pos++;
+                }
+
+                return pos;
+            }
+        }
+        else
+        {
+
+            // An oid
+            // <attributType> ::= [0-9] <digits> <oids> 
+            return parseOidValue(chars, pos);
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributType&gt; ::= [a-zA-Z] &lt;keychars&gt; | 
+     *                          &lt;oidPrefix&gt; [0-9] &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt;
+     * </p>
+     * 
+     * The string *MUST* be an ASCII string, not an unicode string.
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+     */
+    private static int parseAttributeType( byte[] bytes, int pos )
+    {
+
+        // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids>
+        
+        if ( StringUtils.isAlphaASCII( bytes, pos ))
+        {
+            // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> 
+
+            // We have got an Alpha char, it may be the begining of an OID ?
+            int oldPos = pos;
+            
+            if ( ( pos = parseOidPrefix( bytes, oldPos ) ) != -1 ) 
+            {
+                return parseOidValue(bytes, pos);
+            }
+            else 
+            {
+                // It's not an oid, it's a String (ASCII)
+                // <attributType> ::= [a-zA-Z] <keychars>
+                // <keychars>       ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-' <keychar> | e
+                pos = oldPos + 1;
+
+                while ( StringUtils.isAlphaDigitMinus( bytes, pos ) )
+                {
+                    pos++;
+                }
+
+                return pos;
+            }
+        }
+        else
+        {
+
+            // An oid
+            // <attributType> ::= [0-9] <digits> <oids> 
+            return parseOidValue(bytes, pos);
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributeValue&gt;     ::= &lt;pairs-or-strings&gt; | '#' &lt;hexstring&gt; |'"' &lt;quotechar-or-pairs&gt; '"' <br>
+     * &lt;pairs-or-strings&gt;    ::= '\' &lt;pairchar&gt; &lt;pairs-or-strings&gt; | &lt;stringchar&gt; &lt;pairs-or-strings&gt; |  | e <br>
+     * &lt;quotechar-or-pairs&gt;    ::= &lt;quotechar&gt; &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt; &lt;quotechar-or-pairs&gt; | e <br>
+     * </p>
+     * 
+     * @param chars The char array to parse
+     * @param pos The current position in the char array
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array
+     */
+    private static int parseAttributeValue( char[] chars, int pos )
+    {
+        if ( StringUtils.isCharASCII( chars, pos, '#' ) )
+        {
+            pos++;
+
+            // <attributeValue> ::= '#' <hexstring>
+            if ( ( pos = DNUtils.parseHexString( chars, pos ) ) == DNUtils.PARSING_ERROR )
+            {
+
+                return DNUtils.PARSING_ERROR;
+            }
+
+            return StringUtils.trimLeft( chars, pos );
+        }
+        else if ( StringUtils.isCharASCII( chars, pos, '"' ) )
+        {
+            pos++;
+            int nbBytes = 0;
+
+            // <attributeValue>     ::= '"' <quotechar-or-pair> '"'
+            // <quotechar-or-pairs>    ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e
+            while ( true )
+            {
+                if ( StringUtils.isCharASCII( chars, pos, '\\' ) )
+                {
+                    pos++;
+
+                    if ( DNUtils.isPairChar( chars, pos ) )
+                    {
+                        pos++;
+                    }
+                    else
+                    {
+                        return DNUtils.PARSING_ERROR;
+                    }
+                }
+                else if ( (nbBytes = DNUtils.isQuoteChar( chars, pos ) ) != DNUtils.PARSING_ERROR )
+                {
+                    pos += nbBytes;
+                }
+                else
+                {
+                    break;
+                }
+            }
+
+            if ( StringUtils.isCharASCII( chars, pos, '"' ) )
+            {
+                pos++;
+
+                return StringUtils.trimLeft( chars, pos );
+            }
+            else
+            {
+                return DNUtils.PARSING_ERROR;
+            }
+        }
+        else
+        {
+            while ( true )
+            {
+                if ( StringUtils.isCharASCII( chars, pos, '\\' ) )
+                {
+                    // '\' <pairchar> <pairs-or-strings>
+                    pos++;
+
+                    if ( DNUtils.isPairChar( chars, pos ) == false )
+                    {
+                        return DNUtils.PARSING_ERROR;
+                    }
+                    else
+                    {
+                        pos++;
+                    }
+                }
+                else
+                {
+                    int nbChars = 0;
+                    
+                    // <stringchar> <pairs-or-strings>
+                    if ( ( nbChars = DNUtils.isStringChar( chars, pos )) != DNUtils.PARSING_ERROR )
+                    {
+                        // A special case : if we have some spaces before the '+' character,
+                        // we MUST skip them.
+                        if ( StringUtils.isCharASCII( chars, pos, ' ') )
+                        {
+                            pos = StringUtils.trimLeft( chars, pos );
+
+                            if ( ( DNUtils.isStringChar( chars, pos ) == DNUtils.PARSING_ERROR ) &&
+                                    ( StringUtils.isCharASCII( chars, pos, '\\' ) == false ) )
+                            {
+                                // Ok, we are done with the stringchar.
+                                return pos;
+                            }
+                        }
+                        else
+                        {
+                            // An unicode char could be more than one byte long 
+                            pos += nbChars;
+                        }
+                    }
+                    else
+                    {
+                        return pos;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributeValue&gt;     ::= &lt;pairs-or-strings&gt; | '#' &lt;hexstring&gt; |'"' &lt;quotechar-or-pairs&gt; '"' <br>
+     * &lt;pairs-or-strings&gt;    ::= '\' &lt;pairchar&gt; &lt;pairs-or-strings&gt; | &lt;stringchar&gt; &lt;pairs-or-strings&gt; |  | e <br>
+     * &lt;quotechar-or-pairs&gt;    ::= &lt;quotechar&gt; &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt; &lt;quotechar-or-pairs&gt; | e <br>
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+     */
+    private static int parseAttributeValue( byte[] bytes, int pos )
+    {
+        if ( StringUtils.isCharASCII( bytes, pos, '#' ) )
+        {
+            pos++;
+
+            // <attributeValue> ::= '#' <hexstring>
+            if ( ( pos = DNUtils.parseHexString( bytes, pos ) ) == -1 )
+            {
+
+                return DNUtils.PARSING_ERROR;
+            }
+
+            return StringUtils.trimLeft( bytes, pos );
+        }
+        else if ( StringUtils.isCharASCII( bytes, pos, '"' ) )
+        {
+            pos++;
+            int nbBytes = 0;
+
+            // <attributeValue>     ::= '"' <quotechar-or-pair> '"'
+            // <quotechar-or-pairs>    ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e
+            while ( true )
+            {
+                if ( StringUtils.isCharASCII( bytes, pos, '\\' ) )
+                {
+                    pos++;
+
+                    if ( DNUtils.isPairChar( bytes, pos ) )
+                    {
+                        pos++;
+                    }
+                    else
+                    {
+                        return DNUtils.PARSING_ERROR;
+                    }
+                }
+                else if ( (nbBytes = DNUtils.isQuoteChar( bytes, pos ) ) != -1 )
+                {
+                    pos += nbBytes;
+                }
+                else
+                {
+                    break;
+                }
+            }
+
+            if ( StringUtils.isCharASCII( bytes, pos, '"' ) )
+            {
+                pos++;
+
+                return StringUtils.trimLeft( bytes, pos );
+            }
+            else
+            {
+                return DNUtils.PARSING_ERROR;
+            }
+        }
+        else
+        {
+            while ( true )
+            {
+                if ( StringUtils.isCharASCII( bytes, pos, '\\' ) )
+                {
+                    // '\' <pairchar> <pairs-or-strings>
+                    pos++;
+
+                    if ( DNUtils.isPairChar( bytes, pos ) == false )
+                    {
+                        return -1;
+                    }
+                    else
+                    {
+                        pos++;
+                    }
+                }
+                else
+                {
+                    int nbBytes = 0;
+                    
+                    // <stringchar> <pairs-or-strings>
+                    if ( (nbBytes = DNUtils.isStringChar( bytes, pos )) != -1)
+                    {
+                        // A special case : if we have some spaces before the '+' character,
+                        // we MUST skip them.
+                        if ( StringUtils.isCharASCII( bytes, pos, ' ') )
+                        {
+                            pos = StringUtils.trimLeft( bytes, pos );
+
+                            if ( ( DNUtils.isStringChar( bytes, pos ) == -1 ) &&
+                                    ( StringUtils.isCharASCII( bytes, pos, '\\' ) == false ) )
+                            {
+                                // Ok, we are done with the stringchar.
+                                return pos;
+                            }
+                        }
+                        else
+                        {
+                            // An unicode char could be more than one byte long 
+                            pos += nbBytes;
+                        }
+                    }
+                    else
+                    {
+                        return pos;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributeTypeAndValues&gt;    ::= &lt;spaces&gt; '+' &lt;spaces&gt; &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; | e
+     * </p>
+     * 
+     * @param chars The char buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the char buffer, or PARSING_ERROR if the rule does not apply to the char buffer
+     */
+    private static int parseAttributeTypeAndValues( char[] chars, int pos, LdapRDN rdn ) throws InvalidNameException
+    {
+        int newPos = 0;
+        MutableString type = null;
+        MutableString value = null;
+
+        while ( true )
+        {
+            pos = StringUtils.trimLeft( chars, pos );
+
+            if ( StringUtils.isCharASCII( chars, pos, '+' ) )
+            {
+                pos++;
+            }
+            else
+            {
+                // <attributeTypeAndValues> ::= e
+                return pos;
+            }
+
+            pos = StringUtils.trimLeft( chars, pos );
+
+            if ( ( newPos = parseAttributeType( chars, pos ) ) == DNUtils.PARSING_ERROR )
+            {
+                return DNUtils.PARSING_ERROR;
+            }
+
+            if ( rdn != null )
+            {
+                try
+                {
+                    type = new MutableString( chars, pos, newPos - pos );
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    throw new InvalidNameException( "Invalid UTF-8 String" );
+                }
+            }
+
+            pos = StringUtils.trimLeft( chars, newPos );
+
+            if ( StringUtils.isCharASCII( chars, pos, '=' ) )
+            {
+                pos++;
+            }
+            else
+            {
+                return DNUtils.PARSING_ERROR;
+            }
+
+            pos = StringUtils.trimLeft( chars, pos );
+
+            newPos = parseAttributeValue( chars, pos );
+            
+            if ( newPos != DNUtils.PARSING_ERROR )
+            {
+                if ( rdn != null )
+                {
+                    try
+                    {
+                        value = new MutableString( chars, pos, newPos - pos );
+                    }
+                    catch ( UnsupportedEncodingException uee )
+                    {
+                        throw new InvalidNameException( "Invalid UTF-8 String" );
+                    }
+                    
+                    rdn.addNameComponent( type, value );
+                }
+            }
+            
+            pos = newPos;
+        }
+    }
+
+    /**
+     * Parse this rule : <br>
+     * <p>
+     * &lt;attributeTypeAndValues&gt;    ::= &lt;spaces&gt; '+' &lt;spaces&gt; &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; | e
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the byte buffer
+     * @return The new position in the byte buffer, or -1 if the rule does not apply to the byte buffer
+     */
+    private static int parseAttributeTypeAndValues( byte[] bytes, int pos, LdapRDN rdn ) throws InvalidNameException
+    {
+        int newPos = 0;
+        MutableString type = null;
+        MutableString value = null;
+
+        while ( true )
+        {
+            pos = StringUtils.trimLeft( bytes, pos );
+
+            if ( StringUtils.isCharASCII( bytes, pos, '+' ) )
+            {
+                pos++;
+            }
+            else
+            {
+                // <attributeTypeAndValues> ::= e
+                return pos;
+            }
+
+            pos = StringUtils.trimLeft( bytes, pos );
+
+            if ( ( newPos = parseAttributeType( bytes, pos ) ) == DNUtils.PARSING_ERROR )
+            {
+                return DNUtils.PARSING_ERROR;
+            }
+
+            if ( rdn != null )
+            {
+                try
+                {
+                    type = new MutableString( bytes, pos, newPos - pos );
+                }
+                catch ( UnsupportedEncodingException uee )
+                {
+                    throw new InvalidNameException( uee.getMessage() );
+                }
+            }
+
+            pos = StringUtils.trimLeft( bytes, pos );
+
+            if ( StringUtils.isCharASCII( bytes, pos, '=' ) )
+            {
+                pos++;
+            }
+            else
+            {
+
+                return DNUtils.PARSING_ERROR;
+            }
+
+            pos = StringUtils.trimLeft( bytes, pos );
+
+            newPos = parseAttributeValue( bytes, pos );
+            
+            if ( newPos != DNUtils.PARSING_ERROR )
+            {
+                if ( rdn != null )
+                {
+                    try
+                    {
+                        value = new MutableString( bytes, pos, newPos - pos );
+                    }
+                    catch ( UnsupportedEncodingException uee )
+                    {
+                        throw new InvalidNameException( uee.getMessage() );
+                    }
+                    
+                    rdn.addNameComponent( type, value );
+                }
+            }
+            
+            pos = newPos;
+        }
+    }
+
+    /**
+     * Parse a NameComponent : <br>
+     * <p>
+     * &lt;name-component&gt;    ::= &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt;
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the buffer
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array 
+     */
+    public static int parseNameComponent( char[] chars, int pos, LdapRDN rdn ) throws InvalidNameException
+    {
+        int newPos = 0;
+        String type = null;
+        String value = null;
+        
+        pos = StringUtils.trimLeft( chars, pos );
+        
+        if ( ( newPos = parseAttributeType( chars, pos ) ) == DNUtils.PARSING_ERROR )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        
+        if ( rdn != null )
+        {
+            type = new String( chars, pos, newPos - pos );
+        }
+        
+        pos = StringUtils.trimLeft( chars, newPos );
+
+        if ( StringUtils.isCharASCII( chars, pos, '=' ) == false )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+            pos++;
+        }
+
+        pos = StringUtils.trimLeft( chars, pos );
+
+        if ( ( newPos = parseAttributeValue( chars, pos ) ) == DNUtils.PARSING_ERROR )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        
+        if ( rdn != null )
+        {
+            value = new String( chars, pos, newPos - pos );
+            
+            rdn.addNameComponent( type, value );
+        }
+
+        return parseAttributeTypeAndValues( chars, newPos, rdn );
+    }
+
+    /**
+     * Parse a NameComponent : <br>
+     * <p>
+     * &lt;name-component&gt;    ::= &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt;
+     * </p>
+     * 
+     * @param string The buffer to parse
+     * @return The RDN, or null if the rule does not apply to the char array 
+     */
+    public static LdapRDN parseNameComponent( String string ) throws InvalidNameException
+    {
+        LdapRDN rdn = new LdapRDN();
+        
+        parseNameComponent( string.toCharArray(), 0, rdn );
+        
+        return rdn;
+    }
+
+    /**
+     * Parse a NameComponent : <br>
+     * <p>
+     * &lt;name-component&gt;    ::= &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt;
+     * </p>
+     * 
+     * @param string The buffer to parse
+     * @param rdn The RDN to fill. Beware that if the RDN is not empty, the new NameComponent will be added. 
+     * @return The RDN, or null if the rule does not apply to the char array 
+     */
+    public static void parseNameComponent( String string, LdapRDN rdn ) throws InvalidNameException
+    {
+        parseNameComponent( string.toCharArray(), 0, rdn );
+    }
+    
+    /**
+     * Parse a NameComponent : <br>
+     * <p>
+     * &lt;name-component&gt;    ::= &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt; &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt;
+     * </p>
+     * 
+     * @param bytes The buffer to parse
+     * @param pos The current position in the buffer
+     * @return The new position in the char array, or PARSING_ERROR if the rule does not apply to the char array 
+     */
+    public static int parseNameComponent( byte[] bytes, int pos, LdapRDN rdn ) throws InvalidNameException
+    {
+        int newPos = 0;
+        MutableString type = null;
+        MutableString value = null;
+        rdn.normalizedName = new MutableString( bytes.length );
+        
+        pos = StringUtils.trimLeft( bytes, pos );
+        
+        if ( ( newPos = parseAttributeType( bytes, pos ) ) == DNUtils.PARSING_ERROR )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        
+        if ( rdn != null )
+        {
+            try
+            {
+                type = new MutableString( bytes, pos, newPos - pos );
+                rdn.normalizedName.append( type );
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                throw new InvalidNameException( uee.getMessage() );
+            }
+                
+        }
+        
+        pos = StringUtils.trimLeft( bytes, newPos );
+
+        if ( StringUtils.isCharASCII( bytes, pos, '=' ) == false )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        else
+        {
+            pos++;
+        }
+
+        pos = StringUtils.trimLeft( bytes, pos );
+
+        if ( ( newPos = parseAttributeValue( bytes, pos ) ) == DNUtils.PARSING_ERROR )
+        {
+            return DNUtils.PARSING_ERROR;
+        }
+        
+        if ( rdn != null )
+        {
+            try
+            {
+                value = new MutableString( bytes, pos, newPos - pos );
+            }
+            catch ( UnsupportedEncodingException uee )
+            {
+                throw new InvalidNameException( uee.getMessage() );
+            }
+            
+            rdn.addNameComponent( type, value );
+        }
+
+        return parseAttributeTypeAndValues( bytes, newPos, rdn );
+    }
+
+    /**
+     * Clone the LdapRDN
+     */
+    public Object clone()
+    {
+        try
+        {
+            LdapRDN rdn = (LdapRDN)super.clone();
+            
+            // The nameComponent is immutable. We won't clone it
+            
+            if ( nameComponents != null )
+            {
+                nameComponents = (LinkedHashMap)((LinkedHashMap)this.nameComponents).clone();
+                
+                Iterator values = nameComponents.values().iterator();
+                
+                while ( values.hasNext() )
+                {
+                    NameComponent nc = (NameComponent)values.next();
+                    rdn.nameComponents.put( nc.getType(), nc );
+                }
+            }
+            
+            return rdn;
+        }
+        catch ( CloneNotSupportedException cnse )
+        {
+            throw new Error( "Assertion failure" );
+        }
+    }
+
+    /**
+     * Returns a String representation of the RDN
+     */
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        if ( nameComponents != null )
+        {
+            Iterator elems = nameComponents.values().iterator();
+            boolean isFirst = true;
+            
+            while ( elems.hasNext() )
+            {
+                if ( isFirst )
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    sb.append( '+' );
+                }
+                
+                sb.append( elems.next() );
+            }
+        }
+        else
+        {
+            if ( nameComponent != null )
+            {
+                sb.append( nameComponent );
+            }
+            else
+            {
+                return null;
+            }
+        }
+        
+        return sb.toString();
+    }
+}

Added: directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/NameComponent.java
URL: http://svn.apache.org/viewcvs/directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/NameComponent.java?rev=291833&view=auto
==============================================================================
--- directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/NameComponent.java (added)
+++ directory/shared/ldap/branches/shared-ldap-NameComponent/apache2-provider/src/java/main/org/apache/asn1new/ldap/codec/primitives/NameComponent.java Mon Sep 26 23:52:40 2005
@@ -0,0 +1,230 @@
+/*
+ *   Copyright 2005 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.asn1new.ldap.codec.primitives;
+
+import java.io.UnsupportedEncodingException;
+
+import javax.naming.InvalidNameException;
+
+import org.apache.asn1new.util.MutableString;
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * A name-component, which is the basis of all RDN.
+ * It contains a type, and a value.
+ * 
+ * The type must not be case sensitive. Superfluous leading
+ * and trailing spaces MUST have been trimmed before.
+ * 
+ * The value MUST be in UTF8 format, according to RFC 2253. If the type 
+ * is in OID form, then the value must be a hexadecimal string prefixed 
+ * by a '#' character. Otherwise, the string must respect the RC 2253
+ * grammar. No further normalization will be done, because we don't
+ * have any knowledge of the Schema definition in the parser. 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class NameComponent implements Cloneable
+{
+    /** The Name type */
+    private MutableString type;
+    
+    /** The name value */
+    private MutableString value;
+    
+    /**
+     * Construct an empty NameComponent
+     */ 
+    public NameComponent()
+    {
+        type = null;
+        value = null;
+    }
+    
+    /**
+     * Construct an NameComponent. The type and value are normalized :
+     * - the type is trimmed and lowercased
+     * - the value is trimmed
+     * 
+     * @param type The type
+     * @param value the value
+     */ 
+    public NameComponent( String type, String value ) throws InvalidNameException
+    {
+        try
+        {
+            setTypeNormalized( new MutableString( type ) );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "The type is not a valid UTF-8 string" );
+        }
+
+        try
+        {
+            setValueNormalized( new MutableString( value ) );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "The value is not a valid UTF-8 string" );
+        }
+    }
+    
+    /**
+     * Construct an NameComponent. The type and value are normalized :
+     * - the type is trimmed and lowercased
+     * - the value is trimmed
+     * 
+     * @param type The type
+     * @param value the value
+     */ 
+    public NameComponent( MutableString type, MutableString value ) throws InvalidNameException
+    {
+        setTypeNormalized( type );
+        setValueNormalized( value );
+    }
+    
+    /**
+     * Get the type of a NameComponent
+     * 
+     * @return The type
+     */
+    public MutableString getType()
+    {
+        return type;
+    }
+    
+    /**
+     * Store the type 
+     * 
+     * @param type The NameComponent type 
+     */
+    public void setType( MutableString type ) throws InvalidNameException
+    {
+        if ( MutableString.isEmpty( type ) )
+        {
+            throw new InvalidNameException( "The NameComponent type cannot be null : " );
+        }
+        
+        this.type = type;
+    }
+    
+    /**
+     * Store the type 
+     * 
+     * @param type The NameComponent type 
+     */
+    public void setType( String type ) throws InvalidNameException
+    {
+        if ( StringUtils.isEmpty( type ) )
+        {
+            throw new InvalidNameException( "The NameComponent type cannot be null" );
+        }
+        
+        try
+        {
+            this.type = new MutableString( type );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new InvalidNameException( "The NameComponent type is not a valid UTF-8 string" );
+        }
+    }
+    
+    /**
+     * Store the type, after having trimmed and lowercased it.
+     * 
+     * @param type The NameComponent type 
+     */
+    public void setTypeNormalized( MutableString type ) throws InvalidNameException
+    {
+        this.type = MutableString.trim( type );
+        this.type.toLowerCase();
+
+        if ( MutableString.isEmpty( this.type ) )
+        {
+            throw new InvalidNameException( "The NameComponent type cannot be null : " );
+        }
+    }
+    
+    /**
+     * Get the Value of a NameComponent
+     * 
+     * @return The value
+     */
+    public MutableString getValue()
+    {
+        return value;
+    }
+    
+    /**
+     * Store the value of a NameComponent.
+     * 
+     * @param value The value of the NameComponent
+     */
+    public void setValue( MutableString value )
+    {
+        this.value = MutableString.isEmpty( value ) ? MutableString.EMPTY_STRING : value;
+    }
+
+    /**
+     * Store the value of a NameComponent, after having trimmed it. 
+     * 
+     * @param value The value of the NameComponent
+     */
+    public void setValueNormalized( MutableString value )
+    {
+        this.value = MutableString.trim( value );
+
+        if ( MutableString.isEmpty( value ) )
+        {
+            this.value = MutableString.EMPTY_STRING;
+        }
+    }
+    
+    /**
+     * Implements the cloning.
+     * 
+     * @return a clone of this object
+     */
+    public Object clone() 
+    {
+        try 
+        {
+            return super.clone();
+        }
+        catch ( CloneNotSupportedException cnse )
+        {
+            throw new Error( "Assertion failure" );
+        }
+    }
+    
+    /**
+     * A String representation of a NameComponent.
+     * 
+     * @return A string representing a NameComponent
+     */
+    public String toString()
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        sb.append( type ).append( "=" ).append( value );
+        
+        return sb.toString();
+    }
+}
+