Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 8186 invoked from network); 27 Aug 2006 18:27:30 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 27 Aug 2006 18:27:30 -0000 Received: (qmail 27333 invoked by uid 500); 27 Aug 2006 18:27:30 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 27306 invoked by uid 500); 27 Aug 2006 18:27:30 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 27289 invoked by uid 99); 27 Aug 2006 18:27:30 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Aug 2006 11:27:30 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Aug 2006 11:27:25 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id D021A1A981D; Sun, 27 Aug 2006 11:26:41 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r437433 [2/17] - in /incubator/harmony/enhanced/classlib/trunk/modules/jndi/src: main/java/javax/naming/ main/java/javax/naming/directory/ main/java/javax/naming/event/ main/java/javax/naming/ldap/ main/java/javax/naming/spi/ main/java/org/... Date: Sun, 27 Aug 2006 18:26:28 -0000 To: harmony-commits@incubator.apache.org From: ndbeyer@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060827182641.D021A1A981D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Modified: incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/CompoundName.java URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/CompoundName.java?rev=437433&r1=437432&r2=437433&view=diff ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/CompoundName.java (original) +++ incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/CompoundName.java Sun Aug 27 11:26:20 2006 @@ -1,1044 +1,1044 @@ -/* Copyright 2004 The Apache Software Foundation or its licensors, as applicable - * - * 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 javax.naming; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Properties; -import java.util.Vector; - -/** - * A CompoundName is a series of string elements, and it represents - * a name in a naming service within a single namespace. Typically these - * names have a structure which is hierarchical. - *

- * A CompoundName has a sequence of zero or more elements delimited - * by the char specified in the property "jndi.syntax.separator". This property - * is required except when the direction of the name is "flat" - * (see jndi.syntax.direction). The property "jndi.syntax.separator2" allows for - * the specification of an additional separator. A separator string will be - * treated as normal characters if it is preceded by the escape string or is - * within quotes.

- *

- * The property "jndi.syntax.direction" specifies the direction in which the name - * is read. Permitted values are "right_to_left", "left_to_right" and "flat". A - * flat name does not have a hierarchical structure. If this property is not - * specified then the default is "flat". If this property is specified with an - * invalid value then an IllegalArgumentException should be raised.

- *

- * Each element can be accessed using its position. The first element is at - * position 0. The direction of the name is important. When direction is - * "left_to_right" then the leftmost element is at position 0. Conversely when - * the direction is "right_to_left" then the rightmost element is at position 0.

- *

- * There are other properties which affect the syntax of a CompoundName. - * The following properties are all optional: - *

    - *
  • - * jndi.syntax.escape - Escape sequence,The escape sequence is used to escape - * a quote, separator or escape. When preceded itself by - * the escape sequence it is treated as ordinary characters. - * When it is followed by chars which are not quote or - * separator strings then it is treated as ordinary characters
  • - *
  • - * jndi.syntax.beginquote - Used as start of quoted string (Defaults to endquote)
  • - *
  • - * jndi.syntax.endquote - Used as end of quoted string (Defaults to beginquote)
  • - *
  • - * jndi.syntax.beginquote2 - Additionally used as start of quoted string - * (Defaults to endquote2)
  • - *
  • - * jndi.syntax.endquote2 - Additionally used as end of quoted string - * (Defaults to beginquote2)
  • - *
- *

- * When a non-escaped quote appears at the start of an element it must be matched at the - * end. That element can then be said to be quoted. When an escape sequence appears - * within a quoted element then it is treated as normal characters unless it precedes - * an occurrence of the quote in which case it is assumed that the quoted element - * contains a quote which is escaped.

- *

- * If the element does not start with a quote, then any quote strings within that - * element are just normal characters.

- *

- *

    - *
  • - * jndi.syntax.ignorecase - If 'true' then ignore case when name elements are compared. - * If false or not set then case is important.
  • - *
  • - * jndi.syntax.trimblanks - If 'true' then ignore leading & trailing blanks when name elements are compared. - * If false or not set then blanks are important.
  • - *

- *

- * These 2 properties relate to names where the syntax includes attribute/content - * pairs. - *

    - *
  • jndi.syntax.separator.ava
  • - *
  • jndi.syntax.separator.typeval
  • - *
- * For example the LDAP name, "CN=Mandy Jennings, O=Apache, C=UK". - * In this example the pair separator jndi.syntax.separator.ava is ',', and the - * character that separates pairs jndi.syntax.separator.typeval is '='. See - * RFC1779 for LDAP naming conventions.

- *

- * The jndi.syntax.separator.ava is not used when manipulating CompoundName. - * The jndi.syntax.separator is still used to separate elements.

- *

- * The CompoundName needs to be aware of the jndi.syntax.separator.typeval - * in case of the instance where a quoted string is used to provide the content - * of a pair.

- *

- * Consider the string "CN=$Mandy Jennings, O=Apache, C=UK" with - *

    - *
  • - * jndi.syntax.direction set to "right_to_left"
  • - *
  • - * jndi.syntax.separator set to ","
  • - *
  • - * jndi.syntax.separator.typeval set to "="
  • - *
- * When no jndi.syntax.beginquote is set then this creates a valid CompoundName - * with 3 elements.

- *

- * If jndi.syntax.beginquote is then set to "$" the name becomes invalid as the - * content part of the pair CN=$Mandy Jennings has a mismatched quote.

- *

- * The string "CN=$Mandy Jennings$, O=Apache, C=UK" would be fine as the $ quotes - * round Mandy Jennings now balance.

- *

- * A CompoundName may be empty. An empty CompoundName has no elements. - * Elements may also be empty.

- * - *
- * Some Examples:
- * ==============
- * 
- * Consider the following compound name from the file system namespace:
- *     "home/jenningm-abc/.profile" 
- * 
- * jndi.syntax.separator is set to '/' as in the UNIX filesystem.
- * This name has 3 elements:
- *     home jenningm-abc and .profile
- * The direction should be left_to_right as in the UNIX filesystem
- * The element at position 0 would be home.
- * 
- * 
- * Consider if jndi.syntax.separator had been set to '-' then this name 
- * would have 2 elements:
- *     home/jenningm and abc/.profile
- * If the direction was right_to_left then the element at position 0
- * would be abc/.profile.
- * 
- * Consider the name "<ab<cd>ef>" where jndi.syntax.beginquote is <
- * and jndi.syntax.endquote is >. This will give rise to an 
- * InvalidNameException because a close quote was encountered before
- * the end of an element. The same is also true for "<abcd>ef>".
- * If the name was "ab<cd>ef" then this would be valid and there would
- * be one element ab<cd>ef.
- * However if the name was "<abcdef>" there would be one element abcdef.
- * 
- * An empty CompoundName is the name "" and has no elements.
- * 
- * When jndi.syntax.beginquote is set to " and beginquote2 is set to '
- * the behaviour is simliar to CompositeName - 
- * The name "\"abcd" gives an InvalidNameException as there is no closing quote.
- * The name "'\"abcd'" gives one element of value "abcd.
- * The name "\\abcd" gives one element of value \abcd. 
- *  
- * Assuming:
- *     jndi.syntax.separator is "/"
- *     jndi.syntax.direction is "left_to_right"
- * then
- *     "" is empty. It has no elements.
- *     "/" has one empty element.
- *     "//" has 2 empty elements.
- *     "/a/" has 3 elements the middle one is set to a.
- *     "///" has 3 empty elements.
- *     "//a/" has 4 elements, the last but one is set to a.
- * 
- *
- * Assuming the only properties set are: 
- *     jndi.syntax.separator is "/"
- *     jndi.syntax.direction is "left_to_right"
- * then the String
- *     "\"" has one element with the value "
- *     "\\\"" has one element with the value \"
- *     "\\\"'" has one element with the value \"'
- * 
- * Assuming the only properties set are:
- *     jndi.syntax.separator is "/"
- *     jndi.syntax.direction is "left_to_right"
- *     jndi.syntax.beginquote is "\""
- * 
- * then the String
- *     "\"" is invalid because of no closing quote
- *     "\\\"" has one element with the value \"
- *     "\\\"'" has one element with the value \"'
- * 
- * 
- * Assuming the only properties set are:
- *     jndi.syntax.separator is "/"
- *     jndi.syntax.direction is "left_to_right"
- *     jndi.syntax.beginquote is "\""
- *     jndi.syntax.beginquote2 is "\'"
- * then the String
- *     "\"" is invalid because of no closing quote
- *     "\\\"" has one element with the value \"
- *     "\\\"'" has one element with the value \"'
- *     "'\\" is invalid because of no closing quote
- * 
- * - * - */ - -public class CompoundName implements Name { - - /* - * ------------------------------------------------------------------- - * Constants - * ------------------------------------------------------------------- - */ - - /* - *============= - *Serialization - *============= - * - *Note: For serialization purposes, the specified serialVersionUID must be used. - *This class does not have serializable fields specified. Instead the readObject - *and writeObject methods are overidden. - */ - static final long serialVersionUID = 3513100557083972036L; // J2SE 1.4.2 - - //const for property key - private static final String SEPARATOR = "jndi.syntax.separator"; //$NON-NLS-1$ - - private static final String SEPARATOR_AVA = "jndi.syntax.separator.ava"; //$NON-NLS-1$ - - private static final String SEPARATOR_TYPEVAL = "jndi.syntax.separator.typeval"; //$NON-NLS-1$ - - private static final String ESCAPE = "jndi.syntax.escape"; //$NON-NLS-1$ - - private static final String BEGIN_QUOTE = "jndi.syntax.beginquote"; //$NON-NLS-1$ - - private static final String END_QUOTE = "jndi.syntax.endquote"; //$NON-NLS-1$ - - private static final String BEGIN_QUOTE2 = "jndi.syntax.beginquote2"; //$NON-NLS-1$ - - private static final String END_QUOTE2 = "jndi.syntax.endquote2"; //$NON-NLS-1$ - - private static final String IGNORE_CASE = "jndi.syntax.ignorecase"; //$NON-NLS-1$ - - private static final String TRIM_BLANKS = "jndi.syntax.trimblanks"; //$NON-NLS-1$ - - private static final String DIRECTION = "jndi.syntax.direction"; //$NON-NLS-1$ - - private static final String SEPARATOR2 = "jndi.syntax.separator2"; //$NON-NLS-1$ - - //const for direction - private static final String LEFT_TO_RIGHT = "left_to_right"; //$NON-NLS-1$ - - private static final String RIGHT_TO_LEFT = "right_to_left"; //$NON-NLS-1$ - - private static final String FLAT = "flat"; //$NON-NLS-1$ - - // alphabets consts - private static final String NULL_STRING = ""; //$NON-NLS-1$ - - //states consts - private static final int NORMAL_STATUS = 0; - - private static final int QUOTE1_STATUS = 1; - - private static final int QUOTE2_STATUS = 2; - - private static final int INIT_STATUS = 3; - - private static final int QUOTEEND_STATUS = 4; - - /* - * ============ - * Variables - * ============ - */ - //properties variables - private transient String separatorString; - - private transient String separatorString2; - - private transient String escapeString; - - private transient String endQuoteString; - - private transient String endQuoteString2; - - private transient String beginQuoteString; - - private transient String beginQuoteString2; - - private transient String sepAvaString; - - private transient String sepTypeValString; - - private transient String direction; - - private transient boolean trimBlanks; - - private transient boolean ignoreCase; - - private transient boolean flat; - - //elements of compound name - private transient Vector elem; - - //property setting - protected transient Properties mySyntax; - - /* - * The specification calls for a protected variable called 'impl' which is of a non-API - * type. I believe this is an error in the spec, but to be compliant we have implemented - * this as a useless class (below). - */ - protected transient javax.naming.NameImpl impl = new NameImpl(); - - /* - *============ - *Constructors - *============ - */ - - /** - * Constructs a CompoundName with supplied Enumeration - * and Properties - * - * @param elements an enumeration of name elements, cannot be null - * @param props the properties, cannot be null but may be empty. - * If empty, the direction defaults to flat and no - * other properties are required. - */ - protected CompoundName(Enumeration elements, Properties props) { - if (null == props || null == elements) { - throw new NullPointerException(); - } - init(props); - this.elem = new Vector(); - while (elements.hasMoreElements()) { - this.elem.add(elements.nextElement()); - } - } - - /** - * Constructs a CompoundName with supplied String - * and Properties, taking the supplied s and - * breaking it down into its elements. - * - * @param s a string containing the full compound name - * @param props the properties, cannot be null but may be empty for a flat name - * @throws InvalidNameException - * thrown if the supplied String s is invalid - * @throws NullPointerException - * thrown if the supplied String s is null - */ - public CompoundName(String s, Properties props) throws InvalidNameException { - if (null == s || null == props) { - throw new NullPointerException(); - } - init(props); - parseName(s); - } - - /* - * init instance variables - */ - private void init(Properties props) { - trimBlanks = false; - ignoreCase = false; - this.mySyntax = (Properties) props.clone(); - String property; - - //read property settings - // direction's default value is FLAT - direction = null == (property = props.getProperty(DIRECTION)) ? FLAT - : property; - //if direction value must equals to one of FLAT, LEFT_TO_RIGHT and RIGHT_TO_LEFT, exception throwed - if (!LEFT_TO_RIGHT.equals(direction) - && !RIGHT_TO_LEFT.equals(direction) && !FLAT.equals(direction)) { - throw new IllegalArgumentException( - "Illegal direction property value, which must be one of right_to_left, left_to_right or flat"); //$NON-NLS-1$ - } - flat = FLAT.equals(direction); - - separatorString = flat ? NULL_STRING : props.getProperty(SEPARATOR); - // if direction is not FLAT, separator must be set - if (null == separatorString && !flat) { - throw new IllegalArgumentException( - "jndi.syntax.separator property must be set when jndi.syntax.direction is not flat"); //$NON-NLS-1$ - } - separatorString2 = (flat || null == (property = props - .getProperty(SEPARATOR2))) ? NULL_STRING : property; - - //ignorecase default value is false - ignoreCase = null == (property = props.getProperty(IGNORE_CASE)) ? false - : Boolean.valueOf(property).booleanValue(); - //trimblanks default value is false - trimBlanks = null == (property = props.getProperty(TRIM_BLANKS)) ? false - : Boolean.valueOf(property).booleanValue(); - escapeString = null == (property = props.getProperty(ESCAPE)) ? NULL_STRING - : property; - beginQuoteString = null == (property = props.getProperty(BEGIN_QUOTE)) ? NULL_STRING - : property; - beginQuoteString2 = null == (property = props.getProperty(BEGIN_QUOTE2)) ? NULL_STRING - : property; - //end quote string default value is begin quote string - endQuoteString = null == (property = props.getProperty(END_QUOTE)) ? beginQuoteString - : property; - //begin quote string default value is end quote string - if (NULL_STRING.equals(beginQuoteString)) { - beginQuoteString = endQuoteString; - } - //end quote string2 default value is begin quote string2 - endQuoteString2 = null == (property = props.getProperty(END_QUOTE2)) ? beginQuoteString2 - : property; - //begin quote string2 default value is end quote string2 - if (NULL_STRING.equals(beginQuoteString2)) { - beginQuoteString2 = endQuoteString2; - } - - sepTypeValString = null == (property = props - .getProperty(SEPARATOR_TYPEVAL)) ? NULL_STRING : property; - sepAvaString = null == (property = props.getProperty(SEPARATOR_AVA)) ? NULL_STRING - : property; - } - - /* - * parse name from string to elements - */ - private void parseName(String s) throws InvalidNameException { - this.elem = new Vector(); - if ("".equals(s)) { //$NON-NLS-1$ - // if empty string, return empty vector - return; - } - - //init variables - int status = INIT_STATUS; - StringBuffer element = new StringBuffer(); - int pos = 0; - int length = s.length(); - boolean hasNotNullElement = false; - boolean includeQuote = false; - - //scan name - while (pos < length) { - if (startsWithFromPos(s, pos, endQuoteString) - && status == QUOTE1_STATUS) { - status = QUOTEEND_STATUS; - pos += addBuffer(element, endQuoteString, includeQuote); - } else if (startsWithFromPos(s, pos, endQuoteString2) - && status == QUOTE2_STATUS) { - status = QUOTEEND_STATUS; - pos += addBuffer(element, endQuoteString2, includeQuote); - } else if (startsWithFromPos(s, pos, beginQuoteString) - && status == INIT_STATUS) { - hasNotNullElement = true; - status = QUOTE1_STATUS; - pos += addBuffer(element, beginQuoteString, includeQuote); - } else if (startsWithFromPos(s, pos, beginQuoteString2) - && status == INIT_STATUS) { - hasNotNullElement = true; - status = QUOTE2_STATUS; - pos += addBuffer(element, beginQuoteString2, includeQuote); - } else if (startsWithFromPos(s, pos, separatorString) - && (!flat) - && (status == INIT_STATUS || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) { - hasNotNullElement = hasNotNullElement || element.length() > 0; - addElement(element); - status = INIT_STATUS; - pos += separatorString.length(); - includeQuote = false; - } else if (startsWithFromPos(s, pos, separatorString2) - && (!flat) - && (status == INIT_STATUS || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) { - hasNotNullElement = hasNotNullElement || element.length() > 0; - addElement(element); - status = INIT_STATUS; - pos += separatorString2.length(); - includeQuote = false; - } else if (startsWithFromPos(s, pos, escapeString)) { - pos += escapeString.length(); - if (pos == s.length()) { - //if this escape char is last character, throw exception - throw new InvalidNameException(new StringBuffer(50).append( - "The ") //$NON-NLS-1$ - .append(escapeString).append( - " cannot be at end of the component") //$NON-NLS-1$ - .toString()); - } - //if one escape char followed by a special char, append the special char to current element - String str = extractEscapedString(s, pos, status); - if (null == str) { - pos -= escapeString.length(); - element.append(s.charAt(pos++)); - } else { - pos += str.length(); - element.append(str); - } - - } else if (startsWithFromPos(s, pos, sepTypeValString) - && (status == INIT_STATUS || status == NORMAL_STATUS)) { - includeQuote = true; - pos += addBuffer(element, sepTypeValString, true); - status = INIT_STATUS; - } else if (startsWithFromPos(s, pos, sepAvaString) - && (status == INIT_STATUS || status == NORMAL_STATUS)) { - includeQuote = true; - pos += addBuffer(element, sepAvaString, true); - status = INIT_STATUS; - } else if (status == QUOTEEND_STATUS) { - throw new InvalidNameException( - s - + ": close quote must appears at end of component in quoted string"); //$NON-NLS-1$ - } else { - status = status == INIT_STATUS ? NORMAL_STATUS : status; - element.append(s.charAt(pos++)); - } - } - if (QUOTE1_STATUS != status && QUOTE2_STATUS != status) { - hasNotNullElement = hasNotNullElement || element.length() > 0; - addElement(element); - } else { - throw new InvalidNameException(s - + ": close quote is required for quoted string"); //$NON-NLS-1$ - } - if (!hasNotNullElement) { - elem.remove(elem.size() - 1); - } - } - - /* - * add des parameter to stringbuffer if include is true - */ - private int addBuffer(StringBuffer buffer, String des, boolean include) { - if (include) { - buffer.append(des); - } - return des.length(); - } - - /* - * add current content of supplied string buffer as one element of this - * CompoundName and reset the string buffer to empty - */ - private void addElement(StringBuffer element) { - if (LEFT_TO_RIGHT == direction) { - elem.add(element.toString()); - } else { - elem.add(0, element.toString()); - } - element.setLength(0); - } - - /* - * find string to be escaped, if cannot find special string(which means, - * quote, separator and escape), return null - */ - private String extractEscapedString(String s, int pos, int status) { - String result = null; - if (status == QUOTE1_STATUS - && startsWithFromPos(s, pos, endQuoteString)) { - result = endQuoteString; - } else if (status == QUOTE2_STATUS - && startsWithFromPos(s, pos, endQuoteString2)) { - result = endQuoteString2; - } else if (status != QUOTE1_STATUS && status != QUOTE2_STATUS) { - if (startsWithFromPos(s, pos, beginQuoteString)) { - result = beginQuoteString; - } else if (startsWithFromPos(s, pos, beginQuoteString2)) { - result = beginQuoteString2; - } else if (startsWithFromPos(s, pos, endQuoteString)) { - result = endQuoteString; - } else if (startsWithFromPos(s, pos, endQuoteString2)) { - result = endQuoteString2; - } else if (startsWithFromPos(s, pos, separatorString)) { - result = separatorString; - } else if (startsWithFromPos(s, pos, separatorString2)) { - result = separatorString2; - } else if (startsWithFromPos(s, pos, escapeString)) { - result = escapeString; - } - } - return result; - } - - /* - * justify if string src start with des from position pos - * @param src - * @param pos - * @param des - * @return - */ - private boolean startsWithFromPos(String src, int pos, String des) { - if (null == src || null == des || NULL_STRING.equals(des) - || src.length() - pos < des.length()) { - return false; - } - int length = des.length(); - int i = -1; - while (++i < length && src.charAt(pos + i) == des.charAt(i)) { - //empty body - } - return i == length; - } - - /* - * ------------------------------------------------------------------- - * Methods of interface Name - * ------------------------------------------------------------------- - */ - public Enumeration getAll() { - return this.elem.elements(); - } - - public String get(int index) { - validateIndex(index, false); - return (String) elem.elementAt(index); - } - - /* - * validate the index, if isInclude is true, index which equals to this.size() - * is considered as valid, otherwise invalid - */ - private void validateIndex(int index, boolean isInclude) { - if (0 > index || index > elem.size() - || (!isInclude && index == elem.size())) { - throw new ArrayIndexOutOfBoundsException(); - } - } - - public Name getPrefix(int index) { - validateIndex(index, true); - return new CompoundName(new Vector(elem.subList(0, index)).elements(), - mySyntax); - } - - public Name getSuffix(int index) { - validateIndex(index, false); - return new CompoundName(new Vector(elem.subList(index, elem.size())) - .elements(), mySyntax); - } - - public Name addAll(Name name) throws InvalidNameException { - return addAll(elem.size(), name); - } - - public Name addAll(int index, Name name) throws InvalidNameException { - if (!(name instanceof CompoundName)) { - throw new InvalidNameException(name.toString() - + " is not a compound name."); //$NON-NLS-1$ - } - validateIndex(index, true); - Enumeration enumeration = name.getAll(); - while (enumeration.hasMoreElements()) { - elem.add(index++, enumeration.nextElement()); - } - return this; - } - - public Name add(String element) throws InvalidNameException { - if (element == null) { - throw new InvalidNameException("A flat name can only have a single component"); - } - elem.add(element); - return this; - } - - /** - * Insert an element within this CompoundName at the specified index. - * @return this CompoundName. - * @param element the String to insert - * @param index the index of the element to insert - must be greater - * than or equal to 0 and less than size(). - * @throws ArrayIndexOutOfBoundsException - * thrown when the index is invalid. - * @throws InvalidNameException - * thrown if the insertion of the element results in - * this CompoundName becoming invalid. - */ - public Name add(int index, String element) throws InvalidNameException { - if (element == null) { - throw new InvalidNameException("A flat name can only zero or one component"); - } - validateIndex(index, true); - elem.add(index, element); - return this; - } - - /** - * Delete an element from this CompoundName. - * - * @return the deleted element - * @param index the index of the element to delete - must be greater - * than or equal to 0 and less than size(). - * @throws ArrayIndexOutOfBoundsException - * thrown when the index is invalid. - * @throws InvalidNameException - * thrown if the deletion of the element results in - * this CompoundName becoming invalid. - */ - public Object remove(int index) throws InvalidNameException { - validateIndex(index, false); - return elem.remove(index); - } - - public Object clone() { - CompoundName newName = null; - try { - newName = (CompoundName) super.clone(); - } catch (CloneNotSupportedException e) { - throw new Error("Failed to clone object of CompoundName class"); //$NON-NLS-1$ - } - newName.mySyntax = (Properties) mySyntax.clone(); - newName.elem = (Vector) elem.clone(); - return newName; - } - - public int size() { - return elem.size(); - } - - public boolean isEmpty() { - return elem.isEmpty(); - } - - public boolean startsWith(Name name) { - if (!(name instanceof CompoundName)) { - return false; - } - return equals(name, 0, name.size()); - } - - public boolean endsWith(Name name) { - if (!(name instanceof CompoundName)) { - return false; - } - return equals(name, this.size() - name.size(), name - .size()); - } - - /* - * preprocess string according to trimblank and ignorecase properties - */ - private String preProcess(String string, boolean caseInsensitive, - boolean removeBlanks) { - String result = string; - if (null != string && !"".equals(string)) { //$NON-NLS-1$ - result = caseInsensitive ? result.toLowerCase() : result; - result = removeBlanks ? result.trim() : result; - } - return result; - } - - /* - * Writes a serialized representation of the CompoundName. It starts with - * the properties, followed by an int which is the number of elements - * in the name, and is followed by a String for each element. - * @throws java.io.IOException if an error is encountered writing to the stream. - */ - private void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); - oos.writeObject(mySyntax); - oos.writeInt(elem.size()); - for (int i = 0; i < elem.size(); i++) { - String element = (String) elem.elementAt(i); - oos.writeObject(element); - } - } - - /* - * Recreate a CompoundName from the data in the supplied stream. - * Additionally there are 2 protected fields which are not serializable. - * One of them is of a type which is a private class and cannot therefore - * be specified or implemented and so will be excluded from our deliverable. - * The one protected field which we can spec and implement is as follows: - * protected Properties mySyntax - The properties associated with a CompoundName. - - - * - * @throws java.io.IOException if an error is encountered reading from the stream. - * @throws ClassNotFoundException. - */ - private void readObject(ObjectInputStream ois) - throws ClassNotFoundException, IOException { - ois.defaultReadObject(); - init(((Properties) ois.readObject())); - int size = ois.readInt(); - elem = new Vector(); - for (int i = 0; i < size; i++) { - elem.add((String)ois.readObject()); - } - } - - /** - * Compare this CompoundName with the one supplied as a param. - *

- * See the definition of the equals() method to see how the - * direction, ignorecase and trimblanks properties affect the comparison of - * a CompoundName. Other than that the comparison is the same - * as that for a CompositeName.

- * - * @return a negative number means this is less than the - * supplied Object o. - * a positive number means this is greater than the - * supplied Object o. - * zero means the two objects are equal. - * @param o the object to compare - cannot be null. - * @throws ClassCastException - * when o is not a compatible class that - * can be compared or if the object to compare - * o is null. - */ - public int compareTo(Object o) { - if (!(o instanceof CompoundName)) { - throw new ClassCastException(); - } - int result = -1; - CompoundName otherName = (CompoundName) o; - Enumeration otherEnum = otherName.getAll(); - String thisElement; - String otherElement; - int i; - for (i = 0; i < size() && otherEnum.hasMoreElements(); i++) { - thisElement = preProcess((String) elem.get(i), ignoreCase, - trimBlanks); - otherElement = preProcess((String) otherEnum.nextElement(), - ignoreCase, trimBlanks); - result = (null == thisElement ? (null == otherElement ? 0 : -1) - : thisElement.compareTo(otherElement)); - if (0 != result) { - return result; - } - } - if (i < size()) { - result = 1; - } else if (otherEnum.hasMoreElements()) { - result = -1; - } - return result; - } - - /* - * ------------------------------------------------------------------- - * Methods override parent class Object - * ------------------------------------------------------------------- - */ - - /** - * Calculate the hashcode of this CompoundName by summing - * the hashcodes of all of its elements. - *

- * If jndi.syntax.trimblanks is set to true then remove any leading and - * trailing blanks from the elements before calculating the hashcode.

- *

- * If jndi.syntax.ignorecase is set to true then use the lowercase version - * of the element to calculate its hashcode.

- * - * @return the hashcode of this object. - */ - public int hashCode() { - int result = 0; - Enumeration enumeration = elem.elements(); - while (enumeration.hasMoreElements()) { - result += preProcess((String) enumeration.nextElement(), - ignoreCase, trimBlanks).hashCode(); - } - return result; - } - - /** - * Gets the string representation of this CompoundName. - *

- * This is generated by concatenating the elements together with the - * separator string added as the separator between each of them. It may be - * necessary to add quotes and escape string to preserve the meaning. - * The resulting string should produce an equivalent CompoundName - * when used to create a new instance.

- * - * @return the string representation of this CompoundName. - */ - public String toString() { - StringBuffer sb = new StringBuffer(); - String begin = NULL_STRING.equals(beginQuoteString) ? beginQuoteString2 - : beginQuoteString; - String end = NULL_STRING.equals(endQuoteString) ? endQuoteString2 - : endQuoteString; - String separator = NULL_STRING.equals(separatorString) ? separatorString2 - : separatorString; - if(RIGHT_TO_LEFT.equals(direction)){ - for (int i = elem.size() - 1; i >= 0; i--) { - addElement(sb, i, separator, begin, end); - } - }else{ - for (int i = 0; i < elem.size(); i++) { - addElement(sb, i, separator, begin, end); - } - } - if (size() * separator.length() < sb.length()) { - //if the name contains non-empty element, delete the last separator char, which is abundant - sb.setLength(sb.length() - separator.length()); - } - return sb.toString(); - } - - private void addElement(StringBuffer sb, int index, String separator, String begin, String end) { - String elemString = elem.get(index); - if (0 == elemString.length()) { - // if empty element, append a separator and continue - sb.append(separator); - return; - } - int pos = sb.length(); - sb.append(elemString); - if (!NULL_STRING.equals(begin) && !NULL_STRING.equals(end) - && !NULL_STRING.equals(separator) - && (0 <= elemString.indexOf(separator))) { - // if contains separator string, quoted it - sb.insert(pos, begin); - pos += begin.length(); - // if quoted, then every endquote char must be escaped - for (int i = 0, j = 0; 0 <= (j = elemString.indexOf(end, i)); i = j - + end.length()) { - sb.insert(pos + j, escapeString); - pos += escapeString.length(); - } - sb.append(end); - } else { - if (startsWithFromPos(elemString, 0, beginQuoteString) - || startsWithFromPos(elemString, 0, beginQuoteString2)) { - // if not quoted and start with begin quote string, escape it - sb.insert(pos, escapeString); - pos += escapeString.length(); - } - // if not quoted, escape all separator string and all escape string - for (int i = 0; i < elemString.length();) { - if (startsWithFromPos(elemString, i, separatorString)) { - sb.insert(pos + i, escapeString); - pos += escapeString.length(); - i += separatorString.length(); - } else if (startsWithFromPos(elemString, i, separatorString2)) { - sb.insert(pos + i, escapeString); - pos += escapeString.length(); - i += separatorString2.length(); - } else if (startsWithFromPos(elemString, i, escapeString)) { - sb.insert(pos + i, escapeString); - pos += escapeString.length(); - i += escapeString.length(); - } else { - i++; - } - } - } - sb.append(separator); - } - - /** - * Check if the supplied object o is equal to this - * CompoundName. - *

- * The supplied Object o may be null but that will cause - * false to be returned. - *

- *

- * The supplied Object o may be something other than a - * CompoundName but that will cause false to be returned. - *

- *

- * To be equal the supplied CompoundName must have the same - * number of elements and each element must match the corresponding element - * of this CompoundName. The properties - * jndi.syntax.ignorecase and jndi.syntax.trimblanks need to be considered - * if they have been set. - *

- *

- * The properties associated with the CompoundName must be - * taken into account but do not have to match. For example - * "home/jenningm-abc/.profile" with a direction of left to right is equal - * to ".profile/jenningm-abc/home" with a direction of right to left. - *

- * - * @param o - * the object to be compared - * @return true if supplied object o is equals to this - * CompoundName, false otherwise - */ - public boolean equals(Object o) { - if (!(o instanceof CompoundName)) { - return false; - } - - //compare size - CompoundName otherName = (CompoundName) o; - final int size = otherName.size(); - if (size != this.size()) { - return false; - } - - //compare every element - return equals(otherName, 0, size); - } - - /* - * compare this name to the supplied name from position - * start to position start+ - * length-1 - * - */ - private boolean equals(Name name, int start, int length) { - if (length > this.size()) { - return false; - } - CompoundName otherName = (CompoundName) name; - Enumeration otherEnum = otherName.getAll(); - String thisElement; - String otherElement; - for (int i = 0; i < length; i++) { - thisElement = preProcess((String) elem.get(i + start), ignoreCase, - trimBlanks); - otherElement = preProcess((String) otherEnum.nextElement(), - ignoreCase, trimBlanks); - if (!(null == thisElement ? null == otherElement : thisElement - .equals(otherElement))) { - return false; - } - } - return true; - } -} - -/* - * A useless class requied to satisfy the requirement for an 'impl' - * field (see above). - */ -class NameImpl { - protected NameImpl() { - super(); - } -} - - +/* Copyright 2004 The Apache Software Foundation or its licensors, as applicable + * + * 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 javax.naming; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +/** + * A CompoundName is a series of string elements, and it represents + * a name in a naming service within a single namespace. Typically these + * names have a structure which is hierarchical. + *

+ * A CompoundName has a sequence of zero or more elements delimited + * by the char specified in the property "jndi.syntax.separator". This property + * is required except when the direction of the name is "flat" + * (see jndi.syntax.direction). The property "jndi.syntax.separator2" allows for + * the specification of an additional separator. A separator string will be + * treated as normal characters if it is preceded by the escape string or is + * within quotes.

+ *

+ * The property "jndi.syntax.direction" specifies the direction in which the name + * is read. Permitted values are "right_to_left", "left_to_right" and "flat". A + * flat name does not have a hierarchical structure. If this property is not + * specified then the default is "flat". If this property is specified with an + * invalid value then an IllegalArgumentException should be raised.

+ *

+ * Each element can be accessed using its position. The first element is at + * position 0. The direction of the name is important. When direction is + * "left_to_right" then the leftmost element is at position 0. Conversely when + * the direction is "right_to_left" then the rightmost element is at position 0.

+ *

+ * There are other properties which affect the syntax of a CompoundName. + * The following properties are all optional: + *

    + *
  • + * jndi.syntax.escape - Escape sequence,The escape sequence is used to escape + * a quote, separator or escape. When preceded itself by + * the escape sequence it is treated as ordinary characters. + * When it is followed by chars which are not quote or + * separator strings then it is treated as ordinary characters
  • + *
  • + * jndi.syntax.beginquote - Used as start of quoted string (Defaults to endquote)
  • + *
  • + * jndi.syntax.endquote - Used as end of quoted string (Defaults to beginquote)
  • + *
  • + * jndi.syntax.beginquote2 - Additionally used as start of quoted string + * (Defaults to endquote2)
  • + *
  • + * jndi.syntax.endquote2 - Additionally used as end of quoted string + * (Defaults to beginquote2)
  • + *
+ *

+ * When a non-escaped quote appears at the start of an element it must be matched at the + * end. That element can then be said to be quoted. When an escape sequence appears + * within a quoted element then it is treated as normal characters unless it precedes + * an occurrence of the quote in which case it is assumed that the quoted element + * contains a quote which is escaped.

+ *

+ * If the element does not start with a quote, then any quote strings within that + * element are just normal characters.

+ *

+ *

    + *
  • + * jndi.syntax.ignorecase - If 'true' then ignore case when name elements are compared. + * If false or not set then case is important.
  • + *
  • + * jndi.syntax.trimblanks - If 'true' then ignore leading & trailing blanks when name elements are compared. + * If false or not set then blanks are important.
  • + *

+ *

+ * These 2 properties relate to names where the syntax includes attribute/content + * pairs. + *

    + *
  • jndi.syntax.separator.ava
  • + *
  • jndi.syntax.separator.typeval
  • + *
+ * For example the LDAP name, "CN=Mandy Jennings, O=Apache, C=UK". + * In this example the pair separator jndi.syntax.separator.ava is ',', and the + * character that separates pairs jndi.syntax.separator.typeval is '='. See + * RFC1779 for LDAP naming conventions.

+ *

+ * The jndi.syntax.separator.ava is not used when manipulating CompoundName. + * The jndi.syntax.separator is still used to separate elements.

+ *

+ * The CompoundName needs to be aware of the jndi.syntax.separator.typeval + * in case of the instance where a quoted string is used to provide the content + * of a pair.

+ *

+ * Consider the string "CN=$Mandy Jennings, O=Apache, C=UK" with + *

    + *
  • + * jndi.syntax.direction set to "right_to_left"
  • + *
  • + * jndi.syntax.separator set to ","
  • + *
  • + * jndi.syntax.separator.typeval set to "="
  • + *
+ * When no jndi.syntax.beginquote is set then this creates a valid CompoundName + * with 3 elements.

+ *

+ * If jndi.syntax.beginquote is then set to "$" the name becomes invalid as the + * content part of the pair CN=$Mandy Jennings has a mismatched quote.

+ *

+ * The string "CN=$Mandy Jennings$, O=Apache, C=UK" would be fine as the $ quotes + * round Mandy Jennings now balance.

+ *

+ * A CompoundName may be empty. An empty CompoundName has no elements. + * Elements may also be empty.

+ * + *
+ * Some Examples:
+ * ==============
+ * 
+ * Consider the following compound name from the file system namespace:
+ *     "home/jenningm-abc/.profile" 
+ * 
+ * jndi.syntax.separator is set to '/' as in the UNIX filesystem.
+ * This name has 3 elements:
+ *     home jenningm-abc and .profile
+ * The direction should be left_to_right as in the UNIX filesystem
+ * The element at position 0 would be home.
+ * 
+ * 
+ * Consider if jndi.syntax.separator had been set to '-' then this name 
+ * would have 2 elements:
+ *     home/jenningm and abc/.profile
+ * If the direction was right_to_left then the element at position 0
+ * would be abc/.profile.
+ * 
+ * Consider the name "<ab<cd>ef>" where jndi.syntax.beginquote is <
+ * and jndi.syntax.endquote is >. This will give rise to an 
+ * InvalidNameException because a close quote was encountered before
+ * the end of an element. The same is also true for "<abcd>ef>".
+ * If the name was "ab<cd>ef" then this would be valid and there would
+ * be one element ab<cd>ef.
+ * However if the name was "<abcdef>" there would be one element abcdef.
+ * 
+ * An empty CompoundName is the name "" and has no elements.
+ * 
+ * When jndi.syntax.beginquote is set to " and beginquote2 is set to '
+ * the behaviour is simliar to CompositeName - 
+ * The name "\"abcd" gives an InvalidNameException as there is no closing quote.
+ * The name "'\"abcd'" gives one element of value "abcd.
+ * The name "\\abcd" gives one element of value \abcd. 
+ *  
+ * Assuming:
+ *     jndi.syntax.separator is "/"
+ *     jndi.syntax.direction is "left_to_right"
+ * then
+ *     "" is empty. It has no elements.
+ *     "/" has one empty element.
+ *     "//" has 2 empty elements.
+ *     "/a/" has 3 elements the middle one is set to a.
+ *     "///" has 3 empty elements.
+ *     "//a/" has 4 elements, the last but one is set to a.
+ * 
+ *
+ * Assuming the only properties set are: 
+ *     jndi.syntax.separator is "/"
+ *     jndi.syntax.direction is "left_to_right"
+ * then the String
+ *     "\"" has one element with the value "
+ *     "\\\"" has one element with the value \"
+ *     "\\\"'" has one element with the value \"'
+ * 
+ * Assuming the only properties set are:
+ *     jndi.syntax.separator is "/"
+ *     jndi.syntax.direction is "left_to_right"
+ *     jndi.syntax.beginquote is "\""
+ * 
+ * then the String
+ *     "\"" is invalid because of no closing quote
+ *     "\\\"" has one element with the value \"
+ *     "\\\"'" has one element with the value \"'
+ * 
+ * 
+ * Assuming the only properties set are:
+ *     jndi.syntax.separator is "/"
+ *     jndi.syntax.direction is "left_to_right"
+ *     jndi.syntax.beginquote is "\""
+ *     jndi.syntax.beginquote2 is "\'"
+ * then the String
+ *     "\"" is invalid because of no closing quote
+ *     "\\\"" has one element with the value \"
+ *     "\\\"'" has one element with the value \"'
+ *     "'\\" is invalid because of no closing quote
+ * 
+ * + * + */ + +public class CompoundName implements Name { + + /* + * ------------------------------------------------------------------- + * Constants + * ------------------------------------------------------------------- + */ + + /* + *============= + *Serialization + *============= + * + *Note: For serialization purposes, the specified serialVersionUID must be used. + *This class does not have serializable fields specified. Instead the readObject + *and writeObject methods are overidden. + */ + static final long serialVersionUID = 3513100557083972036L; // J2SE 1.4.2 + + //const for property key + private static final String SEPARATOR = "jndi.syntax.separator"; //$NON-NLS-1$ + + private static final String SEPARATOR_AVA = "jndi.syntax.separator.ava"; //$NON-NLS-1$ + + private static final String SEPARATOR_TYPEVAL = "jndi.syntax.separator.typeval"; //$NON-NLS-1$ + + private static final String ESCAPE = "jndi.syntax.escape"; //$NON-NLS-1$ + + private static final String BEGIN_QUOTE = "jndi.syntax.beginquote"; //$NON-NLS-1$ + + private static final String END_QUOTE = "jndi.syntax.endquote"; //$NON-NLS-1$ + + private static final String BEGIN_QUOTE2 = "jndi.syntax.beginquote2"; //$NON-NLS-1$ + + private static final String END_QUOTE2 = "jndi.syntax.endquote2"; //$NON-NLS-1$ + + private static final String IGNORE_CASE = "jndi.syntax.ignorecase"; //$NON-NLS-1$ + + private static final String TRIM_BLANKS = "jndi.syntax.trimblanks"; //$NON-NLS-1$ + + private static final String DIRECTION = "jndi.syntax.direction"; //$NON-NLS-1$ + + private static final String SEPARATOR2 = "jndi.syntax.separator2"; //$NON-NLS-1$ + + //const for direction + private static final String LEFT_TO_RIGHT = "left_to_right"; //$NON-NLS-1$ + + private static final String RIGHT_TO_LEFT = "right_to_left"; //$NON-NLS-1$ + + private static final String FLAT = "flat"; //$NON-NLS-1$ + + // alphabets consts + private static final String NULL_STRING = ""; //$NON-NLS-1$ + + //states consts + private static final int NORMAL_STATUS = 0; + + private static final int QUOTE1_STATUS = 1; + + private static final int QUOTE2_STATUS = 2; + + private static final int INIT_STATUS = 3; + + private static final int QUOTEEND_STATUS = 4; + + /* + * ============ + * Variables + * ============ + */ + //properties variables + private transient String separatorString; + + private transient String separatorString2; + + private transient String escapeString; + + private transient String endQuoteString; + + private transient String endQuoteString2; + + private transient String beginQuoteString; + + private transient String beginQuoteString2; + + private transient String sepAvaString; + + private transient String sepTypeValString; + + private transient String direction; + + private transient boolean trimBlanks; + + private transient boolean ignoreCase; + + private transient boolean flat; + + //elements of compound name + private transient Vector elem; + + //property setting + protected transient Properties mySyntax; + + /* + * The specification calls for a protected variable called 'impl' which is of a non-API + * type. I believe this is an error in the spec, but to be compliant we have implemented + * this as a useless class (below). + */ + protected transient javax.naming.NameImpl impl = new NameImpl(); + + /* + *============ + *Constructors + *============ + */ + + /** + * Constructs a CompoundName with supplied Enumeration + * and Properties + * + * @param elements an enumeration of name elements, cannot be null + * @param props the properties, cannot be null but may be empty. + * If empty, the direction defaults to flat and no + * other properties are required. + */ + protected CompoundName(Enumeration elements, Properties props) { + if (null == props || null == elements) { + throw new NullPointerException(); + } + init(props); + this.elem = new Vector(); + while (elements.hasMoreElements()) { + this.elem.add(elements.nextElement()); + } + } + + /** + * Constructs a CompoundName with supplied String + * and Properties, taking the supplied s and + * breaking it down into its elements. + * + * @param s a string containing the full compound name + * @param props the properties, cannot be null but may be empty for a flat name + * @throws InvalidNameException + * thrown if the supplied String s is invalid + * @throws NullPointerException + * thrown if the supplied String s is null + */ + public CompoundName(String s, Properties props) throws InvalidNameException { + if (null == s || null == props) { + throw new NullPointerException(); + } + init(props); + parseName(s); + } + + /* + * init instance variables + */ + private void init(Properties props) { + trimBlanks = false; + ignoreCase = false; + this.mySyntax = (Properties) props.clone(); + String property; + + //read property settings + // direction's default value is FLAT + direction = null == (property = props.getProperty(DIRECTION)) ? FLAT + : property; + //if direction value must equals to one of FLAT, LEFT_TO_RIGHT and RIGHT_TO_LEFT, exception throwed + if (!LEFT_TO_RIGHT.equals(direction) + && !RIGHT_TO_LEFT.equals(direction) && !FLAT.equals(direction)) { + throw new IllegalArgumentException( + "Illegal direction property value, which must be one of right_to_left, left_to_right or flat"); //$NON-NLS-1$ + } + flat = FLAT.equals(direction); + + separatorString = flat ? NULL_STRING : props.getProperty(SEPARATOR); + // if direction is not FLAT, separator must be set + if (null == separatorString && !flat) { + throw new IllegalArgumentException( + "jndi.syntax.separator property must be set when jndi.syntax.direction is not flat"); //$NON-NLS-1$ + } + separatorString2 = (flat || null == (property = props + .getProperty(SEPARATOR2))) ? NULL_STRING : property; + + //ignorecase default value is false + ignoreCase = null == (property = props.getProperty(IGNORE_CASE)) ? false + : Boolean.valueOf(property).booleanValue(); + //trimblanks default value is false + trimBlanks = null == (property = props.getProperty(TRIM_BLANKS)) ? false + : Boolean.valueOf(property).booleanValue(); + escapeString = null == (property = props.getProperty(ESCAPE)) ? NULL_STRING + : property; + beginQuoteString = null == (property = props.getProperty(BEGIN_QUOTE)) ? NULL_STRING + : property; + beginQuoteString2 = null == (property = props.getProperty(BEGIN_QUOTE2)) ? NULL_STRING + : property; + //end quote string default value is begin quote string + endQuoteString = null == (property = props.getProperty(END_QUOTE)) ? beginQuoteString + : property; + //begin quote string default value is end quote string + if (NULL_STRING.equals(beginQuoteString)) { + beginQuoteString = endQuoteString; + } + //end quote string2 default value is begin quote string2 + endQuoteString2 = null == (property = props.getProperty(END_QUOTE2)) ? beginQuoteString2 + : property; + //begin quote string2 default value is end quote string2 + if (NULL_STRING.equals(beginQuoteString2)) { + beginQuoteString2 = endQuoteString2; + } + + sepTypeValString = null == (property = props + .getProperty(SEPARATOR_TYPEVAL)) ? NULL_STRING : property; + sepAvaString = null == (property = props.getProperty(SEPARATOR_AVA)) ? NULL_STRING + : property; + } + + /* + * parse name from string to elements + */ + private void parseName(String s) throws InvalidNameException { + this.elem = new Vector(); + if ("".equals(s)) { //$NON-NLS-1$ + // if empty string, return empty vector + return; + } + + //init variables + int status = INIT_STATUS; + StringBuffer element = new StringBuffer(); + int pos = 0; + int length = s.length(); + boolean hasNotNullElement = false; + boolean includeQuote = false; + + //scan name + while (pos < length) { + if (startsWithFromPos(s, pos, endQuoteString) + && status == QUOTE1_STATUS) { + status = QUOTEEND_STATUS; + pos += addBuffer(element, endQuoteString, includeQuote); + } else if (startsWithFromPos(s, pos, endQuoteString2) + && status == QUOTE2_STATUS) { + status = QUOTEEND_STATUS; + pos += addBuffer(element, endQuoteString2, includeQuote); + } else if (startsWithFromPos(s, pos, beginQuoteString) + && status == INIT_STATUS) { + hasNotNullElement = true; + status = QUOTE1_STATUS; + pos += addBuffer(element, beginQuoteString, includeQuote); + } else if (startsWithFromPos(s, pos, beginQuoteString2) + && status == INIT_STATUS) { + hasNotNullElement = true; + status = QUOTE2_STATUS; + pos += addBuffer(element, beginQuoteString2, includeQuote); + } else if (startsWithFromPos(s, pos, separatorString) + && (!flat) + && (status == INIT_STATUS || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) { + hasNotNullElement = hasNotNullElement || element.length() > 0; + addElement(element); + status = INIT_STATUS; + pos += separatorString.length(); + includeQuote = false; + } else if (startsWithFromPos(s, pos, separatorString2) + && (!flat) + && (status == INIT_STATUS || status == QUOTEEND_STATUS || status == NORMAL_STATUS)) { + hasNotNullElement = hasNotNullElement || element.length() > 0; + addElement(element); + status = INIT_STATUS; + pos += separatorString2.length(); + includeQuote = false; + } else if (startsWithFromPos(s, pos, escapeString)) { + pos += escapeString.length(); + if (pos == s.length()) { + //if this escape char is last character, throw exception + throw new InvalidNameException(new StringBuffer(50).append( + "The ") //$NON-NLS-1$ + .append(escapeString).append( + " cannot be at end of the component") //$NON-NLS-1$ + .toString()); + } + //if one escape char followed by a special char, append the special char to current element + String str = extractEscapedString(s, pos, status); + if (null == str) { + pos -= escapeString.length(); + element.append(s.charAt(pos++)); + } else { + pos += str.length(); + element.append(str); + } + + } else if (startsWithFromPos(s, pos, sepTypeValString) + && (status == INIT_STATUS || status == NORMAL_STATUS)) { + includeQuote = true; + pos += addBuffer(element, sepTypeValString, true); + status = INIT_STATUS; + } else if (startsWithFromPos(s, pos, sepAvaString) + && (status == INIT_STATUS || status == NORMAL_STATUS)) { + includeQuote = true; + pos += addBuffer(element, sepAvaString, true); + status = INIT_STATUS; + } else if (status == QUOTEEND_STATUS) { + throw new InvalidNameException( + s + + ": close quote must appears at end of component in quoted string"); //$NON-NLS-1$ + } else { + status = status == INIT_STATUS ? NORMAL_STATUS : status; + element.append(s.charAt(pos++)); + } + } + if (QUOTE1_STATUS != status && QUOTE2_STATUS != status) { + hasNotNullElement = hasNotNullElement || element.length() > 0; + addElement(element); + } else { + throw new InvalidNameException(s + + ": close quote is required for quoted string"); //$NON-NLS-1$ + } + if (!hasNotNullElement) { + elem.remove(elem.size() - 1); + } + } + + /* + * add des parameter to stringbuffer if include is true + */ + private int addBuffer(StringBuffer buffer, String des, boolean include) { + if (include) { + buffer.append(des); + } + return des.length(); + } + + /* + * add current content of supplied string buffer as one element of this + * CompoundName and reset the string buffer to empty + */ + private void addElement(StringBuffer element) { + if (LEFT_TO_RIGHT == direction) { + elem.add(element.toString()); + } else { + elem.add(0, element.toString()); + } + element.setLength(0); + } + + /* + * find string to be escaped, if cannot find special string(which means, + * quote, separator and escape), return null + */ + private String extractEscapedString(String s, int pos, int status) { + String result = null; + if (status == QUOTE1_STATUS + && startsWithFromPos(s, pos, endQuoteString)) { + result = endQuoteString; + } else if (status == QUOTE2_STATUS + && startsWithFromPos(s, pos, endQuoteString2)) { + result = endQuoteString2; + } else if (status != QUOTE1_STATUS && status != QUOTE2_STATUS) { + if (startsWithFromPos(s, pos, beginQuoteString)) { + result = beginQuoteString; + } else if (startsWithFromPos(s, pos, beginQuoteString2)) { + result = beginQuoteString2; + } else if (startsWithFromPos(s, pos, endQuoteString)) { + result = endQuoteString; + } else if (startsWithFromPos(s, pos, endQuoteString2)) { + result = endQuoteString2; + } else if (startsWithFromPos(s, pos, separatorString)) { + result = separatorString; + } else if (startsWithFromPos(s, pos, separatorString2)) { + result = separatorString2; + } else if (startsWithFromPos(s, pos, escapeString)) { + result = escapeString; + } + } + return result; + } + + /* + * justify if string src start with des from position pos + * @param src + * @param pos + * @param des + * @return + */ + private boolean startsWithFromPos(String src, int pos, String des) { + if (null == src || null == des || NULL_STRING.equals(des) + || src.length() - pos < des.length()) { + return false; + } + int length = des.length(); + int i = -1; + while (++i < length && src.charAt(pos + i) == des.charAt(i)) { + //empty body + } + return i == length; + } + + /* + * ------------------------------------------------------------------- + * Methods of interface Name + * ------------------------------------------------------------------- + */ + public Enumeration getAll() { + return this.elem.elements(); + } + + public String get(int index) { + validateIndex(index, false); + return (String) elem.elementAt(index); + } + + /* + * validate the index, if isInclude is true, index which equals to this.size() + * is considered as valid, otherwise invalid + */ + private void validateIndex(int index, boolean isInclude) { + if (0 > index || index > elem.size() + || (!isInclude && index == elem.size())) { + throw new ArrayIndexOutOfBoundsException(); + } + } + + public Name getPrefix(int index) { + validateIndex(index, true); + return new CompoundName(new Vector(elem.subList(0, index)).elements(), + mySyntax); + } + + public Name getSuffix(int index) { + validateIndex(index, false); + return new CompoundName(new Vector(elem.subList(index, elem.size())) + .elements(), mySyntax); + } + + public Name addAll(Name name) throws InvalidNameException { + return addAll(elem.size(), name); + } + + public Name addAll(int index, Name name) throws InvalidNameException { + if (!(name instanceof CompoundName)) { + throw new InvalidNameException(name.toString() + + " is not a compound name."); //$NON-NLS-1$ + } + validateIndex(index, true); + Enumeration enumeration = name.getAll(); + while (enumeration.hasMoreElements()) { + elem.add(index++, enumeration.nextElement()); + } + return this; + } + + public Name add(String element) throws InvalidNameException { + if (element == null) { + throw new InvalidNameException("A flat name can only have a single component"); + } + elem.add(element); + return this; + } + + /** + * Insert an element within this CompoundName at the specified index. + * @return this CompoundName. + * @param element the String to insert + * @param index the index of the element to insert - must be greater + * than or equal to 0 and less than size(). + * @throws ArrayIndexOutOfBoundsException + * thrown when the index is invalid. + * @throws InvalidNameException + * thrown if the insertion of the element results in + * this CompoundName becoming invalid. + */ + public Name add(int index, String element) throws InvalidNameException { + if (element == null) { + throw new InvalidNameException("A flat name can only zero or one component"); + } + validateIndex(index, true); + elem.add(index, element); + return this; + } + + /** + * Delete an element from this CompoundName. + * + * @return the deleted element + * @param index the index of the element to delete - must be greater + * than or equal to 0 and less than size(). + * @throws ArrayIndexOutOfBoundsException + * thrown when the index is invalid. + * @throws InvalidNameException + * thrown if the deletion of the element results in + * this CompoundName becoming invalid. + */ + public Object remove(int index) throws InvalidNameException { + validateIndex(index, false); + return elem.remove(index); + } + + public Object clone() { + CompoundName newName = null; + try { + newName = (CompoundName) super.clone(); + } catch (CloneNotSupportedException e) { + throw new Error("Failed to clone object of CompoundName class"); //$NON-NLS-1$ + } + newName.mySyntax = (Properties) mySyntax.clone(); + newName.elem = (Vector) elem.clone(); + return newName; + } + + public int size() { + return elem.size(); + } + + public boolean isEmpty() { + return elem.isEmpty(); + } + + public boolean startsWith(Name name) { + if (!(name instanceof CompoundName)) { + return false; + } + return equals(name, 0, name.size()); + } + + public boolean endsWith(Name name) { + if (!(name instanceof CompoundName)) { + return false; + } + return equals(name, this.size() - name.size(), name + .size()); + } + + /* + * preprocess string according to trimblank and ignorecase properties + */ + private String preProcess(String string, boolean caseInsensitive, + boolean removeBlanks) { + String result = string; + if (null != string && !"".equals(string)) { //$NON-NLS-1$ + result = caseInsensitive ? result.toLowerCase() : result; + result = removeBlanks ? result.trim() : result; + } + return result; + } + + /* + * Writes a serialized representation of the CompoundName. It starts with + * the properties, followed by an int which is the number of elements + * in the name, and is followed by a String for each element. + * @throws java.io.IOException if an error is encountered writing to the stream. + */ + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.defaultWriteObject(); + oos.writeObject(mySyntax); + oos.writeInt(elem.size()); + for (int i = 0; i < elem.size(); i++) { + String element = (String) elem.elementAt(i); + oos.writeObject(element); + } + } + + /* + * Recreate a CompoundName from the data in the supplied stream. + * Additionally there are 2 protected fields which are not serializable. + * One of them is of a type which is a private class and cannot therefore + * be specified or implemented and so will be excluded from our deliverable. + * The one protected field which we can spec and implement is as follows: + * protected Properties mySyntax - The properties associated with a CompoundName. + + + * + * @throws java.io.IOException if an error is encountered reading from the stream. + * @throws ClassNotFoundException. + */ + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + ois.defaultReadObject(); + init(((Properties) ois.readObject())); + int size = ois.readInt(); + elem = new Vector(); + for (int i = 0; i < size; i++) { + elem.add((String)ois.readObject()); + } + } + + /** + * Compare this CompoundName with the one supplied as a param. + *

+ * See the definition of the equals() method to see how the + * direction, ignorecase and trimblanks properties affect the comparison of + * a CompoundName. Other than that the comparison is the same + * as that for a CompositeName.

+ * + * @return a negative number means this is less than the + * supplied Object o. + * a positive number means this is greater than the + * supplied Object o. + * zero means the two objects are equal. + * @param o the object to compare - cannot be null. + * @throws ClassCastException + * when o is not a compatible class that + * can be compared or if the object to compare + * o is null. + */ + public int compareTo(Object o) { + if (!(o instanceof CompoundName)) { + throw new ClassCastException(); + } + int result = -1; + CompoundName otherName = (CompoundName) o; + Enumeration otherEnum = otherName.getAll(); + String thisElement; + String otherElement; + int i; + for (i = 0; i < size() && otherEnum.hasMoreElements(); i++) { + thisElement = preProcess((String) elem.get(i), ignoreCase, + trimBlanks); + otherElement = preProcess((String) otherEnum.nextElement(), + ignoreCase, trimBlanks); + result = (null == thisElement ? (null == otherElement ? 0 : -1) + : thisElement.compareTo(otherElement)); + if (0 != result) { + return result; + } + } + if (i < size()) { + result = 1; + } else if (otherEnum.hasMoreElements()) { + result = -1; + } + return result; + } + + /* + * ------------------------------------------------------------------- + * Methods override parent class Object + * ------------------------------------------------------------------- + */ + + /** + * Calculate the hashcode of this CompoundName by summing + * the hashcodes of all of its elements. + *

+ * If jndi.syntax.trimblanks is set to true then remove any leading and + * trailing blanks from the elements before calculating the hashcode.

+ *

+ * If jndi.syntax.ignorecase is set to true then use the lowercase version + * of the element to calculate its hashcode.

+ * + * @return the hashcode of this object. + */ + public int hashCode() { + int result = 0; + Enumeration enumeration = elem.elements(); + while (enumeration.hasMoreElements()) { + result += preProcess((String) enumeration.nextElement(), + ignoreCase, trimBlanks).hashCode(); + } + return result; + } + + /** + * Gets the string representation of this CompoundName. + *

+ * This is generated by concatenating the elements together with the + * separator string added as the separator between each of them. It may be + * necessary to add quotes and escape string to preserve the meaning. + * The resulting string should produce an equivalent CompoundName + * when used to create a new instance.

+ * + * @return the string representation of this CompoundName. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + String begin = NULL_STRING.equals(beginQuoteString) ? beginQuoteString2 + : beginQuoteString; + String end = NULL_STRING.equals(endQuoteString) ? endQuoteString2 + : endQuoteString; + String separator = NULL_STRING.equals(separatorString) ? separatorString2 + : separatorString; + if(RIGHT_TO_LEFT.equals(direction)){ + for (int i = elem.size() - 1; i >= 0; i--) { + addElement(sb, i, separator, begin, end); + } + }else{ + for (int i = 0; i < elem.size(); i++) { + addElement(sb, i, separator, begin, end); + } + } + if (size() * separator.length() < sb.length()) { + //if the name contains non-empty element, delete the last separator char, which is abundant + sb.setLength(sb.length() - separator.length()); + } + return sb.toString(); + } + + private void addElement(StringBuffer sb, int index, String separator, String begin, String end) { + String elemString = elem.get(index); + if (0 == elemString.length()) { + // if empty element, append a separator and continue + sb.append(separator); + return; + } + int pos = sb.length(); + sb.append(elemString); + if (!NULL_STRING.equals(begin) && !NULL_STRING.equals(end) + && !NULL_STRING.equals(separator) + && (0 <= elemString.indexOf(separator))) { + // if contains separator string, quoted it + sb.insert(pos, begin); + pos += begin.length(); + // if quoted, then every endquote char must be escaped + for (int i = 0, j = 0; 0 <= (j = elemString.indexOf(end, i)); i = j + + end.length()) { + sb.insert(pos + j, escapeString); + pos += escapeString.length(); + } + sb.append(end); + } else { + if (startsWithFromPos(elemString, 0, beginQuoteString) + || startsWithFromPos(elemString, 0, beginQuoteString2)) { + // if not quoted and start with begin quote string, escape it + sb.insert(pos, escapeString); + pos += escapeString.length(); + } + // if not quoted, escape all separator string and all escape string + for (int i = 0; i < elemString.length();) { + if (startsWithFromPos(elemString, i, separatorString)) { + sb.insert(pos + i, escapeString); + pos += escapeString.length(); + i += separatorString.length(); + } else if (startsWithFromPos(elemString, i, separatorString2)) { + sb.insert(pos + i, escapeString); + pos += escapeString.length(); + i += separatorString2.length(); + } else if (startsWithFromPos(elemString, i, escapeString)) { + sb.insert(pos + i, escapeString); + pos += escapeString.length(); + i += escapeString.length(); + } else { + i++; + } + } + } + sb.append(separator); + } + + /** + * Check if the supplied object o is equal to this + * CompoundName. + *

+ * The supplied Object o may be null but that will cause + * false to be returned. + *

+ *

+ * The supplied Object o may be something other than a + * CompoundName but that will cause false to be returned. + *

+ *

+ * To be equal the supplied CompoundName must have the same + * number of elements and each element must match the corresponding element + * of this CompoundName. The properties + * jndi.syntax.ignorecase and jndi.syntax.trimblanks need to be considered + * if they have been set. + *

+ *

+ * The properties associated with the CompoundName must be + * taken into account but do not have to match. For example + * "home/jenningm-abc/.profile" with a direction of left to right is equal + * to ".profile/jenningm-abc/home" with a direction of right to left. + *

+ * + * @param o + * the object to be compared + * @return true if supplied object o is equals to this + * CompoundName, false otherwise + */ + public boolean equals(Object o) { + if (!(o instanceof CompoundName)) { + return false; + } + + //compare size + CompoundName otherName = (CompoundName) o; + final int size = otherName.size(); + if (size != this.size()) { + return false; + } + + //compare every element + return equals(otherName, 0, size); + } + + /* + * compare this name to the supplied name from position + * start to position start+ + * length-1 + * + */ + private boolean equals(Name name, int start, int length) { + if (length > this.size()) { + return false; + } + CompoundName otherName = (CompoundName) name; + Enumeration otherEnum = otherName.getAll(); + String thisElement; + String otherElement; + for (int i = 0; i < length; i++) { + thisElement = preProcess((String) elem.get(i + start), ignoreCase, + trimBlanks); + otherElement = preProcess((String) otherEnum.nextElement(), + ignoreCase, trimBlanks); + if (!(null == thisElement ? null == otherElement : thisElement + .equals(otherElement))) { + return false; + } + } + return true; + } +} + +/* + * A useless class requied to satisfy the requirement for an 'impl' + * field (see above). + */ +class NameImpl { + protected NameImpl() { + super(); + } +} + + Propchange: incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/CompoundName.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/harmony/enhanced/classlib/trunk/modules/jndi/src/main/java/javax/naming/ConfigurationException.java ------------------------------------------------------------------------------ svn:eol-style = native