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