/* Copyright (C) 2001 Free Software Foundation This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ package javax.naming; import java.io.Serializable; import java.util.Enumeration; import java.util.Properties; import java.util.NoSuchElementException; import java.util.Vector; /** * @author Tom Tromey * @date May 16, 2001 * * FIXME: must write readObject and writeObject to conform to * serialization spec. * * FIXME: this class is underspecified. For instance, the `flat' * direction is never described. If it means that the CompoundName * can only have a single element, then the Enumeration-based * constructor ought to throw InvalidNameException. */ public class CompoundName implements Name, Cloneable, Serializable { private CompoundName (Properties syntax) { elts = new Vector (); mySyntax = syntax; initializeSyntax (); } protected CompoundName (Enumeration comps, Properties syntax) { elts = new Vector (); mySyntax = syntax; initializeSyntax (); try { while (comps.hasMoreElements ()) elts.add (comps.nextElement ()); } catch (NoSuchElementException ignore) { } } public CompoundName (String n, Properties syntax) throws InvalidNameException { elts = new Vector (); mySyntax = syntax; initializeSyntax (); StringBuffer new_element = new StringBuffer (); int i = 0; // QUOTE==null means no quoting right now. When it is set it is // the value of the closing quote. String quote = null; while (i < n.length ()) { String special = isSpecial (n, i); if (special == escape && escape != null) { if (n.length () == i + special.length ()) { // A trailing escape is treated as itself. new_element.append (special); i += special.length (); } else { String eSpecial = isSpecial (n, i + special.length ()); if (eSpecial != null) { // Treat the escape as an escape. new_element.append (eSpecial); i += special.length () + eSpecial.length (); } else { // Treat the escape as itself. new_element.append (special); i += special.length (); } continue; } } else if (quote != null) { // It is safe to use == here. if (quote == special) { // Quotes must surround a complete component. if (i + quote.length () < n.length () && ! n.startsWith (separator, i + quote.length ())) throw new InvalidNameException ("close quote before end of component"); elts.add (new_element.toString ()); new_element.setLength (0); i += quote.length (); quote = null; continue; } // Otherwise, fall through. } // Quotes are only special at the start of a component. else if (new_element.length () == 0 && special == beginQuote) { quote = endQuote; i += special.length (); continue; } else if (new_element.length () == 0 && special == beginQuote2) { quote = endQuote2; i += special.length (); continue; } else if (special == separator) { elts.add (new_element.toString ()); new_element.setLength (0); i += special.length (); continue; } // Nothing in particular, so try the next character. new_element.append (n.charAt (i)); ++i; } if (new_element.length () != 0) elts.add (new_element.toString ()); if (direction == RIGHT_TO_LEFT) { // Reverse the order of the elements. int len = elts.size (); for (i = 0; i < len / 2; ++i) { Object t = elts.set (i, elts.get (len - i - 1)); elts.set (len - i - 1, t); } } // Error checking. if (quote != null) throw new InvalidNameException ("unterminated quote"); } public Name add (int posn, String comp) throws InvalidNameException { elts.add (posn, comp); return this; } public Name add (String comp) throws InvalidNameException { elts.add (comp); return this; } public Name addAll (int posn, Name n) throws InvalidNameException { Enumeration e = n.getAll (); try { while (e.hasMoreElements ()) { elts.add (posn, e.nextElement ()); ++posn; } } catch (NoSuchElementException ignore) { } return this; } public Name addAll (Name suffix) throws InvalidNameException { Enumeration e = suffix.getAll (); try { while (e.hasMoreElements ()) elts.add (e.nextElement ()); } catch (NoSuchElementException ignore) { } return this; } public Object clone () { return new CompoundName (elts.elements (), mySyntax); } public int compareTo (Object obj) { if (obj == null || ! (obj instanceof CompoundName)) throw new ClassCastException ("CompoundName.compareTo() expected CompoundName"); CompoundName cn = (CompoundName) obj; int last = Math.min (cn.elts.size (), elts.size ()); for (int i = 0; i < last; ++i) { String f = canonicalize ((String) elts.get (i)); int comp = f.compareTo (canonicalize ((String) cn.elts.get (i))); if (comp != 0) return comp; } return elts.size () - cn.elts.size (); } public boolean endsWith (Name n) { if (! (n instanceof CompoundName)) return false; CompoundName cn = (CompoundName) n; if (cn.elts.size () > elts.size ()) return false; int delta = elts.size () - cn.elts.size (); for (int i = 0; i < cn.elts.size (); ++i) { String f = canonicalize ((String) elts.get (i)); if (! f.equals (canonicalize ((String) cn.elts.get (i)))) return false; } return true; } public boolean equals (Object obj) { if (! (obj instanceof CompoundName)) return false; return compareTo (obj) == 0; } public String get (int posn) { return (String) elts.get (posn); } public Enumeration getAll () { return elts.elements (); } public Name getPrefix (int posn) { CompoundName cn = new CompoundName (mySyntax); for (int i = 0; i < posn; ++i) cn.elts.add (elts.get (i)); return cn; } public Name getSuffix (int posn) { if (posn > elts.size ()) throw new ArrayIndexOutOfBoundsException (posn); CompoundName cn = new CompoundName (mySyntax); for (int i = posn; i < elts.size (); ++i) cn.elts.add (elts.get (i)); return cn; } public int hashCode () { int h = 0; for (int i = 0; i < elts.size (); ++i) h += canonicalize ((String) elts.get (i)).hashCode (); return h; } public boolean isEmpty () { return elts.isEmpty (); } public Object remove (int posn) throws InvalidNameException { return elts.remove (posn); } public int size () { return elts.size (); } public boolean startsWith (Name n) { if (! (n instanceof CompoundName)) return false; CompoundName cn = (CompoundName) n; if (cn.elts.size () > elts.size ()) return false; for (int i = 0; i < cn.elts.size (); ++i) { String f = canonicalize ((String) elts.get (i)); if (! f.equals (canonicalize ((String) cn.elts.get (i)))) return false; } return true; } // If ELEMENT starts with some meta-sequence at OFFSET, then return // the string representing the meta-sequence. Otherwise return // null. private String isSpecial (String element, int offset) { String special = null; if (separator != null && element.startsWith (separator, offset)) special = separator; else if (escape != null && element.startsWith (escape, offset)) special = escape; else if (beginQuote != null && element.startsWith (beginQuote, offset)) special = beginQuote; else if (endQuote != null && element.startsWith (endQuote, offset)) special = endQuote; else if (beginQuote2 != null && element.startsWith (beginQuote2, offset)) special = beginQuote2; else if (endQuote2 != null && element.startsWith (endQuote2, offset)) special = endQuote2; return special; } public String toString () { StringBuffer result = new StringBuffer (); int size = elts.size (); for (int i = 0; i < size; ++i) { // Find the appropriate element. FIXME: not clear what FLAT // means. int offset = (direction == RIGHT_TO_LEFT) ? (size - i - 1) : i; String element = (String) elts.get (offset); if (i > 0 || (i == size - 1 && element.equals (""))) result.append (separator); int k = 0; while (k < element.length ()) { String special = isSpecial (element, k); if (special != null) { result.append (escape); result.append (special); k += special.length (); } else { result.append (element.charAt (k)); ++k; } } } return result.toString (); } // This canonicalizes a String, based on the syntax, for comparison // or other similar purposes. private String canonicalize (String element) { String ret = element; if (ignoreCase) ret = ret.toLowerCase (); if (trimBlanks) { int first = 0; while (first < ret.length () && Character.isWhitespace (ret.charAt (first))) ++first; int last = ret.length () - 1; while (last >= first && Character.isWhitespace (ret.charAt (last))) --last; ret = ret.substring (first, last); } return ret; } // This initializes all the syntax variables. This seems easier // than re-querying the properties every time. We're allowed to do // this because the spec says that subclasses should consider the // syntax as being read-only. private void initializeSyntax () { String t = mySyntax.getProperty ("jndi.syntax.direction", "flat"); if (t.equals ("right_to_left")) this.direction = RIGHT_TO_LEFT; else if (t.equals ("left_to_right")) this.direction = LEFT_TO_RIGHT; else { // If we don't recognize it, default to flat. this.direction = FLAT; } // This is required unless the direction is FLAT. Unfortunately // there is no way to report this error. this.separator = mySyntax.getProperty ("jndi.syntax.separator", ""); this.ignoreCase = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.ignorecase", "false")).booleanValue (); this.escape = mySyntax.getProperty ("jndi.syntax.escape", null); this.beginQuote = mySyntax.getProperty ("jndi.syntax.beginquote", null); this.endQuote = mySyntax.getProperty ("jndi.syntax.endquote", this.beginQuote); this.beginQuote2 = mySyntax.getProperty ("jndi.syntax.beginquote2", null); this.endQuote2 = mySyntax.getProperty ("jndi.syntax.endquote2", this.beginQuote2); this.trimBlanks = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.trimblanks", "false")).booleanValue (); } // The spec specifies this but does not document it in any way (it // is a package-private class). It is useless as far as I can tell. // So we ignore it. // protected transient NameImpl impl; protected transient Properties mySyntax; // The actual elements. private transient Vector elts; // The following are all used for syntax. private transient int direction; private transient String separator; private transient boolean ignoreCase; private transient String escape; private transient String beginQuote; private transient String endQuote; private transient String beginQuote2; private transient String endQuote2; private transient boolean trimBlanks; // We didn't need these for parsing, so they are gone. // private transient String avaSeparator; // private transient String typevalSeparator; private static final int RIGHT_TO_LEFT = -1; private static final int LEFT_TO_RIGHT = 1; private static final int FLAT = 0; }