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 gnu.java.lang.CPStringBuilder;
042
043 import java.io.IOException;
044 import java.io.ObjectInputStream;
045 import java.io.ObjectOutputStream;
046 import java.io.Serializable;
047 import java.util.Enumeration;
048 import java.util.NoSuchElementException;
049 import java.util.Properties;
050 import 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 */
067 public 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 }