001/* CompoundName.java -- 002 Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.naming; 040 041import gnu.java.lang.CPStringBuilder; 042 043import java.io.IOException; 044import java.io.ObjectInputStream; 045import java.io.ObjectOutputStream; 046import java.io.Serializable; 047import java.util.Enumeration; 048import java.util.NoSuchElementException; 049import java.util.Properties; 050import java.util.Vector; 051 052/** 053 * Represents hierarchical names from the single namespace. For instance, 054 * the path /home/audriusa/classpath/file.txt is the compound name, using 055 * the filesystem namespace. 056 * 057 * @author Tom Tromey (tromey@redhat.com) 058 * @date May 16, 2001 059 * 060 * FIXME: this class is underspecified. For instance, the `flat' 061 * direction is never described. If it means that the CompoundName 062 * can only have a single element, then the Enumeration-based 063 * constructor ought to throw InvalidNameException. 064 * 065 * @since 1.3 066 */ 067public class CompoundName implements Name, Cloneable, Serializable 068{ 069 private static final long serialVersionUID = 3513100557083972036L; 070 071 private CompoundName (Properties syntax) 072 { 073 elts = new Vector<String> (); 074 mySyntax = syntax; 075 initializeSyntax (); 076 } 077 078 protected CompoundName (Enumeration<String> comps, Properties syntax) 079 { 080 elts = new Vector<String> (); 081 mySyntax = syntax; 082 initializeSyntax (); 083 try 084 { 085 while (comps.hasMoreElements ()) 086 elts.add (comps.nextElement ()); 087 } 088 catch (NoSuchElementException ignore) 089 { 090 } 091 } 092 093 public CompoundName (String n, Properties syntax) 094 throws InvalidNameException 095 { 096 elts = new Vector<String> (); 097 mySyntax = syntax; 098 initializeSyntax (); 099 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}