001/* CompositeName.java --
002   Copyright (C) 2001, 2005, 2006  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.Vector;
050
051/**
052 * Represents names that may span over several namespaces. For instance,
053 * the composite name http://www.gnu.org/software/classpath/index.html spans
054 * over three namespaces (the protocol http, the web server location
055 * (www.gnu.org) and the index.html location on the server).
056 * 
057 * @author Tom Tromey (tromey@redhat.com)
058 */
059public class CompositeName implements Name, Cloneable, Serializable
060{
061  private static final long serialVersionUID = 1667768148915813118L;
062  
063  private transient Vector<String> elts;  
064
065  public CompositeName ()
066  {
067    elts = new Vector<String> ();
068  }
069
070  protected CompositeName (Enumeration<String> comps)
071  {
072    elts = new Vector<String> ();
073    try
074      {
075        while (comps.hasMoreElements ())
076          elts.add (comps.nextElement ());
077      }
078    catch (NoSuchElementException ignore)
079      {
080      }
081  }
082
083  public CompositeName (String n) throws InvalidNameException
084  {
085    elts = new Vector<String> ();
086    // Parse the string into its components.
087    final char no_quote = 'x';  // Use 'x' to mean no quoting.
088    char quote = no_quote;
089    boolean escaped = false;
090    StringBuilder new_element = new StringBuilder ();
091    for (int i = 0; i < n.length (); ++i)
092      {
093        char c = n.charAt (i);
094        if (escaped)
095          escaped = false;
096        else if (c == '\\')
097          {
098            escaped = true;
099            continue;
100          }
101        else if (quote != no_quote)
102          {
103            if (quote == c)
104              {
105                // The quotes must surround a complete component.
106                if (i + 1 < n.length () && n.charAt (i + 1) != '/')
107                  throw new InvalidNameException ("close quote before end of component");
108                elts.add (new_element.toString ());
109                new_element.setLength (0);
110                quote = no_quote;
111                continue;
112              }
113            // Otherwise, fall through.
114          }
115        // Quotes are only special at the start of a component.
116        else if (new_element.length () == 0
117                 && (c == '\'' || c == '"'))
118          {
119            quote = c;
120            continue;
121          }
122        else if (c == '/')
123          {
124            elts.add (new_element.toString ());
125            new_element.setLength (0);
126            continue;
127          }
128
129        new_element.append (c);
130      }
131
132    if (new_element.length () != 0)
133      elts.add (new_element.toString ());
134
135    // Error checking.
136    if (quote != no_quote)
137      throw new InvalidNameException ("unterminated quote");
138    if (escaped)
139      throw new InvalidNameException ("trailing escape character");
140  }
141
142  public Name add (int posn, String comp) throws InvalidNameException
143  {
144    elts.add (posn, comp);
145    return this;
146  }
147
148  public Name add (String comp) throws InvalidNameException
149  {
150    elts.add (comp);
151    return this;
152  }
153
154  public Name addAll (int posn, Name n) throws InvalidNameException
155  {
156    Enumeration<String> e = n.getAll ();
157    try
158      {
159        while (e.hasMoreElements ())
160          {
161            elts.add (posn, e.nextElement ());
162            ++posn;
163          }
164      }
165    catch (NoSuchElementException ignore)
166      {
167      }
168    return this;
169  }
170
171  public Name addAll (Name suffix) throws InvalidNameException
172  {
173    Enumeration<String> e = suffix.getAll ();
174    try
175      {
176        while (e.hasMoreElements ())
177          elts.add (e.nextElement ());
178      }
179    catch (NoSuchElementException ignore)
180      {
181      }
182    return this;
183  }
184
185  public Object clone ()
186  {
187    return new CompositeName (elts.elements ());
188  }
189
190  public int compareTo (Object obj)
191  {
192    if (obj == null || ! (obj instanceof CompositeName))
193      throw new ClassCastException ("CompositeName.compareTo() expected CompositeName");
194    CompositeName cn = (CompositeName) obj;
195    int last = Math.min (cn.elts.size (), elts.size ());
196    for (int i = 0; i < last; ++i)
197      {
198        String f = elts.get (i);
199        int comp = f.compareTo (cn.elts.get (i));
200        if (comp != 0)
201          return comp;
202      }
203    return elts.size () - cn.elts.size ();
204  }
205
206  public boolean endsWith (Name n)
207  {
208    if (! (n instanceof CompositeName))
209      return false;
210    CompositeName cn = (CompositeName) n;
211    if (cn.elts.size () > elts.size ())
212      return false;
213    int delta = elts.size () - cn.elts.size ();
214    for (int i = 0; i < cn.elts.size (); ++i)
215      {
216        if (! cn.elts.get (i).equals (elts.get (delta + i)))
217          return false;
218      }
219    return true;
220  }
221
222  public boolean equals (Object obj)
223  {
224    if (! (obj instanceof CompositeName))
225      return false;
226    CompositeName cn = (CompositeName) obj;
227    return elts.equals (cn.elts);
228  }
229
230  public String get (int posn)
231  {
232    return elts.get (posn);
233  }
234
235  public Enumeration<String> getAll ()
236  {
237    return elts.elements ();
238  }
239
240  public Name getPrefix (int posn)
241  {
242    CompositeName cn = new CompositeName ();
243    for (int i = 0; i < posn; ++i)
244      cn.elts.add (elts.get (i));
245    return cn;
246  }
247
248  public Name getSuffix (int posn)
249  {
250    if (posn > elts.size ())
251      throw new ArrayIndexOutOfBoundsException (posn);
252    CompositeName cn = new CompositeName ();
253    for (int i = posn; i < elts.size (); ++i)
254      cn.elts.add (elts.get (i));
255    return cn;
256  }
257
258  public int hashCode ()
259  {
260    // Specified in documentation.
261    int h = 0;
262    for (int i = 0; i < elts.size (); ++i)
263      h += elts.get (i).hashCode ();
264    return h;
265  }
266
267  public boolean isEmpty ()
268  {
269    return elts.isEmpty ();
270  }
271
272  public Object remove (int posn) throws InvalidNameException
273  {
274    return elts.remove (posn);
275  }
276
277  public int size ()
278  {
279    return elts.size ();
280  }
281
282  public boolean startsWith (Name n)
283  {
284    if (! (n instanceof CompositeName))
285      return false;
286    CompositeName cn = (CompositeName) n;
287    if (cn.elts.size () > elts.size ())
288      return false;
289    for (int i = 0; i < cn.elts.size (); ++i)
290      {
291        if (! cn.elts.get (i).equals (elts.get (i)))
292          return false;
293      }
294    return true;
295  }
296
297  public String toString ()
298  {
299    CPStringBuilder result = new CPStringBuilder ();
300    for (int i = 0; i < elts.size (); ++i)
301      {
302        // For simplicity we choose to always quote using escapes and
303        // never quotes.
304        String elt = elts.get (i);
305        if (i > 0
306            || (i == elts.size () - 1 && elt.equals ("")))
307          result.append ('/');
308        for (int k = 0; k < elt.length (); ++k)
309          {
310            char c = elt.charAt (k);
311            // We must quote
312            //     ... a leading quote,
313            if ((k == 0 && (c == '"' || c == '\''))
314                // ... an escape preceding a meta character,
315                //     or at the end of a component,
316                || (c == '\\'
317                    && (k == elt.length () - 1
318                        || "\\'\"/".indexOf (elt.charAt (k + 1)) != -1))
319                // ... or a component separator.
320                || c == '/')
321              result.append ('\\');
322            result.append (c);
323          }
324      }
325    return result.toString ();
326  }
327  
328  private void readObject(ObjectInputStream s) 
329    throws IOException, ClassNotFoundException
330  {
331    int size = s.readInt();
332    elts = new Vector<String>(size);
333    for (int i = 0; i < size; i++)
334      elts.add((String) s.readObject());
335  }
336
337  private void writeObject(ObjectOutputStream s) throws IOException
338  {
339    s.writeInt(elts.size());
340    for (int i = 0; i < elts.size(); i++)
341      s.writeObject(elts.get(i));
342  }
343}