| /* |
| * 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.io.OptionalDataException; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Vector; |
| |
| import org.apache.harmony.jndi.internal.nls.Messages; |
| |
| /** |
| * A <code>CompositeName</code> represents a name in a naming service which |
| * spans multiple namespaces. For example the name "www.eclipse.org/index.html" |
| * spans the DNS and file system namespaces. |
| * <p> |
| * A <code>CompositeName</code> is a series of string elements. A composite |
| * name has a sequence of zero or more elements delimited by the '/' char. Each |
| * element can be accessed using its position. The first element is at position |
| * 0. |
| * </p> |
| * <p> |
| * A <code>CompositeName</code> may be empty. An empty composite name has no |
| * elements. Elements may also be empty. |
| * </p> |
| * <p> |
| * <code>CompositeName</code>s are read from left to right unlike |
| * <code>CompoundName</code>s which may have their direction of ordering |
| * specified by properties. |
| * </p> |
| * <p> |
| * Special characters are as follows: |
| * </p> |
| * <ul> |
| * <li>The separator is /</li> |
| * <li>The escape character is \</li> |
| * <li>Quotes can be used - both single quotes and double quotes are allowed. |
| * This allows you to quote strings which contain chars such as / which are part |
| * of a <code>CompositeName</code> element to avoid them being read as a |
| * separator.</li> |
| * </ul> |
| * <p> |
| * See the examples for further clarification. |
| * </p> |
| * <p> |
| * Some Examples:<br /> |
| * ============== |
| * </p> |
| * <p> |
| * The composite name "www.eclipse.org/index.html" has 2 elements. |
| * "www.eclipse.org" is a name from the DNS namespace. "index.html" is a name |
| * from the file system namespace. |
| * </p> |
| * <p> |
| * Another example of a composite name is: "www.eclipse.org/org/index.html". |
| * This name has 3 elements "www.eclipse.org", "org" and "index.html". |
| * www.eclipse.org is a name from the DNS namespace. The last 2 elements are |
| * each from the file system namespace. |
| * </p> |
| * <p> |
| * Some more examples to clarify empty names and elements: |
| * </p> |
| * <p> |
| * An empty CompositeName is the name "" and has no elements. |
| * </p> |
| * <p> |
| * A CompositeName with just one empty element is the name "/". |
| * </p> |
| * <p> |
| * The name "/org/" has 3 elements. The first and last are empty. |
| * </p> |
| * <p> |
| * The name "/a" has 2 elements. The first element is empty and the second |
| * element is "a". |
| * </p> |
| * <p> |
| * The name "a//a" has 3 elements. The middle element is empty and the first & |
| * third elements are both "a". |
| * </p> |
| * <p> |
| * The name "a/'b/a" is invalid as there is no closing quote for the ' |
| * character. |
| * </p> |
| * <p> |
| * The name "a/'a/b/b" is invalid as there is no closing quote for the ' |
| * character. |
| * </p> |
| * <p> |
| * The name "a/\"b/a" is interpreted as a/"b/a and is invalid as there is no |
| * closing quote for the embedded escaped " character. |
| * </p> |
| * <p> |
| * The name "a/'b/c'/a" has 3 elements. The middle element is b/c. |
| * <p> |
| * The name "a/a'a/b'/b" has 4 elements: Element 0 is "a". Element 1 is "a'a". |
| * Element 2 is "b'". Element 3 is "b". |
| * </p> |
| * <p> |
| * Interestingly the name "a/a'a/b/b" is valid and has 4 elements. This is |
| * because the single quote char ' is not a leading quote and is embedded in an |
| * element so is treated as a character. Element 0 is "a". Element 1 is "a'a". |
| * Element 2 is "b". Element 3 is "b". |
| * </p> |
| * <p> |
| * The name "\"abcd" gives an <code>InvalidNameException</code> as there is no |
| * closing quote. |
| * </p> |
| * <p> |
| * The name "'\"abcd'" gives one element of value "abcd. |
| * </p> |
| * <p> |
| * The name "\\abcd" gives one element of value \abcd. |
| * </p> |
| * <p> "" 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. |
| * </p> |
| */ |
| public class CompositeName implements Name { |
| |
| private static final long serialVersionUID = 1667768148915813118L; |
| |
| // status used by parse() |
| private static final int OUT_OF_QUOTE = 0; |
| |
| private static final int IN_SINGLE_QUOTE = 1; |
| |
| private static final int IN_DOUBLE_QUOTE = 2; |
| |
| private static final int QUOTE_ENDED = 3; |
| |
| /* a list holding elements */ |
| private transient Vector<String> elems; |
| |
| /** |
| * Private copy constructor. |
| * |
| * @param elements |
| * a list of name elements |
| */ |
| private CompositeName(List<String> elements) { |
| super(); |
| elems = new Vector<String>(elements); |
| } |
| |
| /** |
| * Construct a composite name with given elements. |
| * |
| * @param elements |
| * an enumeration of name elements |
| */ |
| protected CompositeName(Enumeration<String> elements) { |
| super(); |
| elems = new Vector<String>(); |
| while (elements.hasMoreElements()) { |
| elems.add(elements.nextElement()); |
| } |
| } |
| |
| /** |
| * Default constructor, creates an empty name with zero elements. |
| */ |
| public CompositeName() { |
| super(); |
| elems = new Vector<String>(); |
| } |
| |
| /** |
| * This constructor takes the supplied name and breaks it down into its |
| * elements. |
| * |
| * @param name |
| * a string containing the full composite name |
| * @throws InvalidNameException |
| * if the supplied name is invalid |
| */ |
| public CompositeName(String name) throws InvalidNameException { |
| super(); |
| elems = parseName(name); |
| } |
| |
| /** |
| * Parse string name elements. Delimiter is "/". Escape is "\" and both |
| * single quote and double quote are supported. |
| */ |
| private static Vector<String> parseName(String name) |
| throws InvalidNameException { |
| Vector<String> result = new Vector<String>(); |
| // special case: all '/', means same number of empty elements |
| if (isAllSlash(name)) { |
| int length = name.length(); |
| for (int index = 0; index < length; index++) { |
| result.add(""); //$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| // general simple case, without escape and quote |
| if (name.indexOf('"') < 0 && name.indexOf('\'') < 0 |
| && name.indexOf('\\') < 0) { |
| int i = 0, j = 0; |
| while ((j = name.indexOf('/', i)) >= 0) { |
| result.add(name.substring(i, j)); |
| i = j + 1; |
| } |
| result.add(name.substring(i)); |
| return result; |
| } |
| |
| // general complicated case, consider escape and quote |
| char curC, nextC; |
| char chars[] = name.toCharArray(); |
| StringBuilder buf = new StringBuilder(); |
| int status = OUT_OF_QUOTE; |
| for (int index = 0; index < chars.length; index++) { |
| curC = chars[index]; |
| |
| // check end quote violation |
| if (status == QUOTE_ENDED) { |
| if (curC == '/') { |
| result.add(buf.toString()); |
| buf.setLength(0); |
| status = OUT_OF_QUOTE; |
| continue; |
| } |
| // jndi.0C=End quote is not at the end of element |
| throw new InvalidNameException(Messages.getString("jndi.0C")); //$NON-NLS-1$ |
| } |
| |
| if (curC == '\\') { |
| // escape char |
| try { |
| nextC = chars[++index]; |
| if (nextC == '\\' || nextC == '\'' || nextC == '"' |
| || nextC == '/') { |
| buf.append(nextC); |
| } else { |
| buf.append(curC); |
| buf.append(nextC); |
| } |
| } catch (ArrayIndexOutOfBoundsException e) { |
| // jndi.0D=Escape cannot be at the end of element |
| throw new InvalidNameException(Messages |
| .getString("jndi.0D")); //$NON-NLS-1$ |
| } |
| continue; |
| } |
| if (curC != '/' && curC != '"' && curC != '\'') { |
| // normal char |
| buf.append(curC); |
| continue; |
| } |
| |
| // special char |
| if (status == OUT_OF_QUOTE && curC == '/') { |
| result.add(buf.toString()); |
| buf.setLength(0); |
| } else if (status == OUT_OF_QUOTE && curC == '\'' |
| && buf.length() == 0) { |
| status = IN_SINGLE_QUOTE; |
| } else if (status == OUT_OF_QUOTE && curC == '"' |
| && buf.length() == 0) { |
| status = IN_DOUBLE_QUOTE; |
| } else if (status == IN_SINGLE_QUOTE && curC == '\'') { |
| status = QUOTE_ENDED; |
| } else if (status == IN_DOUBLE_QUOTE && curC == '"') { |
| status = QUOTE_ENDED; |
| } else { |
| buf.append(curC); |
| } |
| } |
| result.add(buf.toString()); |
| |
| // check end status |
| if (status != OUT_OF_QUOTE && status != QUOTE_ENDED) { |
| // jndi.0E=Wrong quote usage. |
| throw new InvalidNameException(Messages.getString("jndi.0E")); //$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| private static boolean isAllSlash(String name) { |
| char[] nameChars = name.toCharArray(); |
| for (int index = 0; index < nameChars.length; index++) { |
| if (nameChars[index] != '/') { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /* |
| * Format name elements to its string representation. |
| */ |
| private static String formatName(Vector<String> elems) { |
| StringBuilder sb = new StringBuilder(); |
| int elemSize = elems.size(); |
| if (isAllEmptyElements(elems)) { |
| // special case: all empty elements |
| for (int index = 0; index < elemSize; index++) { |
| sb.append('/'); |
| } |
| return sb.toString(); |
| } |
| |
| // general case |
| String elem = null; |
| for (int index = 0; index < elemSize; index++) { |
| elem = elems.get(index); |
| if (index > 0) { |
| sb.append('/'); //$NON-NLS-1$ |
| } |
| // Add quotation while elem contains separator char |
| if (elem.indexOf('/') != -1) { |
| sb.append('\"'); |
| sb.append(elem); |
| sb.append('\"'); |
| } else { |
| sb.append(elem); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| private static boolean isAllEmptyElements(Vector<String> elems) { |
| int elemSize = elems.size(); |
| for (int index = 0; index < elemSize; index++) { |
| if (elems.get(index).length() > 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public Enumeration<String> getAll() { |
| return elems.elements(); |
| } |
| |
| public String get(int index) { |
| return elems.get(index); |
| } |
| |
| public Name getPrefix(int index) { |
| if (index < 0 || index > elems.size()) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| return new CompositeName(elems.subList(0, index)); |
| } |
| |
| public Name getSuffix(int index) { |
| if (index < 0 || index > elems.size()) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| return new CompositeName(elems.subList(index, elems.size())); |
| } |
| |
| public Name addAll(Name name) throws InvalidNameException { |
| if (null == name) { |
| throw new NullPointerException(); |
| } |
| if (!(name instanceof CompositeName)) { |
| // jndi.0F=Must be a CompositeName |
| throw new InvalidNameException(Messages.getString("jndi.0F")); //$NON-NLS-1$ |
| } |
| |
| Enumeration<String> enumeration = name.getAll(); |
| while (enumeration.hasMoreElements()) { |
| elems.add(enumeration.nextElement()); |
| } |
| return this; |
| } |
| |
| public Name addAll(int index, Name name) throws InvalidNameException { |
| if (null == name) { |
| throw new NullPointerException(); |
| } |
| if (!(name instanceof CompositeName)) { |
| // jndi.0F=Must be a CompositeName |
| throw new InvalidNameException(Messages.getString("jndi.0F")); //$NON-NLS-1$ |
| } |
| |
| if (index < 0 || index > elems.size()) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| Enumeration<String> enumeration = name.getAll(); |
| while (enumeration.hasMoreElements()) { |
| elems.add(index++, enumeration.nextElement()); |
| } |
| return this; |
| } |
| |
| public Name add(String element) throws InvalidNameException { |
| elems.add(element); |
| return this; |
| } |
| |
| public Name add(int index, String element) throws InvalidNameException { |
| if (index < 0 || index > elems.size()) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| elems.add(index, element); |
| return this; |
| } |
| |
| public Object remove(int index) throws InvalidNameException { |
| if (index < 0 || index >= elems.size()) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| return elems.remove(index); |
| } |
| |
| public int size() { |
| return elems.size(); |
| } |
| |
| public boolean isEmpty() { |
| return elems.isEmpty(); |
| } |
| |
| public boolean startsWith(Name name) { |
| if (!(name instanceof CompositeName)) { |
| return false; |
| } |
| // check size |
| if (name.size() > elems.size()) { |
| return false; |
| } |
| // compare 1 by 1 |
| Enumeration<String> enumeration = name.getAll(); |
| for (int index = 0; enumeration.hasMoreElements(); index++) { |
| if (!elems.get(index).equals(enumeration.nextElement())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public boolean endsWith(Name name) { |
| if (!(name instanceof CompositeName)) { |
| return false; |
| } |
| // check size |
| if (name.size() > elems.size()) { |
| return false; |
| } |
| |
| // compare 1 by 1 |
| Enumeration<String> enumeration = name.getAll(); |
| for (int index = elems.size() - name.size(); enumeration |
| .hasMoreElements(); index++) { |
| if (!elems.get(index).equals(enumeration.nextElement())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Compare this <code>Name</code> with the one supplied as a parameter. |
| * The elements of the names are compared in the same way as strings are |
| * compared to determine whether this <code>CompositeName</code> is less |
| * than, greater than or equal to the supplied object <code>o</code>. |
| * |
| * @param o |
| * the object to compare, cannot be null |
| * @return a negative number means this is less than the supplied object; a |
| * positive number means this is greater than the supplied object; |
| * zero means this CompositeName equals the object as specified in |
| * the description for the equals method of |
| * <code>CompositeName</code>. |
| * @throws ClassCastException |
| * when <code>o</code> is not a <code>CompositeName</code>. |
| */ |
| public int compareTo(Object o) { |
| if (o == this) { |
| return 0; |
| } |
| if (!(o instanceof CompositeName)) { |
| throw new ClassCastException(); |
| } |
| |
| Iterator<String> thisIter = elems.iterator(); |
| Iterator<String> thatIter = ((CompositeName) o).elems.iterator(); |
| int compareResult; |
| while (thisIter.hasNext() && thatIter.hasNext()) { |
| compareResult = thisIter.next().compareTo(thatIter.next()); |
| if (0 != compareResult) { |
| return compareResult; |
| } |
| } |
| if (thisIter.hasNext()) { |
| return 1; |
| } |
| if (thatIter.hasNext()) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * Create a copy of this composite name, a complete (deep) copy of the |
| * object. |
| * |
| * @return a complete (deep) copy of the object. |
| */ |
| @Override |
| public Object clone() { |
| return new CompositeName(elems); |
| } |
| |
| /** |
| * Returns the string representation of this <code>CompositeName</code>. |
| * This is generated by concatenating the elements together with the '/' |
| * char added as the separator between each of them. It may be necessary to |
| * add quotes and escape chars to preserve the meaning. The resulting string |
| * should produce an equivalent <code>CompositeName</code> when used to |
| * create a new instance. |
| * |
| * @return the string representation of this composite name. |
| */ |
| @Override |
| public String toString() { |
| return formatName(elems); |
| } |
| |
| /** |
| * Check if this <code>CompositeName</code> is equal to the supplied |
| * object. |
| * |
| * @param o |
| * the <code>CompositeName</code> to compare - can be null but |
| * then returns false. |
| * @return true if they have the same number of elements all of which are |
| * equal. false if they are not equal. |
| */ |
| @Override |
| public boolean equals(Object o) { |
| // check type |
| if (!(o instanceof CompositeName)) { |
| return false; |
| } |
| CompositeName that = (CompositeName) o; |
| if (this.size() != that.size()) { |
| return false; |
| } |
| |
| Iterator<String> thisIter = elems.iterator(); |
| Iterator<String> thatIter = that.elems.iterator(); |
| while (thisIter.hasNext() && thatIter.hasNext()) { |
| if (!thisIter.next().equals(thatIter.next())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Calculate the hashcode of this <code>CompositeName</code> by summing |
| * the hash codes of all of its elements. |
| * |
| * @return the hashcode of this object. |
| */ |
| @Override |
| public int hashCode() { |
| int hashCode = 0; |
| for (int index = 0; index < elems.size(); index++) { |
| hashCode += elems.get(index).hashCode(); |
| } |
| return hashCode; |
| } |
| |
| /** |
| * Writes a serialized representation of the CompositeName. It starts with |
| * an int which is the number of elements in the name, and is followed by a |
| * String for each element. |
| * |
| * @param oos |
| * @throws IOException |
| * if an error is encountered writing to the stream. |
| */ |
| private void writeObject(ObjectOutputStream oos) throws IOException { |
| oos.defaultWriteObject(); |
| |
| oos.writeInt(elems.size()); |
| for (Object element : elems) { |
| oos.writeObject(element); |
| } |
| } |
| |
| /** |
| * Recreate a CompositeName from the data in the supplied stream. |
| * |
| * @param ois |
| * @throws IOException |
| * if an error is encountered reading from the stream. |
| * @throws ClassNotFoundException. |
| */ |
| private void readObject(ObjectInputStream ois) |
| throws OptionalDataException, ClassNotFoundException, IOException { |
| ois.defaultReadObject(); |
| |
| int size = ois.readInt(); |
| elems = new Vector<String>(); |
| for (int i = 0; i < size; i++) { |
| elems.add((String) ois.readObject()); |
| } |
| } |
| } |