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 CompoundName(Properties syntax)71 private CompoundName (Properties syntax) 72 { 73 elts = new Vector<String> (); 74 mySyntax = syntax; 75 initializeSyntax (); 76 } 77 CompoundName(Enumeration<String> comps, Properties syntax)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 CompoundName(String n, Properties syntax)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 add(int posn, String comp)201 public Name add (int posn, String comp) throws InvalidNameException 202 { 203 elts.add (posn, comp); 204 return this; 205 } 206 add(String comp)207 public Name add (String comp) throws InvalidNameException 208 { 209 elts.add (comp); 210 return this; 211 } 212 addAll(int posn, Name n)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 addAll(Name suffix)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 clone()244 public Object clone () 245 { 246 return new CompoundName (elts.elements (), mySyntax); 247 } 248 compareTo(Object obj)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 endsWith(Name n)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 equals(Object obj)282 public boolean equals (Object obj) 283 { 284 if (! (obj instanceof CompoundName)) 285 return false; 286 return compareTo (obj) == 0; 287 } 288 get(int posn)289 public String get (int posn) 290 { 291 return elts.get (posn); 292 } 293 getAll()294 public Enumeration<String> getAll () 295 { 296 return elts.elements (); 297 } 298 getPrefix(int posn)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 getSuffix(int posn)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 hashCode()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 isEmpty()325 public boolean isEmpty () 326 { 327 return elts.isEmpty (); 328 } 329 remove(int posn)330 public Object remove (int posn) throws InvalidNameException 331 { 332 return elts.remove (posn); 333 } 334 size()335 public int size () 336 { 337 return elts.size (); 338 } 339 startsWith(Name n)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. isSpecial(String element, int offset)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 toString()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. canonicalize(String element)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. initializeSyntax()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 readObject(ObjectInputStream s)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 writeObject(ObjectOutputStream s)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 } 524