001    /* CompositeName.java --
002       Copyright (C) 2001, 2005, 2006  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.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     */
059    public 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    }