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 }