| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.Enumeration; |
| import java.util.Iterator; |
| import java.util.Properties; |
| import java.util.Vector; |
| |
| import org.apache.harmony.jndi.internal.nls.Messages; |
| |
| /** |
| * A <code>CompoundName</code> 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. |
| * <p> |
| * A <code>CompoundName</code> 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. |
| * </p> |
| * <p> |
| * 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 <code>IllegalArgumentException</code> should be |
| * raised. |
| * </p> |
| * <p> |
| * 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. |
| * </p> |
| * <p> |
| * There are other properties which affect the syntax of a |
| * <code>CompoundName</code>. The following properties are all optional: |
| * <ul> |
| * <li>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</li> |
| * <li>jndi.syntax.beginquote - Used as start of quoted string (Defaults to |
| * endquote)</li> |
| * <li>jndi.syntax.endquote - Used as end of quoted string (Defaults to |
| * beginquote)</li> |
| * <li>jndi.syntax.beginquote2 - Additionally used as start of quoted string |
| * (Defaults to endquote2)</li> |
| * <li>jndi.syntax.endquote2 - Additionally used as end of quoted string |
| * (Defaults to beginquote2)</li> |
| * </ul> |
| * <p> |
| * 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. |
| * </p> |
| * <p> |
| * If the element does not start with a quote, then any quote strings within |
| * that element are just normal characters. |
| * </p> |
| * <p> |
| * <ul> |
| * <li>jndi.syntax.ignorecase - If 'true' then ignore case when name elements |
| * are compared. If false or not set then case is important.</li> |
| * <li>jndi.syntax.trimblanks - If 'true' then ignore leading & trailing blanks |
| * when name elements are compared. If false or not set then blanks are |
| * important.</li> |
| * </ul> |
| * </p> |
| * <p> |
| * These 2 properties relate to names where the syntax includes |
| * attribute/content pairs. |
| * <ul> |
| * <li>jndi.syntax.separator.ava</li> |
| * <li>jndi.syntax.separator.typeval</li> |
| * </ul> |
| * 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. |
| * </p> |
| * <p> |
| * The jndi.syntax.separator.ava is not used when manipulating |
| * <code>CompoundName</code>. The jndi.syntax.separator is still used to |
| * separate elements. |
| * </p> |
| * <p> |
| * The <code>CompoundName</code> 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. |
| * </p> |
| * <p> |
| * Consider the string "CN=$Mandy Jennings, O=Apache, C=UK" with |
| * <ul> |
| * <li>jndi.syntax.direction set to "right_to_left"</li> |
| * <li>jndi.syntax.separator set to ","</li> |
| * <li>jndi.syntax.separator.typeval set to "="</li> |
| * </ul> |
| * When no jndi.syntax.beginquote is set then this creates a valid |
| * <code>CompoundName</code> with 3 elements. |
| * </p> |
| * <p> |
| * 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. |
| * </p> |
| * <p> |
| * The string "CN=$Mandy Jennings$, O=Apache, C=UK" would be fine as the $ |
| * quotes round Mandy Jennings now balance. |
| * </p> |
| * <p> |
| * A <code>CompoundName</code> may be empty. An empty <code>CompoundName</code> |
| * has no elements. Elements may also be empty. |
| * </p> |
| * |
| * <pre> |
| * 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 |
| * <code> |
| * CompoundName |
| * </code> |
| * is the name "" and has no elements. |
| * |
| * When jndi.syntax.beginquote is set to " and beginquote2 is set to ' |
| * the behaviour is similar 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 |
| * </pre> |
| */ |
| public class CompoundName implements Name { |
| |
| /* |
| * 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 overridden. |
| */ |
| private static final long serialVersionUID = 3513100557083972036L; |
| |
| // 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; |
| |
| // 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<String> elems; |
| |
| // 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 |
| * complaint we have implemented this as a useless class (below). |
| */ |
| protected transient javax.naming.NameImpl impl = new NameImpl(); |
| |
| /** |
| * Constructs a <code>CompoundName</code> with supplied |
| * <code>Enumeration</code> and <code>Properties</code> |
| * |
| * @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<String> elements, Properties props) { |
| if (null == props || null == elements) { |
| throw new NullPointerException(); |
| } |
| init(props); |
| this.elems = new Vector<String>(); |
| while (elements.hasMoreElements()) { |
| this.elems.add(elements.nextElement()); |
| } |
| } |
| |
| /** |
| * Constructs a <code>CompoundName</code> with supplied <code>String</code> |
| * and <code>Properties</code>, taking the supplied <code>s</code> 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 <code>String s</code> is invalid |
| * @throws NullPointerException |
| * thrown if the supplied <code>String s</code> 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 = props; |
| 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 threw |
| if (!LEFT_TO_RIGHT.equals(direction) |
| && !RIGHT_TO_LEFT.equals(direction) && !FLAT.equals(direction)) { |
| // jndi.04=Illegal direction property value, which must be one of |
| // right_to_left, left_to_right or flat |
| throw new IllegalArgumentException(Messages.getString("jndi.04")); //$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) { |
| // jndi.05=jndi.syntax.separator property must be set when |
| // jndi.syntax.direction is not flat |
| throw new IllegalArgumentException(Messages.getString("jndi.05")); //$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.elems = new Vector<String>(); |
| if ("".equals(s)) { //$NON-NLS-1$ |
| // if empty string, return empty vector |
| return; |
| } |
| |
| // init variables |
| int status = INIT_STATUS; |
| StringBuilder element = new StringBuilder(); |
| 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 |
| // jndi.06=The {0} cannot be at end of the component |
| throw new InvalidNameException(Messages.getString( |
| "jndi.06", escapeString)); //$NON-NLS-1$ |
| } |
| // 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) { |
| // jndi.07={0}: close quote must appears at end of component in |
| // quoted string |
| throw new InvalidNameException(Messages.getString("jndi.07", s)); //$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 { |
| // jndi.08={0}: close quote is required for quoted string |
| throw new InvalidNameException(Messages.getString("jndi.08", s)); //$NON-NLS-1$ |
| } |
| if (!hasNotNullElement) { |
| elems.remove(elems.size() - 1); |
| } |
| } |
| |
| /* |
| * add des parameter to StringBuilder if include is true |
| */ |
| private int addBuffer(StringBuilder 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(StringBuilder element) { |
| if (LEFT_TO_RIGHT == direction) { |
| elems.add(element.toString()); |
| } else { |
| elems.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 |
| */ |
| 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; |
| } |
| |
| public Enumeration<String> getAll() { |
| return this.elems.elements(); |
| } |
| |
| public String get(int index) { |
| validateIndex(index, false); |
| return elems.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 > elems.size() |
| || (!isInclude && index == elems.size())) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| } |
| |
| public Name getPrefix(int index) { |
| validateIndex(index, true); |
| return new CompoundName(new Vector<String>(elems.subList(0, index)) |
| .elements(), mySyntax); |
| } |
| |
| public Name getSuffix(int index) { |
| if (index == elems.size()) { |
| return new CompoundName(new Vector<String>().elements(), mySyntax); |
| } |
| validateIndex(index, false); |
| return new CompoundName(new Vector<String>(elems.subList(index, elems |
| .size())).elements(), mySyntax); |
| } |
| |
| public Name addAll(Name name) throws InvalidNameException { |
| return addAll(elems.size(), name); |
| } |
| |
| public Name addAll(int index, Name name) throws InvalidNameException { |
| if (name == null) { |
| // jndi.00=name must not be null |
| throw new NullPointerException(Messages.getString("jndi.00")); //$NON-NLS-1$ |
| } |
| if (!(name instanceof CompoundName)) { |
| // jndi.09={0} is not a compound name. |
| throw new InvalidNameException(Messages.getString( |
| "jndi.09", name.toString())); //$NON-NLS-1$ |
| } |
| if (FLAT.equals(direction) && (this.size() + name.size() > 1)) { |
| // jndi.0A=A flat name can only have a single component |
| throw new InvalidNameException(Messages.getString("jndi.0A")); //$NON-NLS-1$ |
| } |
| validateIndex(index, true); |
| Enumeration<String> enumeration = name.getAll(); |
| while (enumeration.hasMoreElements()) { |
| elems.add(index++, enumeration.nextElement()); |
| } |
| return this; |
| } |
| |
| public Name add(String element) throws InvalidNameException { |
| if (element == null) { |
| // jndi.8C=component must not be null |
| throw new IllegalArgumentException(Messages.getString("jndi.8C")); //$NON-NLS-1$ |
| } |
| if (FLAT.equals(direction) && (size() > 0)) { |
| // jndi.0A=A flat name can only have a single component |
| throw new InvalidNameException(Messages.getString("jndi.0A")); //$NON-NLS-1$ |
| } |
| elems.add(element); |
| return this; |
| } |
| |
| /** |
| * Insert an element within this CompoundName at the specified index. |
| * |
| * @return this <code>CompoundName</code>. |
| * @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 |
| * <code>CompoundName</code> becoming invalid. |
| */ |
| public Name add(int index, String element) throws InvalidNameException { |
| if (element == null) { |
| // jndi.8C=component must not be null |
| throw new IllegalArgumentException(Messages.getString("jndi.8C")); //$NON-NLS-1$ |
| } |
| if (FLAT.equals(direction) && (size() > 0)) { |
| // jndi.0A=A flat name can only have a single component |
| throw new InvalidNameException(Messages.getString("jndi.0A")); //$NON-NLS-1$ |
| } |
| validateIndex(index, true); |
| elems.add(index, element); |
| return this; |
| } |
| |
| /** |
| * Delete an element from this <code>CompoundName</code>. |
| * |
| * @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 |
| * <code>CompoundName</code> becoming invalid. |
| */ |
| public Object remove(int index) throws InvalidNameException { |
| validateIndex(index, false); |
| return elems.remove(index); |
| } |
| |
| @Override |
| public Object clone() { |
| return new CompoundName(getAll(), mySyntax); |
| } |
| |
| public int size() { |
| return elems.size(); |
| } |
| |
| public boolean isEmpty() { |
| return elems.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); |
| int elemSize = elems.size(); |
| oos.writeInt(elemSize); |
| for (int i = 0; i < elemSize; i++) { |
| String element = elems.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(); |
| elems = new Vector<String>(); |
| for (int i = 0; i < size; i++) { |
| elems.add((String) ois.readObject()); |
| } |
| } |
| |
| /** |
| * Compare this <code>CompoundName</code> with the one supplied as a param. |
| * <p> |
| * See the definition of the <code>equals()</code> method to see how the |
| * direction, ignorecase and trimblanks properties affect the comparison of |
| * a <code>CompoundName</code>. Other than that the comparison is the same |
| * as that for a <code>CompositeName</code>. |
| * </p> |
| * |
| * @return a negative number means this is less than the supplied Object |
| * <code>o</code>. a positive number means this is greater than the |
| * supplied Object <code>o</code>. zero means the two objects are |
| * equal. |
| * @param o |
| * the object to compare - cannot be null. |
| * @throws ClassCastException |
| * when <code>o</code> is not a compatible class that can be |
| * compared or if the object to compare <code>o</code> is null. |
| */ |
| public int compareTo(Object o) { |
| if (o == this) { |
| return 0; |
| } |
| if (!(o instanceof CompoundName)) { |
| throw new ClassCastException(); |
| } |
| |
| Iterator<String> thisIter = elems.iterator(); |
| Iterator<String> thatIter = ((CompoundName) o).elems.iterator(); |
| int compareResult; |
| String thisString, thatString; |
| while (thisIter.hasNext() && thatIter.hasNext()) { |
| thisString = preProcess(thisIter.next(), ignoreCase, trimBlanks); |
| thatString = preProcess(thatIter.next(), ignoreCase, trimBlanks); |
| compareResult = thisString.compareTo(thatString); |
| if (0 != compareResult) { |
| return compareResult; |
| } |
| } |
| if (thisIter.hasNext()) { |
| return 1; |
| } |
| if (thatIter.hasNext()) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * Calculate the hashcode of this <code>CompoundName</code> by summing the |
| * hashcodes of all of its elements. |
| * <p> |
| * If jndi.syntax.trimblanks is set to true then remove any leading and |
| * trailing blanks from the elements before calculating the hashcode. |
| * </p> |
| * <p> |
| * If jndi.syntax.ignorecase is set to true then use the lowercase version |
| * of the element to calculate its hashcode. |
| * </p> |
| * |
| * @return the hashcode of this object. |
| */ |
| @Override |
| public int hashCode() { |
| int hashCode = 0; |
| Enumeration<String> enumeration = elems.elements(); |
| while (enumeration.hasMoreElements()) { |
| hashCode += preProcess(enumeration.nextElement(), ignoreCase, |
| trimBlanks).hashCode(); |
| } |
| return hashCode; |
| } |
| |
| /** |
| * Gets the string representation of this <code>CompoundName</code>. |
| * <p> |
| * 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 <code>CompoundName</code> |
| * when used to create a new instance. |
| * </p> |
| * |
| * @return the string representation of this <code>CompoundName</code>. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| String begin = NULL_STRING.equals(beginQuoteString) ? beginQuoteString2 |
| : beginQuoteString; |
| String end = NULL_STRING.equals(endQuoteString) ? endQuoteString2 |
| : endQuoteString; |
| String separator = NULL_STRING.equals(separatorString) ? separatorString2 |
| : separatorString; |
| int elemSize = elems.size(); |
| if (RIGHT_TO_LEFT.equals(direction)) { |
| for (int i = elemSize - 1; i >= 0; i--) { |
| addElement(sb, i, separator, begin, end); |
| } |
| } else { |
| for (int i = 0; i < elemSize; 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(StringBuilder sb, int index, String separator, |
| String begin, final String end) { |
| final String elemString = elems.get(index); |
| final int elemStringLength = elemString.length(); |
| if (0 == elemStringLength) { |
| // 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 |
| int endLenght = end.length(); |
| for (int i = 0, j = 0; 0 <= (j = elemString.indexOf(end, i)); i = j |
| + endLenght) { |
| 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 < elemStringLength;) { |
| 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 <code>o</code> is equal to this |
| * <code>CompoundName</code>. |
| * <p> |
| * The supplied <code>Object o</code> may be null but that will cause false |
| * to be returned. |
| * </p> |
| * <p> |
| * The supplied <code>Object o</code> may be something other than a |
| * <code>CompoundName</code> but that will cause false to be returned. |
| * </p> |
| * <p> |
| * To be equal the supplied <code>CompoundName</code> must have the same |
| * number of elements and each element must match the corresponding element |
| * of this <code>CompoundName</code>. The properties jndi.syntax.ignorecase |
| * and jndi.syntax.trimblanks need to be considered if they have been set. |
| * </p> |
| * <p> |
| * The properties associated with the <code>CompoundName</code> 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. |
| * </p> |
| * |
| * @param o |
| * the object to be compared |
| * @return true if supplied object <code>o</code> is equals to this |
| * <code>CompoundName</code>, false otherwise |
| */ |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof CompoundName)) { |
| return false; |
| } |
| |
| CompoundName otherName = (CompoundName) o; |
| int otherSize = otherName.size(); |
| if (otherSize != this.size()) { |
| return false; |
| } |
| |
| // compare every element |
| return equals(otherName, 0, otherSize); |
| } |
| |
| /** |
| * compare this name to the supplied <code>name</code> from position |
| * <code>start</code> to position <code>start</code>+ <code>length</code>-1 |
| */ |
| private boolean equals(Name name, int offset, int length) { |
| if (length > this.size()) { |
| return false; |
| } |
| CompoundName otherName = (CompoundName) name; |
| Enumeration<String> otherEnum = otherName.getAll(); |
| String thisElement, otherElement; |
| for (int i = 0; i < length; i++) { |
| thisElement = preProcess(elems.get(i + offset), ignoreCase, |
| trimBlanks); |
| otherElement = preProcess(otherEnum.nextElement(), ignoreCase, |
| trimBlanks); |
| if (!thisElement.equals(otherElement)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |