1/*
2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.naming;
27
28import java.util.Enumeration;
29import java.util.Properties;
30
31/**
32 * This class represents a compound name -- a name from
33 * a hierarchical name space.
34 * Each component in a compound name is an atomic name.
35 * <p>
36 * The components of a compound name are numbered.  The indexes of a
37 * compound name with N components range from 0 up to, but not including, N.
38 * This range may be written as [0,N).
39 * The most significant component is at index 0.
40 * An empty compound name has no components.
41 *
42 * <h1>Compound Name Syntax</h1>
43 * The syntax of a compound name is specified using a set of properties:
44 *<dl>
45 *  <dt>jndi.syntax.direction
46 *  <dd>Direction for parsing ("right_to_left", "left_to_right", "flat").
47 *      If unspecified, defaults to "flat", which means the namespace is flat
48 *      with no hierarchical structure.
49 *
50 *  <dt>jndi.syntax.separator
51 *  <dd>Separator between atomic name components.
52 *      Required unless direction is "flat".
53 *
54 *  <dt>jndi.syntax.ignorecase
55 *  <dd>If present, "true" means ignore the case when comparing name
56 *      components. If its value is not "true", or if the property is not
57 *      present, case is considered when comparing name components.
58 *
59 *  <dt>jndi.syntax.escape
60 *  <dd>If present, specifies the escape string for overriding separator,
61 *      escapes and quotes.
62 *
63 *  <dt>jndi.syntax.beginquote
64 *  <dd>If present, specifies the string delimiting start of a quoted string.
65 *
66 *  <dt>jndi.syntax.endquote
67 *  <dd>String delimiting end of quoted string.
68 *      If present, specifies the string delimiting the end of a quoted string.
69 *      If not present, use syntax.beginquote as end quote.
70 *  <dt>jndi.syntax.beginquote2
71 *  <dd>Alternative set of begin/end quotes.
72 *
73 *  <dt>jndi.syntax.endquote2
74 *  <dd>Alternative set of begin/end quotes.
75 *
76 *  <dt>jndi.syntax.trimblanks
77 *  <dd>If present, "true" means trim any leading and trailing whitespaces
78 *      in a name component for comparison purposes. If its value is not
79 *      "true", or if the property is not present, blanks are significant.
80 *  <dt>jndi.syntax.separator.ava
81 *  <dd>If present, specifies the string that separates
82 *      attribute-value-assertions when specifying multiple attribute/value
83 *      pairs. (e.g. ","  in age=65,gender=male).
84 *  <dt>jndi.syntax.separator.typeval
85 *  <dd>If present, specifies the string that separates attribute
86 *              from value (e.g. "=" in "age=65")
87 *</dl>
88 * These properties are interpreted according to the following rules:
89 *<ol>
90 *<li>
91 * In a string without quotes or escapes, any instance of the
92 * separator delimits two atomic names. Each atomic name is referred
93 * to as a <em>component</em>.
94 *<li>
95 * A separator, quote or escape is escaped if preceded immediately
96 * (on the left) by the escape.
97 *<li>
98 * If there are two sets of quotes, a specific begin-quote must be matched
99 * by its corresponding end-quote.
100 *<li>
101 * A non-escaped begin-quote which precedes a component must be
102 * matched by a non-escaped end-quote at the end of the component.
103 * A component thus quoted is referred to as a
104 * <em>quoted component</em>. It is parsed by
105 * removing the being- and end- quotes, and by treating the intervening
106 * characters as ordinary characters unless one of the rules involving
107 * quoted components listed below applies.
108 *<li>
109 * Quotes embedded in non-quoted components are treated as ordinary strings
110 * and need not be matched.
111 *<li>
112 * A separator that is escaped or appears between non-escaped
113 * quotes is treated as an ordinary string and not a separator.
114 *<li>
115 * An escape string within a quoted component acts as an escape only when
116 * followed by the corresponding end-quote string.
117 * This can be used to embed an escaped quote within a quoted component.
118 *<li>
119 * An escaped escape string is not treated as an escape string.
120 *<li>
121 * An escape string that does not precede a meta string (quotes or separator)
122 * and is not at the end of a component is treated as an ordinary string.
123 *<li>
124 * A leading separator (the compound name string begins with
125 * a separator) denotes a leading empty atomic component (consisting
126 * of an empty string).
127 * A trailing separator (the compound name string ends with
128 * a separator) denotes a trailing empty atomic component.
129 * Adjacent separators denote an empty atomic component.
130 *</ol>
131 * <p>
132 * The string form of the compound name follows the syntax described above.
133 * When the components of the compound name are turned into their
134 * string representation, the reserved syntax rules described above are
135 * applied (e.g. embedded separators are escaped or quoted)
136 * so that when the same string is parsed, it will yield the same components
137 * of the original compound name.
138 *
139 *<h1>Multithreaded Access</h1>
140 * A {@code CompoundName} instance is not synchronized against concurrent
141 * multithreaded access. Multiple threads trying to access and modify a
142 * {@code CompoundName} should lock the object.
143 *
144 * @author Rosanna Lee
145 * @author Scott Seligman
146 * @since 1.3
147 */
148
149public class CompoundName implements Name {
150
151    /**
152     * Implementation of this compound name. This field is initialized by the
153     * constructors and cannot be null.
154     */
155    private transient NameImpl impl;
156    /**
157      * Syntax properties for this compound name.
158      * This field is initialized by the constructors and cannot be null.
159      * It should be treated as a read-only variable by subclasses.
160      * Any necessary changes to mySyntax should be made within constructors
161      * and not after the compound name has been instantiated.
162      */
163    protected transient Properties mySyntax;
164
165    /**
166      * Constructs a new compound name instance using the components
167      * specified in comps and syntax. This protected method is intended
168      * to be used by subclasses of CompoundName when they override
169      * methods such as clone(), getPrefix(), getSuffix().
170      *
171      * @param comps  A non-null enumeration of the components to add.
172      *   Each element of the enumeration is of class String.
173      *               The enumeration will be consumed to extract its
174      *               elements.
175      * @param syntax   A non-null properties that specify the syntax of
176      *                 this compound name. See class description for
177      *                 contents of properties.
178      */
179    protected CompoundName(Enumeration<String> comps, Properties syntax) {
180        if (syntax == null) {
181            throw new NullPointerException();
182        }
183        mySyntax = syntax;
184        impl = new NameImpl(syntax, comps);
185    }
186
187    /**
188      * Constructs a new compound name instance by parsing the string n
189      * using the syntax specified by the syntax properties supplied.
190      *
191      * @param  n       The non-null string to parse.
192      * @param syntax   A non-null list of properties that specify the syntax of
193      *                 this compound name.  See class description for
194      *                 contents of properties.
195      * @exception      InvalidNameException If 'n' violates the syntax specified
196      *                 by {@code syntax}.
197      */
198    public CompoundName(String n, Properties syntax) throws InvalidNameException {
199        if (syntax == null) {
200            throw new NullPointerException();
201        }
202        mySyntax = syntax;
203        impl = new NameImpl(syntax, n);
204    }
205
206    /**
207      * Generates the string representation of this compound name, using
208      * the syntax rules of the compound name. The syntax rules
209      * are described in the class description.
210      * An empty component is represented by an empty string.
211      *
212      * The string representation thus generated can be passed to
213      * the CompoundName constructor with the same syntax properties
214      * to create a new equivalent compound name.
215      *
216      * @return A non-null string representation of this compound name.
217      */
218    public String toString() {
219        return (impl.toString());
220    }
221
222    /**
223      * Determines whether obj is syntactically equal to this compound name.
224      * If obj is null or not a CompoundName, false is returned.
225      * Two compound names are equal if each component in one is "equal"
226      * to the corresponding component in the other.
227      *<p>
228      * Equality is also defined in terms of the syntax of this compound name.
229      * The default implementation of CompoundName uses the syntax properties
230      * jndi.syntax.ignorecase and jndi.syntax.trimblanks when comparing
231      * two components for equality.  If case is ignored, two strings
232      * with the same sequence of characters but with different cases
233      * are considered equal. If blanks are being trimmed, leading and trailing
234      * blanks are ignored for the purpose of the comparison.
235      *<p>
236      * Both compound names must have the same number of components.
237      *<p>
238      * Implementation note: Currently the syntax properties of the two compound
239      * names are not compared for equality. They might be in the future.
240      *
241      * @param  obj     The possibly null object to compare against.
242      * @return true if obj is equal to this compound name, false otherwise.
243      * @see #compareTo(java.lang.Object obj)
244      */
245    public boolean equals(Object obj) {
246        // %%% check syntax too?
247        return (obj != null &&
248                obj instanceof CompoundName &&
249                impl.equals(((CompoundName)obj).impl));
250    }
251
252    /**
253      * Computes the hash code of this compound name.
254      * The hash code is the sum of the hash codes of the "canonicalized"
255      * forms of individual components of this compound name.
256      * Each component is "canonicalized" according to the
257      * compound name's syntax before its hash code is computed.
258      * For a case-insensitive name, for example, the uppercased form of
259      * a name has the same hash code as its lowercased equivalent.
260      *
261      * @return An int representing the hash code of this name.
262      */
263    public int hashCode() {
264        return impl.hashCode();
265    }
266
267    /**
268      * Creates a copy of this compound name.
269      * Changes to the components of this compound name won't
270      * affect the new copy and vice versa.
271      * The clone and this compound name share the same syntax.
272      *
273      * @return A non-null copy of this compound name.
274      */
275    public Object clone() {
276        return (new CompoundName(getAll(), mySyntax));
277    }
278
279    /**
280     * Compares this CompoundName with the specified Object for order.
281     * Returns a
282     * negative integer, zero, or a positive integer as this Name is less
283     * than, equal to, or greater than the given Object.
284     * <p>
285     * If obj is null or not an instance of CompoundName, ClassCastException
286     * is thrown.
287     * <p>
288     * See equals() for what it means for two compound names to be equal.
289     * If two compound names are equal, 0 is returned.
290     *<p>
291     * Ordering of compound names depend on the syntax of the compound name.
292     * By default, they follow lexicographical rules for string comparison
293     * with the extension that this applies to all the components in the
294     * compound name and that comparison of individual components is
295     * affected by the jndi.syntax.ignorecase and jndi.syntax.trimblanks
296     * properties, identical to how they affect equals().
297     * If this compound name is "lexicographically" lesser than obj,
298     * a negative number is returned.
299     * If this compound name is "lexicographically" greater than obj,
300     * a positive number is returned.
301     *<p>
302     * Implementation note: Currently the syntax properties of the two compound
303     * names are not compared when checking order. They might be in the future.
304     * @param   obj     The non-null object to compare against.
305     * @return  a negative integer, zero, or a positive integer as this Name
306     *          is less than, equal to, or greater than the given Object.
307     * @exception ClassCastException if obj is not a CompoundName.
308     * @see #equals(java.lang.Object)
309     */
310    public int compareTo(Object obj) {
311        if (!(obj instanceof CompoundName)) {
312            throw new ClassCastException("Not a CompoundName");
313        }
314        return impl.compareTo(((CompoundName)obj).impl);
315    }
316
317    /**
318      * Retrieves the number of components in this compound name.
319      *
320      * @return The nonnegative number of components in this compound name.
321      */
322    public int size() {
323        return (impl.size());
324    }
325
326    /**
327      * Determines whether this compound name is empty.
328      * A compound name is empty if it has zero components.
329      *
330      * @return true if this compound name is empty, false otherwise.
331      */
332    public boolean isEmpty() {
333        return (impl.isEmpty());
334    }
335
336    /**
337      * Retrieves the components of this compound name as an enumeration
338      * of strings.
339      * The effects of updates to this compound name on this enumeration
340      * is undefined.
341      *
342      * @return A non-null enumeration of the components of this
343      * compound name. Each element of the enumeration is of class String.
344      */
345    public Enumeration<String> getAll() {
346        return (impl.getAll());
347    }
348
349    /**
350      * Retrieves a component of this compound name.
351      *
352      * @param  posn    The 0-based index of the component to retrieve.
353      *                 Must be in the range [0,size()).
354      * @return The component at index posn.
355      * @exception ArrayIndexOutOfBoundsException if posn is outside the
356      *         specified range.
357      */
358    public String get(int posn) {
359        return (impl.get(posn));
360    }
361
362    /**
363      * Creates a compound name whose components consist of a prefix of the
364      * components in this compound name.
365      * The result and this compound name share the same syntax.
366      * Subsequent changes to
367      * this compound name do not affect the name that is returned and
368      * vice versa.
369      *
370      * @param  posn    The 0-based index of the component at which to stop.
371      *                 Must be in the range [0,size()].
372      * @return A compound name consisting of the components at indexes in
373      *         the range [0,posn).
374      * @exception ArrayIndexOutOfBoundsException
375      *         If posn is outside the specified range.
376      */
377    public Name getPrefix(int posn) {
378        Enumeration<String> comps = impl.getPrefix(posn);
379        return (new CompoundName(comps, mySyntax));
380    }
381
382    /**
383      * Creates a compound name whose components consist of a suffix of the
384      * components in this compound name.
385      * The result and this compound name share the same syntax.
386      * Subsequent changes to
387      * this compound name do not affect the name that is returned.
388      *
389      * @param  posn    The 0-based index of the component at which to start.
390      *                 Must be in the range [0,size()].
391      * @return A compound name consisting of the components at indexes in
392      *         the range [posn,size()).  If posn is equal to
393      *         size(), an empty compound name is returned.
394      * @exception ArrayIndexOutOfBoundsException
395      *         If posn is outside the specified range.
396      */
397    public Name getSuffix(int posn) {
398        Enumeration<String> comps = impl.getSuffix(posn);
399        return (new CompoundName(comps, mySyntax));
400    }
401
402    /**
403      * Determines whether a compound name is a prefix of this compound name.
404      * A compound name 'n' is a prefix if it is equal to
405      * getPrefix(n.size())--in other words, this compound name
406      * starts with 'n'.
407      * If n is null or not a compound name, false is returned.
408      *<p>
409      * Implementation note: Currently the syntax properties of n
410      *  are not used when doing the comparison. They might be in the future.
411      * @param  n       The possibly null compound name to check.
412      * @return true if n is a CompoundName and
413      *                 is a prefix of this compound name, false otherwise.
414      */
415    public boolean startsWith(Name n) {
416        if (n instanceof CompoundName) {
417            return (impl.startsWith(n.size(), n.getAll()));
418        } else {
419            return false;
420        }
421    }
422
423    /**
424      * Determines whether a compound name is a suffix of this compound name.
425      * A compound name 'n' is a suffix if it is equal to
426      * getSuffix(size()-n.size())--in other words, this
427      * compound name ends with 'n'.
428      * If n is null or not a compound name, false is returned.
429      *<p>
430      * Implementation note: Currently the syntax properties of n
431      *  are not used when doing the comparison. They might be in the future.
432      * @param  n       The possibly null compound name to check.
433      * @return true if n is a CompoundName and
434      *         is a suffix of this compound name, false otherwise.
435      */
436    public boolean endsWith(Name n) {
437        if (n instanceof CompoundName) {
438            return (impl.endsWith(n.size(), n.getAll()));
439        } else {
440            return false;
441        }
442    }
443
444    /**
445      * Adds the components of a compound name -- in order -- to the end of
446      * this compound name.
447      *<p>
448      * Implementation note: Currently the syntax properties of suffix
449      *  is not used or checked. They might be in the future.
450      * @param suffix   The non-null components to add.
451      * @return The updated CompoundName, not a new one. Cannot be null.
452      * @exception InvalidNameException If suffix is not a compound name,
453      *            or if the addition of the components violates the syntax
454      *            of this compound name (e.g. exceeding number of components).
455      */
456    public Name addAll(Name suffix) throws InvalidNameException {
457        if (suffix instanceof CompoundName) {
458            impl.addAll(suffix.getAll());
459            return this;
460        } else {
461            throw new InvalidNameException("Not a compound name: " +
462                suffix.toString());
463        }
464    }
465
466    /**
467      * Adds the components of a compound name -- in order -- at a specified
468      * position within this compound name.
469      * Components of this compound name at or after the index of the first
470      * new component are shifted up (away from index 0)
471      * to accommodate the new components.
472      *<p>
473      * Implementation note: Currently the syntax properties of suffix
474      *  is not used or checked. They might be in the future.
475      *
476      * @param n        The non-null components to add.
477      * @param posn     The index in this name at which to add the new
478      *                 components.  Must be in the range [0,size()].
479      * @return The updated CompoundName, not a new one. Cannot be null.
480      * @exception ArrayIndexOutOfBoundsException
481      *         If posn is outside the specified range.
482      * @exception InvalidNameException If n is not a compound name,
483      *            or if the addition of the components violates the syntax
484      *            of this compound name (e.g. exceeding number of components).
485      */
486    public Name addAll(int posn, Name n) throws InvalidNameException {
487        if (n instanceof CompoundName) {
488            impl.addAll(posn, n.getAll());
489            return this;
490        } else {
491            throw new InvalidNameException("Not a compound name: " +
492                n.toString());
493        }
494    }
495
496    /**
497      * Adds a single component to the end of this compound name.
498      *
499      * @param comp     The non-null component to add.
500      * @return The updated CompoundName, not a new one. Cannot be null.
501      * @exception InvalidNameException If adding comp at end of the name
502      *                         would violate the compound name's syntax.
503      */
504    public Name add(String comp) throws InvalidNameException{
505        impl.add(comp);
506        return this;
507    }
508
509    /**
510      * Adds a single component at a specified position within this
511      * compound name.
512      * Components of this compound name at or after the index of the new
513      * component are shifted up by one (away from index 0)
514      * to accommodate the new component.
515      *
516      * @param  comp    The non-null component to add.
517      * @param  posn    The index at which to add the new component.
518      *                 Must be in the range [0,size()].
519      * @exception ArrayIndexOutOfBoundsException
520      *         If posn is outside the specified range.
521      * @return The updated CompoundName, not a new one. Cannot be null.
522      * @exception InvalidNameException If adding comp at the specified position
523      *                         would violate the compound name's syntax.
524      */
525    public Name add(int posn, String comp) throws InvalidNameException{
526        impl.add(posn, comp);
527        return this;
528    }
529
530    /**
531      * Deletes a component from this compound name.
532      * The component of this compound name at position 'posn' is removed,
533      * and components at indices greater than 'posn'
534      * are shifted down (towards index 0) by one.
535      *
536      * @param  posn    The index of the component to delete.
537      *                 Must be in the range [0,size()).
538      * @return The component removed (a String).
539      * @exception ArrayIndexOutOfBoundsException
540      *         If posn is outside the specified range (includes case where
541      *         compound name is empty).
542      * @exception InvalidNameException If deleting the component
543      *                         would violate the compound name's syntax.
544      */
545    public Object remove(int posn) throws InvalidNameException {
546        return impl.remove(posn);
547    }
548
549    /**
550     * Overridden to avoid implementation dependency.
551     * @serialData The syntax {@code Properties}, followed by
552     * the number of components (an {@code int}), and the individual
553     * components (each a {@code String}).
554     */
555    private void writeObject(java.io.ObjectOutputStream s)
556            throws java.io.IOException {
557        s.writeObject(mySyntax);
558        s.writeInt(size());
559        Enumeration<String> comps = getAll();
560        while (comps.hasMoreElements()) {
561            s.writeObject(comps.nextElement());
562        }
563    }
564
565    /**
566     * Overridden to avoid implementation dependency.
567     */
568    private void readObject(java.io.ObjectInputStream s)
569            throws java.io.IOException, ClassNotFoundException {
570        mySyntax = (Properties)s.readObject();
571        impl = new NameImpl(mySyntax);
572        int n = s.readInt();    // number of components
573        try {
574            while (--n >= 0) {
575                add((String)s.readObject());
576            }
577        } catch (InvalidNameException e) {
578            throw (new java.io.StreamCorruptedException("Invalid name"));
579        }
580    }
581
582    /**
583     * Use serialVersionUID from JNDI 1.1.1 for interoperability
584     */
585    private static final long serialVersionUID = 3513100557083972036L;
586
587/*
588//   For testing
589
590    public static void main(String[] args) {
591        Properties dotSyntax = new Properties();
592        dotSyntax.put("jndi.syntax.direction", "right_to_left");
593        dotSyntax.put("jndi.syntax.separator", ".");
594        dotSyntax.put("jndi.syntax.ignorecase", "true");
595        dotSyntax.put("jndi.syntax.escape", "\\");
596//      dotSyntax.put("jndi.syntax.beginquote", "\"");
597//      dotSyntax.put("jndi.syntax.beginquote2", "'");
598
599        Name first = null;
600        try {
601            for (int i = 0; i < args.length; i++) {
602                Name name;
603                Enumeration e;
604                System.out.println("Given name: " + args[i]);
605                name = new CompoundName(args[i], dotSyntax);
606                if (first == null) {
607                    first = name;
608                }
609                e = name.getComponents();
610                while (e.hasMoreElements()) {
611                    System.out.println("Element: " + e.nextElement());
612                }
613                System.out.println("Constructed name: " + name.toString());
614
615                System.out.println("Compare " + first.toString() + " with "
616                    + name.toString() + " = " + first.compareTo(name));
617            }
618        } catch (Exception ne) {
619            ne.printStackTrace();
620        }
621    }
622*/
623}
624