1 /* CompoundName.java --
2    Copyright (C) 2001 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 java.io.Serializable;
42 import java.util.Enumeration;
43 import java.util.Properties;
44 import java.util.NoSuchElementException;
45 import java.util.Vector;
46 
47 /**
48  * @author Tom Tromey <tromey@redhat.com>
49  * @date May 16, 2001
50  *
51  * FIXME: must write readObject and writeObject to conform to
52  * serialization spec.
53  *
54  * FIXME: this class is underspecified.  For instance, the `flat'
55  * direction is never described.  If it means that the CompoundName
56  * can only have a single element, then the Enumeration-based
57  * constructor ought to throw InvalidNameException.
58  */
59 public class CompoundName implements Name, Cloneable, Serializable
60 {
61   private static final long serialVersionUID = 3513100557083972036L;
62 
CompoundName(Properties syntax)63   private CompoundName (Properties syntax)
64   {
65     elts = new Vector ();
66     mySyntax = syntax;
67     initializeSyntax ();
68   }
69 
CompoundName(Enumeration comps, Properties syntax)70   protected CompoundName (Enumeration comps, Properties syntax)
71   {
72     elts = new Vector ();
73     mySyntax = syntax;
74     initializeSyntax ();
75     try
76       {
77 	while (comps.hasMoreElements ())
78 	  elts.add (comps.nextElement ());
79       }
80     catch (NoSuchElementException ignore)
81       {
82       }
83   }
84 
CompoundName(String n, Properties syntax)85   public CompoundName (String n, Properties syntax)
86     throws InvalidNameException
87   {
88     elts = new Vector ();
89     mySyntax = syntax;
90     initializeSyntax ();
91 
92     StringBuffer new_element = new StringBuffer ();
93     int i = 0;
94     // QUOTE==null means no quoting right now.  When it is set it is
95     // the value of the closing quote.
96     String quote = null;
97     while (i < n.length ())
98       {
99 	String special = isSpecial (n, i);
100 
101 	if (special == escape && escape != null)
102 	  {
103 	    if (n.length () == i + special.length ())
104 	      {
105 		// A trailing escape is treated as itself.
106 		new_element.append (special);
107 		i += special.length ();
108 	      }
109 	    else
110 	      {
111 		String eSpecial = isSpecial (n, i + special.length ());
112 		if (eSpecial != null)
113 		  {
114 		    // Treat the escape as an escape.
115 		    new_element.append (eSpecial);
116 		    i += special.length () + eSpecial.length ();
117 		  }
118 		else
119 		  {
120 		    // Treat the escape as itself.
121 		    new_element.append (special);
122 		    i += special.length ();
123 		  }
124 		continue;
125 	      }
126 	  }
127 	else if (quote != null)
128 	  {
129 	    // It is safe to use == here.
130 	    if (quote == special)
131 	      {
132 		// Quotes must surround a complete component.
133 		if (i + quote.length () < n.length ()
134 		    && ! n.startsWith (separator, i + quote.length ()))
135 		  throw new InvalidNameException ("close quote before end of component");
136 		elts.add (new_element.toString ());
137 		new_element.setLength (0);
138 		i += quote.length ();
139 		quote = null;
140 		continue;
141 	      }
142 	    // Otherwise, fall through.
143 	  }
144 	// Quotes are only special at the start of a component.
145 	else if (new_element.length () == 0
146 		 && special == beginQuote
147 		 && beginQuote != null)
148 	  {
149 	    quote = endQuote;
150 	    i += special.length ();
151 	    continue;
152 	  }
153 	else if (new_element.length () == 0
154 		 && special == beginQuote2
155 		 && beginQuote2 != null)
156 	  {
157 	    quote = endQuote2;
158 	    i += special.length ();
159 	    continue;
160 	  }
161 	else if (special == separator)
162 	  {
163 	    elts.add (new_element.toString ());
164 	    new_element.setLength (0);
165 	    i += special.length ();
166 	    continue;
167 	  }
168 
169 	// Nothing in particular, so try the next character.
170 	new_element.append (n.charAt (i));
171 	++i;
172       }
173 
174     if (new_element.length () != 0)
175       elts.add (new_element.toString ());
176 
177     if (direction == RIGHT_TO_LEFT)
178       {
179 	// Reverse the order of the elements.
180 	int len = elts.size ();
181 	for (i = 0; i < len / 2; ++i)
182 	  {
183 	    Object t = elts.set (i, elts.get (len - i - 1));
184 	    elts.set (len - i - 1, t);
185 	  }
186       }
187 
188     // Error checking.
189     if (quote != null)
190       throw new InvalidNameException ("unterminated quote");
191   }
192 
add(int posn, String comp)193   public Name add (int posn, String comp) throws InvalidNameException
194   {
195     elts.add (posn, comp);
196     return this;
197   }
198 
add(String comp)199   public Name add (String comp) throws InvalidNameException
200   {
201     elts.add (comp);
202     return this;
203   }
204 
addAll(int posn, Name n)205   public Name addAll (int posn, Name n) throws InvalidNameException
206   {
207     Enumeration e = n.getAll ();
208     try
209       {
210 	while (e.hasMoreElements ())
211 	  {
212 	    elts.add (posn, e.nextElement ());
213 	    ++posn;
214 	  }
215       }
216     catch (NoSuchElementException ignore)
217       {
218       }
219     return this;
220   }
221 
addAll(Name suffix)222   public Name addAll (Name suffix) throws InvalidNameException
223   {
224     Enumeration e = suffix.getAll ();
225     try
226       {
227 	while (e.hasMoreElements ())
228 	  elts.add (e.nextElement ());
229       }
230     catch (NoSuchElementException ignore)
231       {
232       }
233     return this;
234   }
235 
clone()236   public Object clone ()
237   {
238     return new CompoundName (elts.elements (), mySyntax);
239   }
240 
compareTo(Object obj)241   public int compareTo (Object obj)
242   {
243     if (! (obj instanceof CompoundName))
244       throw new ClassCastException ("CompoundName.compareTo() expected CompoundName");
245     CompoundName cn = (CompoundName) obj;
246     int last = Math.min (cn.elts.size (), elts.size ());
247     for (int i = 0; i < last; ++i)
248       {
249 	String f = canonicalize ((String) elts.get (i));
250 	int comp = f.compareTo (canonicalize ((String) cn.elts.get (i)));
251 	if (comp != 0)
252 	  return comp;
253       }
254     return elts.size () - cn.elts.size ();
255   }
256 
endsWith(Name n)257   public boolean endsWith (Name n)
258   {
259     if (! (n instanceof CompoundName))
260       return false;
261     CompoundName cn = (CompoundName) n;
262     if (cn.elts.size () > elts.size ())
263       return false;
264     int delta = elts.size () - cn.elts.size ();
265     for (int i = 0; i < cn.elts.size (); ++i)
266       {
267 	String f = canonicalize ((String) elts.get (i));
268 	if (! f.equals (canonicalize ((String) cn.elts.get (i))))
269 	  return false;
270       }
271     return true;
272   }
273 
equals(Object obj)274   public boolean equals (Object obj)
275   {
276     if (! (obj instanceof CompoundName))
277       return false;
278     return compareTo (obj) == 0;
279   }
280 
get(int posn)281   public String get (int posn)
282   {
283     return (String) elts.get (posn);
284   }
285 
getAll()286   public Enumeration getAll ()
287   {
288     return elts.elements ();
289   }
290 
getPrefix(int posn)291   public Name getPrefix (int posn)
292   {
293     CompoundName cn = new CompoundName (mySyntax);
294     for (int i = 0; i < posn; ++i)
295       cn.elts.add (elts.get (i));
296     return cn;
297   }
298 
getSuffix(int posn)299   public Name getSuffix (int posn)
300   {
301     if (posn > elts.size ())
302       throw new ArrayIndexOutOfBoundsException (posn);
303     CompoundName cn = new CompoundName (mySyntax);
304     for (int i = posn; i < elts.size (); ++i)
305       cn.elts.add (elts.get (i));
306     return cn;
307   }
308 
hashCode()309   public int hashCode ()
310   {
311     int h = 0;
312     for (int i = 0; i < elts.size (); ++i)
313       h += canonicalize ((String) elts.get (i)).hashCode ();
314     return h;
315   }
316 
isEmpty()317   public boolean isEmpty ()
318   {
319     return elts.isEmpty ();
320   }
321 
remove(int posn)322   public Object remove (int posn) throws InvalidNameException
323   {
324     return elts.remove (posn);
325   }
326 
size()327   public int size ()
328   {
329     return elts.size ();
330   }
331 
startsWith(Name n)332   public boolean startsWith (Name n)
333   {
334     if (! (n instanceof CompoundName))
335       return false;
336     CompoundName cn = (CompoundName) n;
337     if (cn.elts.size () > elts.size ())
338       return false;
339     for (int i = 0; i < cn.elts.size (); ++i)
340       {
341 	String f = canonicalize ((String) elts.get (i));
342 	if (! f.equals (canonicalize ((String) cn.elts.get (i))))
343 	  return false;
344       }
345     return true;
346   }
347 
348   // If ELEMENT starts with some meta-sequence at OFFSET, then return
349   // the string representing the meta-sequence.  Otherwise return
350   // null.
isSpecial(String element, int offset)351   private String isSpecial (String element, int offset)
352   {
353     String special = null;
354     if (separator != null && element.startsWith (separator, offset))
355       special = separator;
356     else if (escape != null && element.startsWith (escape, offset))
357       special = escape;
358     else if (beginQuote != null && element.startsWith (beginQuote, offset))
359       special = beginQuote;
360     else if (endQuote != null && element.startsWith (endQuote, offset))
361       special = endQuote;
362     else if (beginQuote2 != null
363 	     && element.startsWith (beginQuote2, offset))
364       special = beginQuote2;
365     else if (endQuote2 != null && element.startsWith (endQuote2, offset))
366       special = endQuote2;
367 
368     return special;
369   }
370 
toString()371   public String toString ()
372   {
373     StringBuffer result = new StringBuffer ();
374     int size = elts.size ();
375     for (int i = 0; i < size; ++i)
376       {
377 	// Find the appropriate element.  FIXME: not clear what FLAT
378 	// means.
379 	int offset = (direction == RIGHT_TO_LEFT) ? (size - i - 1) : i;
380 	String element = (String) elts.get (offset);
381 	if (i > 0
382 	    || (i == size - 1 && element.equals ("")))
383 	  result.append (separator);
384 
385 	int k = 0;
386 	while (k < element.length ())
387 	  {
388 	    String special = isSpecial (element, k);
389 	    if (special != null)
390 	      {
391 		result.append (escape);
392 		result.append (special);
393 		k += special.length ();
394 	      }
395 	    else
396 	      {
397 		result.append (element.charAt (k));
398 		++k;
399 	      }
400 	  }
401       }
402 
403     return result.toString ();
404   }
405 
406   // This canonicalizes a String, based on the syntax, for comparison
407   // or other similar purposes.
canonicalize(String element)408   private String canonicalize (String element)
409   {
410     String ret = element;
411 
412     if (ignoreCase)
413       ret = ret.toLowerCase ();
414 
415     if (trimBlanks)
416       {
417 	int first = 0;
418 	while (first < ret.length ()
419 	       && Character.isWhitespace (ret.charAt (first)))
420 	  ++first;
421 
422 	int last = ret.length () - 1;
423 	while (last >= first
424 	       && Character.isWhitespace (ret.charAt (last)))
425 	  --last;
426 
427 	ret = ret.substring (first, last);
428       }
429 
430     return ret;
431   }
432 
433   // This initializes all the syntax variables.  This seems easier
434   // than re-querying the properties every time.  We're allowed to do
435   // this because the spec says that subclasses should consider the
436   // syntax as being read-only.
initializeSyntax()437   private void initializeSyntax ()
438   {
439     String t = mySyntax.getProperty ("jndi.syntax.direction", "flat");
440     if (t.equals ("right_to_left"))
441       this.direction = RIGHT_TO_LEFT;
442     else if (t.equals ("left_to_right"))
443       this.direction = LEFT_TO_RIGHT;
444     else
445       {
446 	// If we don't recognize it, default to flat.
447 	this.direction = FLAT;
448       }
449 
450     // This is required unless the direction is FLAT.  Unfortunately
451     // there is no way to report this error.
452     this.separator = mySyntax.getProperty ("jndi.syntax.separator", "");
453 
454     this.ignoreCase
455       = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.ignorecase",
456 					       "false")).booleanValue ();
457     this.escape = mySyntax.getProperty ("jndi.syntax.escape", null);
458     this.beginQuote = mySyntax.getProperty ("jndi.syntax.beginquote", null);
459     this.endQuote = mySyntax.getProperty ("jndi.syntax.endquote",
460 					  this.beginQuote);
461     this.beginQuote2 = mySyntax.getProperty ("jndi.syntax.beginquote2",
462 					     null);
463     this.endQuote2 = mySyntax.getProperty ("jndi.syntax.endquote2",
464 					   this.beginQuote2);
465     this.trimBlanks
466       = Boolean.valueOf (mySyntax.getProperty ("jndi.syntax.trimblanks",
467 					       "false")).booleanValue ();
468   }
469 
470   // The spec specifies this but does not document it in any way (it
471   // is a package-private class).  It is useless as far as I can tell.
472   // So we ignore it.
473   // protected transient NameImpl impl;
474   protected transient Properties mySyntax;
475 
476   // The actual elements.
477   private transient Vector elts;
478 
479   // The following are all used for syntax.
480   private transient int direction;
481   private transient String separator;
482   private transient boolean ignoreCase;
483   private transient String escape;
484   private transient String beginQuote;
485   private transient String endQuote;
486   private transient String beginQuote2;
487   private transient String endQuote2;
488   private transient boolean trimBlanks;
489   // We didn't need these for parsing, so they are gone.
490   // private transient String avaSeparator;
491   // private transient String typevalSeparator;
492 
493   private static final int RIGHT_TO_LEFT = -1;
494   private static final int LEFT_TO_RIGHT = 1;
495   private static final int FLAT = 0;
496 }
497