Source for javax.naming.CompoundName

   1: /* CompoundName.java --
   2:    Copyright (C) 2001, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.naming;
  40: 
  41: import java.io.IOException;
  42: import java.io.ObjectInputStream;
  43: import java.io.ObjectOutputStream;
  44: import java.io.Serializable;
  45: import java.util.Enumeration;
  46: import java.util.NoSuchElementException;
  47: import java.util.Properties;
  48: import java.util.Vector;
  49: 
  50: /**
  51:  * Represents hierarchical names from the single namespace. For instance,
  52:  * the path /home/audriusa/classpath/file.txt is the compound name, using
  53:  * the filesystem namespace. 
  54:  * 
  55:  * @author Tom Tromey (tromey@redhat.com)
  56:  * @date May 16, 2001
  57:  *
  58:  * FIXME: this class is underspecified.  For instance, the `flat'
  59:  * direction is never described.  If it means that the CompoundName
  60:  * can only have a single element, then the Enumeration-based
  61:  * constructor ought to throw InvalidNameException.
  62:  *
  63:  * @since 1.3
  64:  */
  65: public class CompoundName implements Name, Cloneable, Serializable
  66: {
  67:   private static final long serialVersionUID = 3513100557083972036L;
  68: 
  69:   private CompoundName (Properties syntax)
  70:   {
  71:     elts = new Vector<String> ();
  72:     mySyntax = syntax;
  73:     initializeSyntax ();
  74:   }
  75: 
  76:   protected CompoundName (Enumeration<String> comps, Properties syntax)
  77:   {
  78:     elts = new Vector<String> ();
  79:     mySyntax = syntax;
  80:     initializeSyntax ();
  81:     try
  82:       {
  83:     while (comps.hasMoreElements ())
  84:       elts.add (comps.nextElement ());
  85:       }
  86:     catch (NoSuchElementException ignore)
  87:       {
  88:       }
  89:   }
  90: 
  91:   public CompoundName (String n, Properties syntax)
  92:     throws InvalidNameException
  93:   {
  94:     elts = new Vector<String> ();
  95:     mySyntax = syntax;
  96:     initializeSyntax ();
  97: 
  98:     StringBuffer new_element = new StringBuffer ();
  99:     int i = 0;
 100:     // QUOTE==null means no quoting right now.  When it is set it is
 101:     // the value of the closing quote.
 102:     String quote = null;
 103:     while (i < n.length ())
 104:       {
 105:     String special = isSpecial (n, i);
 106: 
 107:     if (special == escape && escape != null)
 108:       {
 109:         if (n.length () == i + special.length ())
 110:           {
 111:         // A trailing escape is treated as itself.
 112:         new_element.append (special);
 113:         i += special.length ();
 114:           }
 115:         else
 116:           {
 117:         String eSpecial = isSpecial (n, i + special.length ());
 118:         if (eSpecial != null)
 119:           {
 120:             // Treat the escape as an escape.
 121:             new_element.append (eSpecial);
 122:             i += special.length () + eSpecial.length ();
 123:           }
 124:         else
 125:           {
 126:             // Treat the escape as itself.
 127:             new_element.append (special);
 128:             i += special.length ();
 129:           }
 130:         continue;
 131:           }
 132:       }
 133:     else if (quote != null)
 134:       {
 135:         // It is safe to use == here.
 136:         if (quote == special)
 137:           {
 138:         // Quotes must surround a complete component.
 139:         if (i + quote.length () < n.length ()
 140:             && ! n.startsWith (separator, i + quote.length ()))
 141:           throw new InvalidNameException ("close quote before end of component");
 142:         elts.add (new_element.toString ());
 143:         new_element.setLength (0);
 144:         i += quote.length ();
 145:         quote = null;
 146:         continue;
 147:           }
 148:         // Otherwise, fall through.
 149:       }
 150:     // Quotes are only special at the start of a component.
 151:     else if (new_element.length () == 0
 152:          && special == beginQuote
 153:          && beginQuote != null)
 154:       {
 155:         quote = endQuote;
 156:         i += special.length ();
 157:         continue;
 158:       }
 159:     else if (new_element.length () == 0
 160:          && special == beginQuote2
 161:          && beginQuote2 != null)
 162:       {
 163:         quote = endQuote2;
 164:         i += special.length ();
 165:         continue;
 166:       }
 167:     else if (direction != FLAT && special == separator)
 168:       {
 169:         elts.add (new_element.toString ());
 170:         new_element.setLength (0);
 171:         i += special.length ();
 172:         continue;
 173:       }
 174: 
 175:     // Nothing in particular, so try the next character.
 176:     new_element.append (n.charAt (i));
 177:     ++i;
 178:       }
 179: 
 180:     if (new_element.length () != 0)
 181:       elts.add (new_element.toString ());
 182: 
 183:     if (direction == RIGHT_TO_LEFT)
 184:       {
 185:     // Reverse the order of the elements.
 186:     int len = elts.size ();
 187:     for (i = 0; i < len / 2; ++i)
 188:       {
 189:         String t = elts.set (i, elts.get (len - i - 1));
 190:         elts.set (len - i - 1, t);
 191:       }
 192:       }
 193: 
 194:     // Error checking.
 195:     if (quote != null)
 196:       throw new InvalidNameException ("unterminated quote");
 197:   }
 198: 
 199:   public Name add (int posn, String comp) throws InvalidNameException
 200:   {
 201:     elts.add (posn, comp);
 202:     return this;
 203:   }
 204: 
 205:   public Name add (String comp) throws InvalidNameException
 206:   {
 207:     elts.add (comp);
 208:     return this;
 209:   }
 210: 
 211:   public Name addAll (int posn, Name n) throws InvalidNameException
 212:   {
 213:     Enumeration<String> e = n.getAll ();
 214:     try
 215:       {
 216:     while (e.hasMoreElements ())
 217:       {
 218:         elts.add (posn, e.nextElement ());
 219:         ++posn;
 220:       }
 221:       }
 222:     catch (NoSuchElementException ignore)
 223:       {
 224:       }
 225:     return this;
 226:   }
 227: 
 228:   public Name addAll (Name suffix) throws InvalidNameException
 229:   {
 230:     Enumeration<String> e = suffix.getAll ();
 231:     try
 232:       {
 233:     while (e.hasMoreElements ())
 234:       elts.add (e.nextElement ());
 235:       }
 236:     catch (NoSuchElementException ignore)
 237:       {
 238:       }
 239:     return this;
 240:   }
 241: 
 242:   public Object clone ()
 243:   {
 244:     return new CompoundName (elts.elements (), mySyntax);
 245:   }
 246: 
 247:   public int compareTo (Object obj)
 248:   {
 249:     if (! (obj instanceof CompoundName))
 250:       throw new ClassCastException ("CompoundName.compareTo() expected CompoundName");
 251:     CompoundName cn = (CompoundName) obj;
 252:     int last = Math.min (cn.elts.size (), elts.size ());
 253:     for (int i = 0; i < last; ++i)
 254:       {
 255:     String f = canonicalize (elts.get (i));
 256:     int comp = f.compareTo (canonicalize (cn.elts.get (i)));
 257:     if (comp != 0)
 258:       return comp;
 259:       }
 260:     return elts.size () - cn.elts.size ();
 261:   }
 262: 
 263:   public boolean endsWith (Name n)
 264:   {
 265:     if (! (n instanceof CompoundName))
 266:       return false;
 267:     CompoundName cn = (CompoundName) n;
 268:     if (cn.elts.size () > elts.size ())
 269:       return false;
 270:     int delta = elts.size () - cn.elts.size ();
 271:     for (int i = 0; i < cn.elts.size (); ++i)
 272:       {
 273:     String f = canonicalize (elts.get (delta + i));
 274:     if (! f.equals (canonicalize (cn.elts.get (i))))
 275:       return false;
 276:       }
 277:     return true;
 278:   }
 279: 
 280:   public boolean equals (Object obj)
 281:   {
 282:     if (! (obj instanceof CompoundName))
 283:       return false;
 284:     return compareTo (obj) == 0;
 285:   }
 286: 
 287:   public String get (int posn)
 288:   {
 289:     return elts.get (posn);
 290:   }
 291: 
 292:   public Enumeration<String> getAll ()
 293:   {
 294:     return elts.elements ();
 295:   }
 296: 
 297:   public Name getPrefix (int posn)
 298:   {
 299:     CompoundName cn = new CompoundName (mySyntax);
 300:     for (int i = 0; i < posn; ++i)
 301:       cn.elts.add (elts.get (i));
 302:     return cn;
 303:   }
 304: 
 305:   public Name getSuffix (int posn)
 306:   {
 307:     if (posn > elts.size ())
 308:       throw new ArrayIndexOutOfBoundsException (posn);
 309:     CompoundName cn = new CompoundName (mySyntax);
 310:     for (int i = posn; i < elts.size (); ++i)
 311:       cn.elts.add (elts.get (i));
 312:     return cn;
 313:   }
 314: 
 315:   public int hashCode ()
 316:   {
 317:     int h = 0;
 318:     for (int i = 0; i < elts.size (); ++i)
 319:       h += canonicalize (elts.get (i)).hashCode ();
 320:     return h;
 321:   }
 322: 
 323:   public boolean isEmpty ()
 324:   {
 325:     return elts.isEmpty ();
 326:   }
 327: 
 328:   public Object remove (int posn) throws InvalidNameException
 329:   {
 330:     return elts.remove (posn);
 331:   }
 332: 
 333:   public int size ()
 334:   {
 335:     return elts.size ();
 336:   }
 337: 
 338:   public boolean startsWith (Name n)
 339:   {
 340:     if (! (n instanceof CompoundName))
 341:       return false;
 342:     CompoundName cn = (CompoundName) n;
 343:     if (cn.elts.size () > elts.size ())
 344:       return false;
 345:     for (int i = 0; i < cn.elts.size (); ++i)
 346:       {
 347:     String f = canonicalize (elts.get (i));
 348:     if (! f.equals (canonicalize (cn.elts.get (i))))
 349:       return false;
 350:       }
 351:     return true;
 352:   }
 353: 
 354:   // If ELEMENT starts with some meta-sequence at OFFSET, then return
 355:   // the string representing the meta-sequence.  Otherwise return
 356:   // null.
 357:   private String isSpecial (String element, int offset)
 358:   {
 359:     String special = null;
 360:     if (separator != null && element.startsWith (separator, offset))
 361:       special = separator;
 362:     else if (escape != null && element.startsWith (escape, offset))
 363:       special = escape;
 364:     else if (beginQuote != null && element.startsWith (beginQuote, offset))
 365:       special = beginQuote;
 366:     else if (endQuote != null && element.startsWith (endQuote, offset))
 367:       special = endQuote;
 368:     else if (beginQuote2 != null
 369:          && element.startsWith (beginQuote2, offset))
 370:       special = beginQuote2;
 371:     else if (endQuote2 != null && element.startsWith (endQuote2, offset))
 372:       special = endQuote2;
 373: 
 374:     return special;
 375:   }
 376: 
 377:   public String toString ()
 378:   {
 379:     StringBuffer result = new StringBuffer ();
 380:     int size = elts.size ();
 381:     for (int i = 0; i < size; ++i)
 382:       {
 383:     // Find the appropriate element.  FIXME: not clear what FLAT
 384:     // means.
 385:     int offset = (direction == RIGHT_TO_LEFT) ? (size - i - 1) : i;
 386:     String element = elts.get (offset);
 387:     if (i > 0
 388:         || (i == size - 1 && element.equals ("")))
 389:       result.append (separator);
 390: 
 391:     int k = 0;
 392:     while (k < element.length ())
 393:       {
 394:         String special = isSpecial (element, k);
 395:         if (special != null)
 396:           {
 397:         result.append (escape);
 398:         result.append (special);
 399:         k += special.length ();
 400:           }
 401:         else
 402:           {
 403:         result.append (element.charAt (k));
 404:         ++k;
 405:           }
 406:       }
 407:       }
 408: 
 409:     return result.toString ();
 410:   }
 411: 
 412:   // This canonicalizes a String, based on the syntax, for comparison
 413:   // or other similar purposes.
 414:   private String canonicalize (String element)
 415:   {
 416:     String ret = element;
 417: 
 418:     if (ignoreCase)
 419:       ret = ret.toLowerCase ();
 420: 
 421:     if (trimBlanks)
 422:       {
 423:     int first = 0;
 424:     while (first < ret.length ()
 425:            && Character.isWhitespace (ret.charAt (first)))
 426:       ++first;
 427: 
 428:     int last = ret.length () - 1;
 429:     while (last >= first
 430:            && Character.isWhitespace (ret.charAt (last)))
 431:       --last;
 432: 
 433:     ret = ret.substring (first, last);
 434:       }
 435: 
 436:     return ret;
 437:   }
 438: 
 439:   // This initializes all the syntax variables.  This seems easier
 440:   // than re-querying the properties every time.  We're allowed to do
 441:   // this because the spec says that subclasses should consider the
 442:   // syntax as being read-only.
 443:   private void initializeSyntax ()
 444:   {
 445:     String t = mySyntax.getProperty ("jndi.syntax.direction", "flat");
 446:     if (t.equals ("right_to_left"))
 447:       this.direction = RIGHT_TO_LEFT;
 448:     else if (t.equals ("left_to_right"))
 449:       this.direction = LEFT_TO_RIGHT;
 450:     else
 451:       {
 452:     // If we don't recognize it, default to flat.
 453:     this.direction = FLAT;
 454:       }
 455: 
 456:     // This is required unless the direction is FLAT.  Unfortunately
 457:     // there is no way to report this error.
 458:     this.separator = mySyntax.getProperty ("jndi.syntax.separator", "");
 459: 
 460:     this.ignoreCase
 461:       = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.ignorecase",
 462:                            "false")).booleanValue ();
 463:     this.escape = mySyntax.getProperty ("jndi.syntax.escape", null);
 464:     this.beginQuote = mySyntax.getProperty ("jndi.syntax.beginquote", null);
 465:     this.endQuote = mySyntax.getProperty ("jndi.syntax.endquote",
 466:                       this.beginQuote);
 467:     this.beginQuote2 = mySyntax.getProperty ("jndi.syntax.beginquote2",
 468:                          null);
 469:     this.endQuote2 = mySyntax.getProperty ("jndi.syntax.endquote2",
 470:                        this.beginQuote2);
 471:     this.trimBlanks
 472:       = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.trimblanks",
 473:                            "false")).booleanValue ();
 474:   }
 475: 
 476:   private void readObject(ObjectInputStream s)
 477:     throws IOException, ClassNotFoundException
 478:   {
 479:     mySyntax = (Properties) s.readObject();
 480:     int count = s.readInt();
 481:     elts = new Vector<String>(count);
 482:     for (int i = 0; i < count; i++)
 483:       elts.addElement((String) s.readObject());
 484:   }
 485: 
 486:   private void writeObject(ObjectOutputStream s)
 487:     throws IOException
 488:   {
 489:     s.writeObject(mySyntax);
 490:     s.writeInt(elts.size());
 491:     for (int i = 0; i < elts.size(); i++)
 492:         s.writeObject(elts.elementAt(i));
 493:   }
 494: 
 495:   // The spec specifies this but does not document it in any way (it
 496:   // is a package-private class).  It is useless as far as I can tell.
 497:   // So we ignore it.
 498:   // protected transient NameImpl impl;
 499:   protected transient Properties mySyntax;
 500: 
 501:   // The actual elements.
 502:   private transient Vector<String> elts;
 503: 
 504:   // The following are all used for syntax.
 505:   private transient int direction;
 506:   private transient String separator;
 507:   private transient boolean ignoreCase;
 508:   private transient String escape;
 509:   private transient String beginQuote;
 510:   private transient String endQuote;
 511:   private transient String beginQuote2;
 512:   private transient String endQuote2;
 513:   private transient boolean trimBlanks;
 514:   // We didn't need these for parsing, so they are gone.
 515:   // private transient String avaSeparator;
 516:   // private transient String typevalSeparator;
 517: 
 518:   private static final int RIGHT_TO_LEFT = -1;
 519:   private static final int LEFT_TO_RIGHT = 1;
 520:   private static final int FLAT = 0;
 521: }