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 2006/01/05 10:03:52 UTC
svn commit: r366127 [2/3] - in
/directory/branches/DN-refactoring/ldap-common/src: main/antlr/
main/java/org/apache/ldap/common/aci/ main/java/org/apache/ldap/common/codec/
main/java/org/apache/ldap/common/codec/add/
main/java/org/apache/ldap/common/co...
Added: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/Rdn.java
URL: http://svn.apache.org/viewcvs/directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/Rdn.java?rev=366127&view=auto
==============================================================================
--- directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/Rdn.java (added)
+++ directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/Rdn.java Thu Jan 5 01:02:51 2006
@@ -0,0 +1,1082 @@
+/*
+ * 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.ldap.common.name;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+
+import javax.naming.InvalidNameException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+
+import org.apache.commons.collections.MultiHashMap;
+import org.apache.ldap.common.codec.AttributeTypeAndValue;
+import org.apache.ldap.common.util.StringTools;
+
+/**
+ * This class store the name-component part or the following BNF grammar (as of RFC2253, par. 3,
+ * and RFC1779, fig. 1) : <br>
+ * - <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> <br>
+ * - <attributeTypeAndValues> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> | e <br>
+ * - <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids> <br>
+ * - <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' <keychars> | e <br>
+ * - <oidPrefix> ::= 'OID.' | 'oid.' | e <br>
+ * - <oids> ::= '.' [0-9] <digits> <oids> | e <br>
+ * - <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ * - <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | e <br>
+ * - <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e <br>
+ * - <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br>
+ * - <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br>
+ * - <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br>
+ * - <digits> ::= [0-9] <digits> | e <br>
+ * - <stringchar> ::= [0x00-0xFF] - [,=+<>#;\"\n\r] <br>
+ * - <quotechar> ::= [0x00-0xFF] - [\"] <br>
+ * - <separator> ::= ',' | ';' <br>
+ * - <spaces> ::= ' ' <spaces> | 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>
+ * or
+ * <br>
+ * ou=value + ou=another 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>
+ *
+ * The Rdn is composed of one or more AttributeTypeAndValue (atav)
+ * Those atavs are ordered in the alphabetical natural order :
+ * a < b < c ... < z
+ *
+ * As the type are not case sensitive, we can say that a = A
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ *
+ */
+public class Rdn implements Cloneable, Comparable, Serializable
+{
+ /**
+ * Declares the Serial Version Uid.
+ *
+ * @see <a href="http://c2.com/cgi/wiki?AlwaysDeclareSerialVersionUid">Always Declare Serial Version Uid</a>
+ */
+ private static final long serialVersionUID = 1L;
+
+ /** The User Provided RDN */
+ private String upName = null;
+
+ /** The normalized RDN */
+ private String string = null;
+
+ /**
+ * Stores all couple type = value. We may have more than one type,
+ * if the '+' character appears in the AttributeTypeAndValue. This is
+ * a TreeSet, because we want the ATAVs to be sorted.
+ * An atav may contain more than one value. In this case, the values
+ * are String stored in a List.
+ */
+ private TreeSet atavs = null;
+
+ /**
+ * We also keep a set of types, in order to use manipulations. A type is
+ * connected with the atav it represents.
+ */
+ private Map atavTypes = new MultiHashMap();
+
+ /**
+ * A simple AttributeTypeAndValue is used to store the Rdn 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 AttributeTypeAndValue atav = null;
+
+ /** The number of atavs. We store this number here to avoid complex
+ * manipulation of atav and atavs */
+ private int nbAtavs = 0;
+
+ /** CompareTo() results */
+ public static final int UNDEFINED = Integer.MAX_VALUE;
+ public static final int SUPERIOR = 1;
+ public static final int INFERIOR = -1;
+ public static final int EQUALS = 0;
+
+ /**
+ * A empty constructor.
+ */
+ public Rdn()
+ {
+ // Don't waste space... This is not so often we have multiple
+ // name-components in a RDN... So we won't initialize the Map and the treeSet.
+ upName = "";
+ string = "";
+ }
+
+ /**
+ * A constructor that parse a String RDN
+ *
+ * @param rdn The String containing the RDN to parse
+ * @throws InvalidNameException If the RDN is invalid
+ */
+ public Rdn( String rdn ) throws InvalidNameException
+ {
+ if ( StringTools.isNotEmpty( rdn ) )
+ {
+ try
+ {
+ // Check that the String is a Valid UTF-8 string
+ rdn.getBytes( "UTF-8" );
+ }
+ catch ( UnsupportedEncodingException uee )
+ {
+ throw new InvalidNameException( "The byte array is not an UTF-8 encoded Unicode String : " + uee.getMessage() );
+ }
+
+ // Parse the string. The Rdn will be updated.
+ RdnParser.parse( rdn, this );
+ normalizeString();
+ // The upName is set by the RdnParser
+ }
+ else
+ {
+ upName = "";
+ string = "";
+ }
+ }
+
+ /**
+ * A constructor that parse a RDN from a byte array. This method
+ * is called when whe get a LdapMessage which contains a byte
+ * array representing the ASN.1 RelativeRDN
+ *
+ * @param rdn The byte array containing the RDN to parse
+ * @throws InvalidNameException If the RDN is invalid
+ */
+
+ public Rdn( byte[] bytes ) throws InvalidNameException
+ {
+ try
+ {
+ RdnParser.parse( new String( bytes, "UTF-8" ), this );
+ normalizeString();
+ // The upName is set by the RdnParser
+ }
+ catch ( UnsupportedEncodingException uee )
+ {
+ throw new InvalidNameException( "The byte array is not an UTF-8 encoded Unicode String : " + uee.getMessage() );
+ }
+ }
+
+ /**
+ * A constructor that constructs a RDN from a type and a value.
+ *
+ * Constructs an Rdn from the given attribute type and value. The string attribute
+ * values are not interpretted as RFC 2253 formatted RDN strings. That is, the
+ * values are used literally (not parsed) and assumed to be unescaped.
+ *
+ * @param type The type of the RDN
+ * @param value The value of the RDN
+ * @throws InvalidNameException If the RDN is invalid
+ */
+ public Rdn( String type, String value ) throws InvalidNameException
+ {
+ super();
+
+ addAttributeTypeAndValue( type, value );
+
+ upName = type + '=' + value;
+ normalizeString();
+ }
+
+ /**
+ * Constructs an Rdn from the given rdn. The contents of the rdn are simply
+ * copied into the newly created
+ *
+ * @param rdn The non-null Rdn to be copied.
+ */
+ public Rdn( Rdn rdn )
+ {
+ super();
+ nbAtavs = rdn.getNbAtavs();
+ this.string = new String( rdn.string );
+ this.upName = new String( rdn.getUpName() );
+
+ switch ( rdn.getNbAtavs() )
+ {
+ case 0 :
+ return;
+
+ case 1 :
+ this.atav = (AttributeTypeAndValue)rdn.atav.clone();
+ return;
+
+ default :
+ // We must duplicate the treeSet and the hashMap
+ Iterator iter = rdn.atavs.iterator();
+
+ atavs = new TreeSet();
+ atavTypes = new MultiHashMap();
+
+ while ( iter.hasNext() )
+ {
+ AttributeTypeAndValue currentAtav = (AttributeTypeAndValue)iter.next();
+ atavs.add( currentAtav.clone() );
+ atavTypes.put( currentAtav.getType(), currentAtav );
+ }
+ }
+ }
+
+ /**
+ * Transform the external representation of the current RDN
+ * to an internal normalized form where :
+ * - types are trimmed and lowercased
+ * - values are trimmed and lowercased
+ */
+ // WARNING : The protection level is left unspecified intentionnaly.
+ // We need this method to be visible from the DnParser class, but not
+ // from outside this package.
+ /* Unspecified protection */ void normalizeString()
+ {
+ switch ( nbAtavs )
+ {
+ case 0 :
+ // An empty RDN
+ string = "";
+ break;
+
+ case 1:
+ // We have a single AttributeTypeAndValue
+ // We will trim and lowercase type and value.
+ string = StringTools.lowerCase( StringTools.trim( atav.getType() ) ) +
+ '=' + StringTools.trim( atav.getValue() );
+ break;
+
+ default :
+ // We have more than one AttributeTypeAndValue
+ StringBuffer sb = new StringBuffer();
+
+ Iterator elems = atavs.iterator();
+ boolean isFirst = true;
+
+ while ( elems.hasNext() )
+ {
+ AttributeTypeAndValue ata = (AttributeTypeAndValue)elems.next();
+
+ if ( isFirst )
+ {
+ isFirst = false;
+ }
+ else
+ {
+ sb.append( '+' );
+ }
+
+
+ sb.append( ata.normalize() );
+ }
+
+ string = sb.toString();
+ break;
+ }
+ }
+
+ /**
+ * Add a AttributeTypeAndValue 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
+ */
+ // WARNING : The protection level is left unspecified intentionnaly.
+ // We need this method to be visible from the DnParser class, but not
+ // from outside this package.
+ /* Unspecified protection */ void addAttributeTypeAndValue( String type, String value) throws InvalidNameException
+ {
+ // First, let's normalize the type
+ String normalizedType = StringTools.lowerCase( StringTools.trim( type ) );
+ String normalizedValue = StringTools.trim( value );
+
+ switch ( nbAtavs )
+ {
+ case 0 :
+ // This is the first AttributeTypeAndValue. Just stores it.
+ atav = new AttributeTypeAndValue( normalizedType, normalizedValue );
+ nbAtavs = 1;
+ atavTypes.put( normalizedType, atav );
+ return;
+
+ case 1 :
+ // We already have an atav. We have to put it in the HashMap
+ // before adding a new one.
+ // First, create the HashMap,
+ atavs = new TreeSet();
+
+ // and store the existing AttributeTypeAndValue into it.
+ atavs.add( atav );
+
+ atav = null;
+
+ // Now, fall down to the commmon case
+ // NO BREAK !!!
+
+ default :
+ // add a new AttributeTypeAndValue
+ AttributeTypeAndValue newAtav = new AttributeTypeAndValue( normalizedType, normalizedValue );
+ atavs.add( newAtav );
+ atavTypes.put( normalizedType, newAtav );
+
+ nbAtavs++;
+ break;
+
+ }
+ }
+
+ /**
+ * Clear the RDN, removing all the AttributeTypeAndValues.
+ */
+ public void clear()
+ {
+ atav = null;
+ atavs = null;
+ atavTypes.clear();
+ nbAtavs = 0;
+ string = "";
+ upName = "";
+ }
+
+ /**
+ * Get the Value of the AttributeTypeAndValue 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 String getValue( String type ) throws InvalidNameException
+ {
+ // First, let's normalize the type
+ String normalizedType = StringTools.lowerCase( StringTools.trim( type ) );
+
+ switch ( nbAtavs )
+ {
+ case 0:
+ return "";
+
+ case 1:
+ if ( StringTools.equals( atav.getType(), normalizedType ) )
+ {
+ return atav.getValue();
+ }
+ else
+ {
+ return "";
+ }
+
+ default :
+ if ( atavTypes.containsKey( normalizedType ) )
+ {
+ Object obj = atavTypes.get( normalizedType );
+
+ if ( obj instanceof AttributeTypeAndValue )
+ {
+ return ((AttributeTypeAndValue)obj).getValue();
+ }
+ else if ( obj instanceof List )
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean isFirst = true;
+
+ for ( int i = 0; i < ((List)obj).size(); i++ )
+ {
+ AttributeTypeAndValue elem = (AttributeTypeAndValue)((List)obj).get( i );
+
+ if ( isFirst )
+ {
+ isFirst = false;
+ }
+ else
+ {
+ sb.append( ',' );
+ }
+
+ sb.append( elem.getValue() );
+ }
+
+ return sb.toString();
+ }
+ else
+ {
+ throw new InvalidNameException( "Bad object stored in the RDN" );
+ }
+ }
+ else
+ {
+ return "";
+ }
+ }
+ }
+
+ /**
+ * Get the AttributeTypeAndValue which type is given as an argument.
+ * If we have more than one value associated with the type, we will
+ * return only the first one.
+ *
+ * @param type The type of the NameArgument to be returned
+ * @return The AttributeTypeAndValue, of null if none is found.
+ */
+ public AttributeTypeAndValue getAttributeTypeAndValue( String type )
+ {
+ // First, let's normalize the type
+ String normalizedType = StringTools.lowerCase( StringTools.trim( type ) );
+
+ switch ( nbAtavs )
+ {
+ case 0:
+ return null;
+
+ case 1:
+ if ( atav.getType().equals( normalizedType ) )
+ {
+ return atav;
+ }
+ else
+ {
+ return null;
+ }
+
+ default :
+ if ( atavTypes.containsKey( normalizedType ) )
+ {
+ return (AttributeTypeAndValue)atavTypes.get( normalizedType );
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the components of this name as an enumeration
+ * of strings. The effect on the enumeration of updates to
+ * this name is undefined. If the name has zero components,
+ * an empty (non-null) enumeration is returned.
+ *
+ * @return an enumeration of the components of this name, each a string
+ */
+ public Iterator iterator()
+ {
+ if ( nbAtavs == 1 )
+ {
+ return new Iterator()
+ {
+ private boolean hasMoreElement = true;
+
+ public boolean hasNext()
+ {
+ return hasMoreElement ;
+ }
+
+ public Object next()
+ {
+ Object obj = atav ;
+ hasMoreElement = false ;
+ return obj ;
+ }
+
+ public void remove()
+ {
+
+ }
+ };
+ }
+ else
+ {
+ return atavs.iterator();
+ }
+ }
+
+ /**
+ * Clone the Rdn
+ */
+ public Object clone()
+ {
+ try
+ {
+ Rdn rdn = (Rdn)super.clone();
+
+ // The AttributeTypeAndValue is immutable. We won't clone it
+
+ switch ( rdn.getNbAtavs() )
+ {
+ case 0 :
+ break;
+
+ case 1 :
+ rdn.atav = (AttributeTypeAndValue)this.atav.clone();
+ rdn.atavTypes = new MultiHashMap();
+ rdn.atavTypes.put( rdn.atav.getType(), rdn.atav );
+ break;
+
+ default :
+ // We must duplicate the treeSet and the hashMap
+ rdn.atavTypes = new MultiHashMap();
+ rdn.atavs = new TreeSet();
+
+ Iterator iter = this.atavs.iterator();
+
+ while ( iter.hasNext() )
+ {
+ AttributeTypeAndValue currentAtav = (AttributeTypeAndValue)iter.next();
+ rdn.atavs.add( currentAtav.clone() );
+ rdn.atavTypes.put( currentAtav.getType(), currentAtav );
+ }
+
+ break;
+ }
+
+ return rdn;
+ }
+ catch ( CloneNotSupportedException cnse )
+ {
+ throw new Error( "Assertion failure" );
+ }
+ }
+
+ /**
+ * Compares two RDNs. They are equals if :
+ * - their have the same number of NC (AttributeTypeAndValue)
+ * - each ATAVs are equals
+ * - comparizon of type are done case insensitive
+ * - each value is equel, case sensitive
+ * - Order of ATAV is not important
+ *
+ * If the RDNs are not equals, a positive number is returned if the
+ * first RDN is greated, negative otherwise
+ * @param object
+ * @return 0 if both rdn are equals. -1 if the current RDN is inferior, 1 if
+ * teh current Rdn is superioir, UNDIFIED otherwise.
+ */
+ public int compareTo( Object object )
+ {
+ if ( object instanceof Rdn )
+ {
+ Rdn rdn = (Rdn)object;
+
+ if ( rdn == null )
+ {
+ return SUPERIOR;
+ }
+
+ if ( rdn.nbAtavs != nbAtavs )
+ {
+ // We don't have the same number of ATAVs. The Rdn which
+ // has the higher number of Atav is the one which is
+ // superior
+ return nbAtavs - rdn.nbAtavs;
+ }
+
+ switch ( nbAtavs )
+ {
+ case 0:
+ return EQUALS;
+
+ case 1:
+ return atav.compareTo( rdn.atav );
+
+ default :
+ // We have more than one value. We will
+ // go through all of them.
+ Iterator keys = atavs.iterator();
+
+ while ( keys.hasNext() )
+ {
+ AttributeTypeAndValue current = (AttributeTypeAndValue)keys.next();
+ String type = current.getType();
+
+ if ( rdn.atavTypes.containsKey( type ) )
+ {
+ List atavLocalList = (List)atavTypes.get( type );
+ List atavParamList = (List)rdn.atavTypes.get( type );
+
+ if ( atavLocalList.size() == 1 )
+ {
+ // We have only one ATAV
+ AttributeTypeAndValue atavLocal = (AttributeTypeAndValue)atavLocalList.get(0);
+ AttributeTypeAndValue atavParam = (AttributeTypeAndValue)atavParamList.get(0);
+
+ return atavLocal.compareTo( atavParam );
+ }
+ else
+ {
+ // We have to verify that each value of the first list are present in
+ // the second list
+ Iterator atavLocals = atavLocalList.iterator();
+
+ while ( atavLocals.hasNext() )
+ {
+ AttributeTypeAndValue atavLocal = (AttributeTypeAndValue)atavLocals.next();
+
+ Iterator atavParams = atavParamList.iterator();
+ boolean found = false;
+
+ while ( atavParams.hasNext() )
+ {
+ AttributeTypeAndValue atavParam = (AttributeTypeAndValue)atavParams.next();
+
+ if ( atavLocal.compareTo( atavParam ) == EQUALS )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ // The ATAV does not exist in the second RDN
+ return SUPERIOR;
+ }
+ }
+ }
+
+ return EQUALS;
+ }
+ else
+ {
+ // We can't find an atav in the rdn : the current one is superior
+ return SUPERIOR;
+ }
+ }
+
+ return EQUALS;
+ }
+ }
+ else
+ {
+ return object != null ? UNDEFINED : SUPERIOR;
+ }
+ }
+
+ /**
+ * Returns a String representation of the RDN
+ */
+ public String toString()
+ {
+ return string;
+ }
+
+ /**
+ * Returns a String representation of the RDN
+ */
+ public String getUpName()
+ {
+ return upName;
+ }
+
+ /**
+ * Set the User Provided Name
+ */
+ public void setUpName( String upName )
+ {
+ this.upName = upName;
+ }
+
+ /**
+ * @return Returns the nbAtavs.
+ */
+ public int getNbAtavs()
+ {
+ return nbAtavs;
+ }
+
+ /**
+ * Return the unique AttributeTypeAndValue, or the first one of we have more than one
+ * @return The first AttributeTypeAndValue of this RDN
+ */
+ public AttributeTypeAndValue getAtav()
+ {
+ switch ( nbAtavs )
+ {
+ case 0 :
+ return null;
+
+ case 1 :
+ return atav;
+
+ default :
+ return (AttributeTypeAndValue)atavs.first();
+ }
+ }
+
+ /**
+ * Return the type, or the first one of we have more than one (the lowest)
+ * @return The first type of this RDN
+ */
+ public String getType()
+ {
+ switch ( nbAtavs )
+ {
+ case 0 :
+ return null;
+
+ case 1 :
+ return atav.getType();
+
+ default :
+ return ((AttributeTypeAndValue)atavs.first()).getType();
+ }
+ }
+
+ /**
+ * Return the value, or the first one of we have more than one (the lowest)
+ * @return The first value of this RDN
+ */
+ public String getValue()
+ {
+ switch ( nbAtavs )
+ {
+ case 0 :
+ return null;
+
+ case 1 :
+ return atav.getValue();
+
+ default :
+ return ((AttributeTypeAndValue)atavs.first()).getValue();
+ }
+ }
+
+ /**
+ * Compares the specified Object with this Rdn for equality. Returns true
+ * if the given object is also a Rdn and the two Rdns represent the same
+ * attribute type and value mappings. The order of components in multi-valued
+ * Rdns is not significant.
+ * @param rdn Rdn to be compared for equality with this Rdn
+ * @return true if the specified object is equal to this Rdn
+ */
+ public boolean equals( Object rdn )
+ {
+ if ( this == rdn )
+ {
+ return true;
+ }
+
+ if ( !(rdn instanceof Rdn ) )
+ {
+ return false;
+ }
+
+ return compareTo( (Rdn)rdn ) == EQUALS;
+ }
+
+ /**
+ * Returns the hash code of this RDN. Two RDNs that are equal (according
+ * to the equals method) will have the same hash code.
+ *
+ * @returnAn int representing the hash code of this Rdn
+ */
+ public int hashcode()
+ {
+ // We compute the hashcode using the string, which is a
+ // normalized form of a rdn. unescapeValue
+ return 37*17 + string.hashCode();
+ }
+
+ /**
+ * Get the number of Attribute type and value of this Rdn
+ * @return The number of ATAVs in this Rdn
+ */
+ public int size()
+ {
+ return nbAtavs;
+ }
+
+ /**
+ * Transform the Rdn into an javax.naming.directory.Attributes
+ *
+ * @return An attributes structure containing all the ATAVs
+ */
+ public Attributes toAttributes()
+ {
+ Attributes attributes = new BasicAttributes();
+
+ Iterator types = atavTypes.keySet().iterator();
+
+ while ( types.hasNext() )
+ {
+ String type = (String)types.next();
+ List values = (List)atavTypes.get( type );
+
+ Attribute attribute = new BasicAttribute( type, true );
+
+ Iterator iterValues = values.iterator();
+
+ while ( iterValues.hasNext() )
+ {
+ AttributeTypeAndValue value = (AttributeTypeAndValue)iterValues.next();
+
+ attribute.add( value.getValue() );
+ }
+
+ attributes.put( attribute );
+ }
+
+ return attributes;
+ }
+
+ /**
+ * Unescape the given string according to RFC 2253
+ * If in <string> form, a LDAP string representation asserted value can
+ * be obtained by replacing (left-to-right, non-recursively) each <pair>
+ * appearing in the <string> as follows:
+ * replace <ESC><ESC> with <ESC>;
+ * replace <ESC><special> with <special>;
+ * replace <ESC><hexpair> with the octet indicated by the <hexpair>
+ *
+ * If in <hexstring> form, a BER representation can be obtained from
+ * converting each <hexpair> of the <hexstring> to the octet indicated by
+ * the <hexpair>
+ *
+ * @param value The value to be unescaped
+ * @return Returns a string value as a String, and a binary value as a byte array.
+ * @throws IllegalArgumentException - When an Illegal value is provided.
+ */
+ public static Object unescapeValue( String value ) throws IllegalArgumentException
+ {
+ if ( StringTools.isEmpty( value ) )
+ {
+ return "";
+ }
+
+ char[] chars = value.toCharArray();
+
+ if ( chars[0] == '#' )
+ {
+ if ( chars.length == 1 )
+ {
+ // The value is only containing a #
+ return StringTools.EMPTY_BYTES;
+ }
+
+ if ( ( chars.length % 2 ) != 1 )
+ {
+ throw new IllegalArgumentException("This value is not in hex form, we have an odd number of hex chars" );
+ }
+
+ // HexString form
+ byte[] hexValue = new byte[ (chars.length - 1)/2];
+ int pos = 0;
+
+ for ( int i = 1; i < chars.length; i += 2 )
+ {
+ if ( StringTools.isHex( chars, i ) && StringTools.isHex( chars, i + 1 ) )
+ {
+ hexValue[ pos++ ] = (byte)((StringTools.HEX_VALUE[ chars[i] ] << 4) + StringTools.HEX_VALUE[ chars[i + 1] ]);
+ }
+ else
+ {
+ throw new IllegalArgumentException("This value is not in hex form" );
+ }
+ }
+
+ return hexValue;
+ }
+ else
+ {
+ boolean escaped = false;
+ boolean isHex = false;
+ byte pair = -1;
+ int pos = 0;
+
+ byte[] bytes = new byte[ chars.length * 6];
+
+ for ( int i = 0; i < chars.length; i++ )
+ {
+ if ( escaped )
+ {
+ escaped = false;
+
+ switch ( chars[i] )
+ {
+ case '\\' :
+ case '"' :
+ case '+' :
+ case ',' :
+ case ';' :
+ case '<' :
+ case '>' :
+ case '#' :
+ case '=' :
+ case ' ' :
+ bytes[pos++] = (byte)chars[i];
+ break;
+
+ default :
+ if ( StringTools.isHex( chars, i ) )
+ {
+ isHex = true;
+ pair = ((byte)(StringTools.HEX_VALUE[ chars[i] ] << 4));
+ }
+ }
+ }
+ else
+ {
+ if ( isHex )
+ {
+ if ( StringTools.isHex( chars, i ) )
+ {
+ pair += (byte)StringTools.HEX_VALUE[ chars[i] ];
+ bytes[pos++] = pair;
+ }
+ }
+ else
+ {
+ switch ( chars[i] )
+ {
+ case '\\' :
+ escaped = true;
+ break;
+
+ // We must not have a special char
+ // Specials are : '"', '+', ',', ';', '<', '>', ' ', '#' and '='
+ case '"' :
+ case '+' :
+ case ',' :
+ case ';' :
+ case '<' :
+ case '>' :
+ case '#' :
+ case '=' :
+ case ' ' :
+ throw new IllegalArgumentException( "Unescaped special characters are not allowed" );
+
+ default :
+ byte[] result = StringTools.charToBytes( chars[i] );
+
+ for ( int j = 0; j < result.length; j++ )
+ {
+ bytes[pos++] = result[j];
+ }
+ }
+ }
+ }
+ }
+
+ return StringTools.utf8ToString( bytes, pos );
+ }
+ }
+
+ /**
+ * Transform a value in a String, accordingly to RFC 2253
+ *
+ * @param attrValue The attribute value to be escaped
+ * @return The escaped string value.
+ */
+ public static String escapeValue( Object attrValue)
+ {
+ if (StringTools.isEmpty( (byte[])attrValue) )
+ {
+ return "";
+ }
+
+ String value = StringTools.utf8ToString( (byte[])attrValue );
+
+ char[] chars = value.toCharArray();
+ char[] newChars = new char[chars.length * 3];
+ int pos = 0;
+
+ for ( int i = 0; i < chars.length; i++ )
+ {
+ switch ( chars[i] )
+ {
+ case ' ' :
+ case '"' :
+ case '#' :
+ case '+' :
+ case ',' :
+ case ';' :
+ case '=' :
+ case '<' :
+ case '>' :
+ case '\\' :
+ newChars[pos++] = '\\';
+ newChars[pos++] = chars[i];
+ break;
+
+ case 0x7F :
+ newChars[pos++] = '\\';
+ newChars[pos++] = '7';
+ newChars[pos++] = 'F';
+ break;
+
+ case 0x00 : case 0x01 : case 0x02 : case 0x03 :
+ case 0x04 : case 0x05 : case 0x06 : case 0x07 :
+ case 0x08 : case 0x09 : case 0x0A : case 0x0B :
+ case 0x0C : case 0x0D : case 0x0E : case 0x0F :
+ newChars[pos++] = '\\';
+ newChars[pos++] = '0';
+ newChars[pos++] = StringTools.dumpHex( (byte)(chars[i] & 0x0F) );
+ break;
+
+ case 0x10 : case 0x11 : case 0x12 : case 0x13 :
+ case 0x14 : case 0x15 : case 0x16 : case 0x17 :
+ case 0x18 : case 0x19 : case 0x1A : case 0x1B :
+ case 0x1C : case 0x1D : case 0x1E : case 0x1F :
+ newChars[pos++] = '\\';
+ newChars[pos++] = '1';
+ newChars[pos++] = StringTools.dumpHex( (byte)(chars[i] & 0x0F) );
+ break;
+
+
+ default :
+ newChars[pos++] = chars[i];
+
+ }
+ }
+
+ return new String( newChars, 0, pos);
+ }
+}
Propchange: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/Rdn.java
------------------------------------------------------------------------------
svn:executable = *
Added: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/RdnParser.java
URL: http://svn.apache.org/viewcvs/directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/RdnParser.java?rev=366127&view=auto
==============================================================================
--- directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/RdnParser.java (added)
+++ directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/RdnParser.java Thu Jan 5 01:02:51 2006
@@ -0,0 +1,615 @@
+/*
+ * 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.ldap.common.name;
+
+import javax.naming.InvalidNameException;
+
+import org.apache.ldap.common.util.DnUtils;
+import org.apache.ldap.common.util.StringTools;
+
+/**
+ * This class parse the name-component part or the following BNF grammar (as of RFC2253, par. 3,
+ * and RFC1779, fig. 1) : <br>
+ * - <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> <br>
+ * - <attributeTypeAndValues> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <attributeTypeAndValues> | e <br>
+ * - <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits> <oids> | [0-9] <digits> <oids> <br>
+ * - <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-' <keychars> | e <br>
+ * - <oidPrefix> ::= 'OID.' | 'oid.' | e <br>
+ * - <oids> ::= '.' [0-9] <digits> <oids> | e <br>
+ * - <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ * - <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | e <br>
+ * - <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | e <br>
+ * - <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br>
+ * - <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br>
+ * - <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br>
+ * - <digits> ::= [0-9] <digits> | e <br>
+ * - <stringchar> ::= [0x00-0xFF] - [,=+<>#;\"\n\r] <br>
+ * - <quotechar> ::= [0x00-0xFF] - [\"] <br>
+ * - <separator> ::= ',' | ';' <br>
+ * - <spaces> ::= ' ' <spaces> | 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 RdnParser
+{
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <oidValue> ::= [0-9] <digits> <oids>
+ * </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 ( StringTools.isDigit( chars, pos ) == false )
+ {
+ // Nope... An error
+ return DnUtils.PARSING_ERROR;
+ }
+ else
+ {
+ // Let's process an oid
+ pos++;
+
+ while ( StringTools.isDigit( chars, pos ) )
+ {
+ pos++;
+ }
+
+ // <oids> ::= '.' [0-9] <digits> <oids> | e
+ if ( StringTools.isCharASCII( chars, pos, '.' ) == false )
+ {
+ return pos;
+ }
+ else
+ {
+ do
+ {
+ pos++;
+
+ if ( StringTools.isDigit( chars, pos ) == false )
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+ else
+ {
+ pos++;
+
+ while ( StringTools.isDigit( chars, pos ) )
+ {
+ pos++;
+ }
+ }
+ }
+ while ( StringTools.isCharASCII( chars, pos, '.' ) );
+
+ return pos;
+ }
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <oidPrefix> ::= '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 ( ( StringTools.areEquals( chars, pos, DnUtils.OID_LOWER ) == DnUtils.PARSING_ERROR ) &&
+ ( StringTools.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>
+ * <attributType> ::= [a-zA-Z] <keychars> |
+ * <oidPrefix> [0-9] <digits> <oids> |
+ * [0-9] <digits> <oids>
+ * </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 ( StringTools.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 ( StringTools.isAlphaDigitMinus( chars, pos ) )
+ {
+ pos++;
+ }
+
+ return pos;
+ }
+ }
+ else
+ {
+
+ // An oid
+ // <attributType> ::= [0-9] <digits> <oids>
+ return parseOidValue(chars, pos);
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ * <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | | e <br>
+ * <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | 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 ( StringTools.isCharASCII( chars, pos, '#' ) )
+ {
+ pos++;
+
+ // <attributeValue> ::= '#' <hexstring>
+ if ( ( pos = DnUtils.parseHexString( chars, pos ) ) == DnUtils.PARSING_ERROR )
+ {
+
+ return DnUtils.PARSING_ERROR;
+ }
+
+ return StringTools.trimLeft( chars, pos );
+ }
+ else if ( StringTools.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 ( StringTools.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 ( StringTools.isCharASCII( chars, pos, '"' ) )
+ {
+ pos++;
+
+ return StringTools.trimLeft( chars, pos );
+ }
+ else
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+ }
+ else
+ {
+ while ( true )
+ {
+ if ( StringTools.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 ( StringTools.isCharASCII( chars, pos, ' ') )
+ {
+ pos = StringTools.trimLeft( chars, pos );
+
+ if ( ( DnUtils.isStringChar( chars, pos ) == DnUtils.PARSING_ERROR ) &&
+ ( StringTools.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>
+ * <nameComponents> ::= <spaces> '+' <spaces> <attributeType> <spaces> '=' <spaces> <attributeValue> <nameComponents> | 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 parseNameComponents( char[] chars, int pos, Rdn rdn ) throws InvalidNameException
+ {
+ int newPos = 0;
+ String type = null;
+ String value = null;
+
+ while ( true )
+ {
+ pos = StringTools.trimLeft( chars, pos );
+
+ if ( StringTools.isCharASCII( chars, pos, '+' ) )
+ {
+ pos++;
+ }
+ else
+ {
+ // <attributeTypeAndValues> ::= e
+ return pos;
+ }
+
+ pos = StringTools.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 = StringTools.trimLeft( chars, newPos );
+
+ if ( StringTools.isCharASCII( chars, pos, '=' ) )
+ {
+ pos++;
+ }
+ else
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+
+ pos = StringTools.trimLeft( chars, pos );
+
+ newPos = parseAttributeValue( chars, pos );
+
+ if ( newPos != DnUtils.PARSING_ERROR )
+ {
+ if ( rdn != null )
+ {
+ newPos = StringTools.trimRight( chars, newPos );
+ value = new String( chars, pos, newPos - pos );
+
+ rdn.addAttributeTypeAndValue( type, value );
+ }
+ }
+
+ pos = newPos;
+ }
+ }
+
+ /**
+ * Parse this rule : <br>
+ * <p>
+ * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring> |'"' <quotechar-or-pairs> '"' <br>
+ * <pairs-or-strings> ::= '\' <pairchar> <pairs-or-strings> | <stringchar> <pairs-or-strings> | | e <br>
+ * <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\' <pairchar> <quotechar-or-pairs> | 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
+ */
+ public static int unescapeValue( String value ) throws IllegalArgumentException
+ {
+ char[] chars = value.toCharArray();
+ int pos = 0;
+
+ if ( StringTools.isCharASCII( chars, pos, '#' ) )
+ {
+ pos++;
+
+ // <attributeValue> ::= '#' <hexstring>
+ if ( ( pos = DnUtils.parseHexString( chars, pos ) ) == DnUtils.PARSING_ERROR )
+ {
+
+ throw new IllegalArgumentException();
+ }
+
+ return StringTools.trimLeft( chars, pos );
+ }
+ else if ( StringTools.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 ( StringTools.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 ( StringTools.isCharASCII( chars, pos, '"' ) )
+ {
+ pos++;
+
+ return StringTools.trimLeft( chars, pos );
+ }
+ else
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+ }
+ else
+ {
+ while ( true )
+ {
+ if ( StringTools.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 ( StringTools.isCharASCII( chars, pos, ' ') )
+ {
+ pos = StringTools.trimLeft( chars, pos );
+
+ if ( ( DnUtils.isStringChar( chars, pos ) == DnUtils.PARSING_ERROR ) &&
+ ( StringTools.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 a NameComponent : <br>
+ * <p>
+ * <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <nameComponents>
+ * </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 parse( char[] chars, int pos, Rdn rdn ) throws InvalidNameException
+ {
+ int newPos = 0;
+ String type = null;
+ String value = null;
+ int start = pos;
+
+ pos = StringTools.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 = StringTools.trimLeft( chars, newPos );
+
+ if ( StringTools.isCharASCII( chars, pos, '=' ) == false )
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+ else
+ {
+ pos++;
+ }
+
+ pos = StringTools.trimLeft( chars, pos );
+
+ if ( ( newPos = parseAttributeValue( chars, pos ) ) == DnUtils.PARSING_ERROR )
+ {
+ return DnUtils.PARSING_ERROR;
+ }
+
+ if ( rdn != null )
+ {
+ newPos = StringTools.trimRight( chars, newPos );
+ value = new String( chars, pos, newPos - pos );
+
+ rdn.addAttributeTypeAndValue( type, value );
+ }
+
+ int end = parseNameComponents( chars, newPos, rdn );
+ rdn.setUpName( new String( chars, start, end - start ) );
+ rdn.normalizeString();
+ return end;
+ }
+
+ /**
+ * Parse a NameComponent : <br>
+ * <p>
+ * <name-component> ::= <attributeType> <spaces> '=' <spaces> <attributeValue> <nameComponents>
+ * </p>
+ *
+ * @param string The buffer to parse
+ * @param rdn The RDN to fill. Beware that if the RDN is not empty, the new AttributeTypeAndValue will be added.
+ */
+ public static void parse( String string, Rdn rdn ) throws InvalidNameException
+ {
+ parse( string.toCharArray(), 0, rdn );
+ rdn.normalizeString();
+ }
+}
Propchange: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/name/RdnParser.java
------------------------------------------------------------------------------
svn:executable = *
Added: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/schema/OidNormalizer.java
URL: http://svn.apache.org/viewcvs/directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/schema/OidNormalizer.java?rev=366127&view=auto
==============================================================================
--- directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/schema/OidNormalizer.java (added)
+++ directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/schema/OidNormalizer.java Thu Jan 5 01:02:51 2006
@@ -0,0 +1,92 @@
+/*
+ * 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.ldap.common.schema;
+
+/**
+ * The OidNomalizer class contains a couple : and OID with its Normalizer
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class OidNormalizer {
+ /** The oid */
+ private String oid;
+
+ /** The normalizer to be used with this OID */
+ private Normalizer normalizer;
+
+ /**
+ * A constructor which accept two parameters
+ * @param oid The oid
+ * @param normalizer The associated normalizer
+ */
+ public OidNormalizer( String oid, Normalizer normalizer )
+ {
+ this.oid = oid;
+ this.normalizer = normalizer;
+ }
+
+ /**
+ * A copy constructor.
+ * @param oidNormalizer The OidNormalizer to copy from
+ */
+ public OidNormalizer( OidNormalizer oidNormalizer )
+ {
+ oid = oidNormalizer.oid;
+ normalizer = oidNormalizer.normalizer;
+ }
+
+ /**
+ * Get the normalizer
+ * @return The normalizer associated to the current OID
+ */
+ public Normalizer getNormalizer() {
+ return normalizer;
+ }
+
+ /**
+ * Set the normalizer
+ * @param The normalizer to be associated to the current OID
+ */
+ public void setNormalizer(Normalizer normalizer) {
+ this.normalizer = normalizer;
+ }
+
+ /**
+ * Get the current OID
+ * @return The current OID
+ */
+ public String getOid() {
+ return oid;
+ }
+
+ /**
+ * Set the current OID
+ * @param The current OID
+ */
+ public void setOid(String oid) {
+ this.oid = oid;
+ }
+
+ /**
+ * Return a String representation of this class
+ */
+ public String toString()
+ {
+ return "OidNormalizer : { " + oid + ", " + normalizer.toString() + "}";
+ }
+}
Propchange: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/schema/OidNormalizer.java
------------------------------------------------------------------------------
svn:executable = *
Added: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/util/DnUtils.java
URL: http://svn.apache.org/viewcvs/directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/util/DnUtils.java?rev=366127&view=auto
==============================================================================
--- directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/util/DnUtils.java (added)
+++ directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/util/DnUtils.java Thu Jan 5 01:02:51 2006
@@ -0,0 +1,616 @@
+/*
+ * 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.ldap.common.util;
+
+/**
+ * Utility class used by the LdapDN Parser.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public class DnUtils
+{
+ //~ Static fields/initializers -----------------------------------------------------------------
+
+ /** <safe-init-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F] */
+ private static final boolean[] SAFE_INIT_CHAR =
+ {
+ false, true, true, true, true, true, true, true, true, true, false, true, true, false,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, false, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, false,
+ true, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true
+ };
+
+ /** <safe-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F] */
+ private static final boolean[] SAFE_CHAR =
+ {
+ false, true, true, true, true, true, true, true, true, true, false, true, true, false,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true
+ };
+
+ /** <base64-char> ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-0x5A] | [0x61-0x7A] */
+ private static final boolean[] BASE64_CHAR =
+ {
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, true, false, false, false, true, true, true, true, true, true,
+ true, true, true, true, true, false, false, false, true, false, false, false, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, false, false, false, false, false,
+ false, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, false, false,
+ false, false, false
+ };
+
+ /** '"' | '#' | '+' | ',' | [0-9] | ';' | '<' | '=' | '>' | [A-F] | '\' | [a-f]
+ * 0x22 | 0x23 | 0x2B | 0x2C | [0x30-0x39] | 0x3B | 0x3C | 0x3D | 0x3E | [0x41-0x46] | 0x5C | [0x61-0x66] */
+ private static final boolean[] PAIR_CHAR =
+ {
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, true, true, false, false, false,
+ false, false, false, false, true, true, false, false, false, true, true, true, true, true,
+ true, true, true, true, true, false, true, true, true, true, false, false, true, true,
+ true, true, true, true, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, true, true, true, true, true, true, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false
+ };
+
+ /** "oid." static */
+ public static final char[] OID_LOWER = new char[] { 'o', 'i', 'd', '.' };
+
+ /** "OID." static */
+ public static final char[] OID_UPPER = new char[] { 'O', 'I', 'D', '.' };
+
+ /** "oid." static */
+ public static final byte[] OID_LOWER_BYTES = new byte[] { 'o', 'i', 'd', '.' };
+
+ /** "OID." static */
+ public static final byte[] OID_UPPER_BYTES = new byte[] { 'O', 'I', 'D', '.' };
+
+ /** A value if we got an error while parsing */
+ public static final int PARSING_ERROR = -1;
+
+ /** If an hex pair contains only one char, this value is returned */
+ public static final int BAD_HEX_PAIR = -2;
+
+ /** A constant representing one char length */
+ public static final int ONE_CHAR = 1;
+
+ /** A constant representing two chars length */
+ public static final int TWO_CHARS = 2;
+
+ /** A constant representing one byte length */
+ public static final int ONE_BYTE = 1;
+
+ /** A constant representing two bytes length */
+ public static final int TWO_BYTES = 2;
+
+ //~ Methods ------------------------------------------------------------------------------------
+
+ /**
+ * Walk the buffer while characters are Safe String characters :
+ * <safe-string> ::= <safe-init-char> <safe-chars>
+ * <safe-init-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F]
+ * <safe-chars> ::= <safe-char> <safe-chars> |
+ * <safe-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not a Safe Char
+ */
+ public static int parseSafeString( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( SAFE_INIT_CHAR[c] == false ) )
+ {
+ return -1;
+ }
+
+ index++;
+
+ while ( index < byteArray.length )
+ {
+ c = byteArray[index];
+
+ if ( ( c > 127 ) || ( SAFE_CHAR[c] == false ) )
+ {
+ break;
+ }
+
+ index++;
+ }
+
+ return index;
+ }
+ }
+
+ /**
+ * Walk the buffer while characters are Alpha characters :
+ * <alpha> ::= [0x41-0x5A] | [0x61-0x7A]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not an Alpha Char
+ */
+ public static int parseAlphaASCII( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index++];
+
+ if ( ( c > 127 ) || ( StringTools.ALPHA[c] == false ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return index;
+ }
+ }
+ }
+
+ /**
+ * Walk the buffer while characters are Alpha characters :
+ * <alpha> ::= [0x41-0x5A] | [0x61-0x7A]
+ *
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not an Alpha Char
+ */
+ public static int parseAlphaASCII( char[] charArray, int index )
+ {
+ if ( ( charArray == null ) || ( charArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= charArray.length ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ char c = charArray[index++];
+
+ if ( ( c > 127 ) || ( StringTools.ALPHA[c] == false ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ return index;
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Pair Char
+ * <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Pair Char
+ */
+ public static boolean isPairChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return false;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( PAIR_CHAR[c] == false ) )
+ {
+ return false;
+ }
+ else
+ {
+ if ( StringTools.isHex( byteArray, index++ ) )
+ {
+ return StringTools.isHex( byteArray, index );
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Pair Char
+ * <pairchar> ::= ',' | '=' | '+' | '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F]
+ *
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Pair Char
+ */
+ public static boolean isPairChar( char[] charArray, int index )
+ {
+ if ( ( charArray == null ) || ( charArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= charArray.length ) )
+ {
+ return false;
+ }
+ else
+ {
+ char c = charArray[index];
+
+ if ( ( c > 127 ) || ( PAIR_CHAR[c] == false ) )
+ {
+ return false;
+ }
+ else
+ {
+ if ( StringTools.isHex( charArray, index++ ) )
+ {
+ return StringTools.isHex( charArray, index );
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a String Char.
+ * Chars are Unicode, not ASCII.
+ * <stringchar> ::= [0x00-0xFFFF] - [,=+<>#;\"\n\r]
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The current char if it is a String Char, or '#' (this is
+ * simpler than throwing an exception :)
+ */
+ public static int isStringChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c == 0x0A ) ||
+ ( c == 0x0D ) ||
+ ( c == '"' ) ||
+ ( c == '#' ) ||
+ ( c == '+' ) ||
+ ( c == ',' ) ||
+ ( c == ';' ) ||
+ ( c == '<' ) ||
+ ( c == '=' ) ||
+ ( c == '>' ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return StringTools.countBytesPerChar(byteArray, index);
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a String Char.
+ * Chars are Unicode, not ASCII.
+ * <stringchar> ::= [0x00-0xFFFF] - [,=+<>#;\"\n\r]
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The current char if it is a String Char, or '#' (this is
+ * simpler than throwing an exception :)
+ */
+ public static int isStringChar( char[] charArray, int index )
+ {
+ if ( ( charArray == null ) || ( charArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= charArray.length ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ char c = charArray[index];
+
+ if ( ( c == 0x0A ) ||
+ ( c == 0x0D ) ||
+ ( c == '"' ) ||
+ ( c == '#' ) ||
+ ( c == '+' ) ||
+ ( c == ',' ) ||
+ ( c == ';' ) ||
+ ( c == '<' ) ||
+ ( c == '=' ) ||
+ ( c == '>' ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ return ONE_CHAR;
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Quote Char
+ * We are testing Unicode chars
+ * <quotechar> ::= [0x00-0xFFFF] - [\"]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Quote Char
+ */
+ public static int isQuoteChar( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c == '\\' ) || ( c == '"' ) )
+ {
+ return -1;
+ }
+ else
+ {
+ return StringTools.countBytesPerChar(byteArray, index);
+ }
+ }
+ }
+
+ /**
+ * Check if the current character is a Quote Char
+ * We are testing Unicode chars
+ * <quotechar> ::= [0x00-0xFFFF] - [\"]
+ *
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return <code>true</code> if the current character is a Quote Char
+ */
+ public static int isQuoteChar( char[] charArray, int index )
+ {
+ if ( ( charArray == null ) || ( charArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= charArray.length ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ char c = charArray[index];
+
+ if ( ( c == '\\' ) || ( c == '"' ) )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ return ONE_CHAR;
+ }
+ }
+ }
+
+ /**
+ * Parse an hex pair
+ * <hexpair> ::= <hex> <hex>
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The new position, -1 if the buffer does not contain an HexPair, -2 if the
+ * buffer contains an hex byte but not two.
+ */
+ public static int parseHexPair( byte[] byteArray, int index )
+ {
+ if ( StringTools.isHex( byteArray, index ) )
+ {
+ if ( StringTools.isHex( byteArray, index + 1 ) )
+ {
+ return index + 2;
+ }
+ else
+ {
+ return -2;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * Parse an hex pair
+ * <hexpair> ::= <hex> <hex>
+ *
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The new position, -1 if the buffer does not contain an HexPair, -2 if the
+ * buffer contains an hex byte but not two.
+ */
+ public static int parseHexPair( char[] charArray, int index )
+ {
+ if ( StringTools.isHex( charArray, index ) )
+ {
+ if ( StringTools.isHex( charArray, index + 1 ) )
+ {
+ return index + TWO_CHARS;
+ }
+ else
+ {
+ return BAD_HEX_PAIR;
+ }
+ }
+ else
+ {
+ return PARSING_ERROR;
+ }
+ }
+
+ /**
+ * Parse an hex string, which is a list of hex pairs
+ * <hexstring> ::= <hexpair> <hexpairs>
+ * <hexpairs> ::= <hexpair> <hexpairs> | e
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return Return the first position which is not an hex pair, or -1 if there is no
+ * hexpair at the beginning or if an hexpair is invalid (if we have only one hex instead of 2)
+ */
+ public static int parseHexString( byte[] byteArray, int index )
+ {
+ int result = parseHexPair( byteArray, index );
+
+ if ( result < 0 )
+ {
+ return -1;
+ }
+ else
+ {
+ index += 2;
+ }
+
+ while ( ( result = parseHexPair( byteArray, index ) ) >= 0 )
+ {
+ index += 2;
+ }
+
+ return ( ( result == -2 ) ? -1 : index );
+ }
+
+ /**
+ * Parse an hex string, which is a list of hex pairs
+ * <hexstring> ::= <hexpair> <hexpairs>
+ * <hexpairs> ::= <hexpair> <hexpairs> | e
+ *
+ * @param charArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return Return the first position which is not an hex pair, or -1 if there is no
+ * hexpair at the beginning or if an hexpair is invalid (if we have only one hex instead of 2)
+ */
+ public static int parseHexString( char[] charArray, int index )
+ {
+ int result = parseHexPair( charArray, index );
+
+ if ( result < 0 )
+ {
+ return PARSING_ERROR;
+ }
+ else
+ {
+ index += TWO_CHARS;
+ }
+
+ while ( ( result = parseHexPair( charArray, index ) ) >= 0 )
+ {
+ index += TWO_CHARS;
+ }
+
+ return ( ( result == BAD_HEX_PAIR ) ? PARSING_ERROR : index );
+ }
+
+ /**
+ * Walk the buffer while characters are Base64 characters :
+ * <base64-string> ::= <base64-char> <base64-chars>
+ * <base64-chars> ::= <base64-char> <base64-chars> |
+ * <base64-char> ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-0x5A] | [0x61-0x7A]
+ *
+ * @param byteArray The buffer which contains the data
+ * @param index Current position in the buffer
+ *
+ * @return The position of the first character which is not a Base64 Char
+ */
+ public static int parseBase64String( byte[] byteArray, int index )
+ {
+ if ( ( byteArray == null ) || ( byteArray.length == 0 ) || ( index < 0 ) ||
+ ( index >= byteArray.length ) )
+ {
+ return -1;
+ }
+ else
+ {
+ byte c = byteArray[index];
+
+ if ( ( c > 127 ) || ( BASE64_CHAR[c] == false ) )
+ {
+ return -1;
+ }
+
+ index++;
+
+ while ( index < byteArray.length )
+ {
+ c = byteArray[index];
+
+ if ( ( c > 127 ) || ( BASE64_CHAR[c] == false ) )
+ {
+ break;
+ }
+
+ index++;
+ }
+
+ return index;
+ }
+ }
+
+}
Propchange: directory/branches/DN-refactoring/ldap-common/src/main/java/org/apache/ldap/common/util/DnUtils.java
------------------------------------------------------------------------------
svn:executable = *