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