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