CNNameParser.java revision 820:9205e980062a
1/*
2 * Copyright (c) 1999, 2011, 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 com.sun.jndi.cosnaming;
27
28import javax.naming.*;
29import java.util.Properties;
30import java.util.Vector;
31import java.util.Enumeration;
32
33import org.omg.CosNaming.NameComponent;
34
35/**
36  * Parsing routines for NameParser as well as COS Naming stringified names.
37  * This is used by CNCtx to create a NameComponent[] object and vice versa.
38  * It follows Section 4.5 of Interoperable Naming Service (INS) 98-10-11.
39  * In summary, the stringified form is a left-to-right, forward-slash
40  * separated name. id and kinds are separated by '.'. backslash is the
41  * escape character.
42  *
43  * @author Rosanna Lee
44  */
45
46final public class CNNameParser implements NameParser {
47
48    private static final Properties mySyntax = new Properties();
49    private static final char kindSeparator = '.';
50    private static final char compSeparator = '/';
51    private static final char escapeChar = '\\';
52    static {
53        mySyntax.put("jndi.syntax.direction", "left_to_right");
54        mySyntax.put("jndi.syntax.separator", ""+compSeparator);
55        mySyntax.put("jndi.syntax.escape", ""+escapeChar);
56    };
57
58  /**
59    * Constructs a new name parser for parsing names in INS syntax.
60    */
61    public CNNameParser() {
62    }
63
64  /**
65    * Returns a CompoundName given a string in INS syntax.
66    * @param name The non-null string representation of the name.
67    * @return a non-null CompoundName
68    */
69    public Name parse(String name) throws NamingException {
70        Vector<String> comps = insStringToStringifiedComps(name);
71        return new CNCompoundName(comps.elements());
72    }
73
74    /**
75     * Creates a NameComponent[] from a Name structure.
76     * Used by CNCtx to convert the input Name arg into a NameComponent[].
77     * @param a CompoundName or a CompositeName;
78     * each component must be the stringified form of a NameComponent.
79     */
80    static NameComponent[] nameToCosName(Name name)
81        throws InvalidNameException {
82            int len = name.size();
83            if (len == 0) {
84                return new NameComponent[0];
85            }
86
87            NameComponent[] answer = new NameComponent[len];
88            for (int i = 0; i < len; i++) {
89                answer[i] = parseComponent(name.get(i));
90            }
91            return answer;
92    }
93
94    /**
95     * Returns the INS stringified form of a NameComponent[].
96     * Used by CNCtx.getNameInNamespace(), CNCompoundName.toString().
97     */
98    static String cosNameToInsString(NameComponent[] cname) {
99      StringBuilder str = new StringBuilder();
100      for ( int i = 0; i < cname.length; i++) {
101          if ( i > 0) {
102              str.append(compSeparator);
103          }
104          str.append(stringifyComponent(cname[i]));
105      }
106      return str.toString();
107    }
108
109    /**
110     * Creates a CompositeName from a NameComponent[].
111     * Used by ExceptionMapper and CNBindingEnumeration to convert
112     * a NameComponent[] into a composite name.
113     */
114    static Name cosNameToName(NameComponent[] cname) {
115        Name nm = new CompositeName();
116        for ( int i = 0; cname != null && i < cname.length; i++) {
117            try {
118                nm.add(stringifyComponent(cname[i]));
119            } catch (InvalidNameException e) {
120                // ignore
121            }
122        }
123        return nm;
124    }
125
126    /**
127     * Converts an INS-syntax string name into a Vector in which
128     * each element of the vector contains a stringified form of
129     * a NameComponent.
130     */
131    private static Vector<String> insStringToStringifiedComps(String str)
132        throws InvalidNameException {
133
134        int len = str.length();
135        Vector<String> components = new Vector<>(10);
136        char[] id = new char[len];
137        char[] kind = new char[len];
138        int idCount, kindCount;
139        boolean idMode;
140        for (int i = 0; i < len; ) {
141            idCount = kindCount = 0; // reset for new component
142            idMode = true;           // always start off parsing id
143            while (i < len) {
144                if (str.charAt(i) == compSeparator) {
145                    break;
146
147                } else if (str.charAt(i) == escapeChar) {
148                    if (i + 1 >= len) {
149                        throw new InvalidNameException(str +
150                            ": unescaped \\ at end of component");
151                    } else if (isMeta(str.charAt(i+1))) {
152                        ++i; // skip escape and let meta through
153                        if (idMode) {
154                            id[idCount++] = str.charAt(i++);
155                        } else {
156                            kind[kindCount++] = str.charAt(i++);
157                        }
158                    } else {
159                        throw new InvalidNameException(str +
160                            ": invalid character being escaped");
161                    }
162
163                } else if (idMode && str.charAt(i) == kindSeparator) {
164                    // just look for the first kindSeparator
165                    ++i; // skip kind separator
166                    idMode = false;
167
168                } else {
169                    if (idMode) {
170                        id[idCount++] = str.charAt(i++);
171                    } else {
172                        kind[kindCount++] = str.charAt(i++);
173                    }
174                }
175            }
176            components.addElement(stringifyComponent(
177                new NameComponent(new String(id, 0, idCount),
178                    new String(kind, 0, kindCount))));
179
180            if (i < len) {
181                ++i; // skip separator
182            }
183        }
184
185        return components;
186    }
187
188    /**
189     * Return a NameComponent given its stringified form.
190     */
191    private static NameComponent parseComponent(String compStr)
192    throws InvalidNameException {
193        NameComponent comp = new NameComponent();
194        int kindSep = -1;
195        int len = compStr.length();
196
197        int j = 0;
198        char[] newStr = new char[len];
199        boolean escaped = false;
200
201        // Find the kind separator
202        for (int i = 0; i < len && kindSep < 0; i++) {
203            if (escaped) {
204                newStr[j++] = compStr.charAt(i);
205                escaped = false;
206            } else if (compStr.charAt(i) == escapeChar) {
207                if (i + 1 >= len) {
208                    throw new InvalidNameException(compStr +
209                            ": unescaped \\ at end of component");
210                } else if (isMeta(compStr.charAt(i+1))) {
211                    escaped = true;
212                } else {
213                    throw new InvalidNameException(compStr +
214                        ": invalid character being escaped");
215                }
216            } else if (compStr.charAt(i) == kindSeparator) {
217                kindSep = i;
218            } else {
219                newStr[j++] = compStr.charAt(i);
220            }
221        }
222
223        // Set id
224        comp.id = new String(newStr, 0, j);
225
226        // Set kind
227        if (kindSep < 0) {
228            comp.kind = "";  // no kind separator
229        } else {
230            // unescape kind
231            j = 0;
232            escaped = false;
233            for (int i = kindSep+1; i < len; i++) {
234                if (escaped) {
235                    newStr[j++] = compStr.charAt(i);
236                    escaped = false;
237                } else if (compStr.charAt(i) == escapeChar) {
238                    if (i + 1 >= len) {
239                        throw new InvalidNameException(compStr +
240                            ": unescaped \\ at end of component");
241                    } else if (isMeta(compStr.charAt(i+1))) {
242                        escaped = true;
243                    } else {
244                        throw new InvalidNameException(compStr +
245                            ": invalid character being escaped");
246                    }
247                } else {
248                    newStr[j++] = compStr.charAt(i);
249                }
250            }
251            comp.kind = new String(newStr, 0, j);
252        }
253        return comp;
254    }
255
256    private static String stringifyComponent(NameComponent comp) {
257        StringBuilder one = new StringBuilder(escape(comp.id));
258        if (comp.kind != null && !comp.kind.equals("")) {
259            one.append(kindSeparator).append(escape(comp.kind));
260        }
261        if (one.length() == 0) {
262            return ""+kindSeparator;  // if neither id nor kind specified
263        } else {
264            return one.toString();
265        }
266    }
267
268    /**
269     * Returns a string with '.', '\', '/' escaped. Used when
270     * stringifying the name into its INS stringified form.
271     */
272    private static String escape(String str) {
273        if (str.indexOf(kindSeparator) < 0 &&
274            str.indexOf(compSeparator) < 0 &&
275            str.indexOf(escapeChar) < 0) {
276            return str;                         // no meta characters to escape
277        } else {
278            int len = str.length();
279            int j = 0;
280            char[] newStr = new char[len+len];
281            for (int i = 0; i < len; i++) {
282                if (isMeta(str.charAt(i))) {
283                    newStr[j++] = escapeChar;   // escape meta character
284                }
285                newStr[j++] = str.charAt(i);
286            }
287            return new String(newStr, 0, j);
288        }
289    }
290
291    /**
292     * In INS, there are three meta characters: '.', '/' and '\'.
293     */
294    private static boolean isMeta(char ch) {
295        switch (ch) {
296        case kindSeparator:
297        case compSeparator:
298        case escapeChar:
299            return true;
300        }
301        return false;
302    }
303
304    /**
305     * An implementation of CompoundName that bypasses the parsing
306     * and stringifying code of the default CompoundName.
307     */
308    static final class CNCompoundName extends CompoundName {
309        CNCompoundName(Enumeration<String> enum_) {
310            super(enum_, CNNameParser.mySyntax);
311        }
312
313        public Object clone() {
314            return new CNCompoundName(getAll());
315        }
316
317        public Name getPrefix(int posn) {
318            Enumeration<String> comps = super.getPrefix(posn).getAll();
319            return new CNCompoundName(comps);
320        }
321
322        public Name getSuffix(int posn) {
323            Enumeration<String> comps = super.getSuffix(posn).getAll();
324            return new CNCompoundName(comps);
325        }
326
327        public String toString() {
328            try {
329                // Convert Name to NameComponent[] then stringify
330                return cosNameToInsString(nameToCosName(this));
331            } catch (InvalidNameException e) {
332                return super.toString();
333            }
334        }
335
336        private static final long serialVersionUID = -6599252802678482317L;
337    }
338
339// for testing only
340/*
341    private static void print(String input) {
342        try {
343            System.out.println("\n >>>>>> input: " + input);
344
345            System.out.println("--Compound Name: ");
346            NameParser parser = new CNNameParser();
347            Name name = parser.parse(input);
348            for (int i = 0; i < name.size(); i++) {
349                System.out.println("\t" + i + ": " + name.get(i));
350                NameComponent cp = parseComponent(name.get(i));
351                System.out.println("\t\t" + "id: " + cp.id + ";kind: " + cp.kind);
352            }
353            System.out.println("\t" + name.toString());
354
355            System.out.println("--Composite Name: ");
356            Name composite = new CompositeName(input);
357            for (int i = 0; i < composite.size(); i++) {
358                System.out.println("\t" + i+": " + composite.get(i));
359            }
360            System.out.println("\t" + composite.toString());
361
362            System.out.println("--Composite To NameComponent");
363            NameComponent[] names = nameToCosName(composite);
364            for (int i = 0; i < composite.size(); i++) {
365                System.out.println("\t" + i+": id: " + names[i].id + "; kind: " + names[i].kind);
366            }
367            System.out.println("\t" + cosNameToInsString(names));
368        } catch (NamingException e) {
369            System.out.println(e);
370        }
371    }
372
373    private static void checkName(Name name, String[] comps) throws Exception {
374        if (name.size() != comps.length) {
375            throw new Exception(
376                "test failed; incorrect component count in " + name + "; " +
377                "expecting " + comps.length + " got " + name.size());
378        }
379        for (int i = 0; i < name.size(); i++) {
380            if (!comps[i].equals(name.get(i))) {
381                throw new Exception (
382                    "test failed; invalid component in " + name + "; " +
383                    "expecting '" + comps[i] + "' got '" + name.get(i) + "'");
384            }
385        }
386    }
387
388    private static void checkCompound(NameParser parser,
389        String input, String[] comps) throws Exception {
390        checkName(parser.parse(input), comps);
391    }
392
393    private static void checkComposite(String input, String[] comps)
394    throws Exception {
395        checkName(new CompositeName(input), comps);
396    }
397
398    private static String[] compounds = {
399        "a/b/c",
400        "a.b/c.d",
401        "a",
402        ".",
403        "a.",
404        "c.d",
405        ".e",
406        "a/x\\/y\\/z/b",
407        "a\\.b.c\\.d/e.f",
408        "a/b\\\\/c",
409        "x\\\\.y",
410        "x\\.y",
411        "x.\\\\y",
412        "x.y\\\\",
413        "\\\\x.y",
414        "a.b\\.c/d"
415    };
416    private static String[][] compoundComps = {
417        {"a", "b", "c"},
418        {"a.b", "c.d"},
419        {"a"},
420        {"."},
421        {"a"},
422        {"c.d"},
423        {".e"},
424        {"a", "x\\/y\\/z", "b"},
425        {"a\\.b.c\\.d", "e.f"},
426        {"a", "b\\\\", "c"},
427        {"x\\\\.y"},
428        {"x\\.y"},
429        {"x.\\\\y"},
430        {"x.y\\\\"},
431        {"\\\\x.y"},
432        {"a.b\\.c", "d"},
433    };
434
435    private static String[] composites = {
436        "a/b/c",
437        "a.b/c.d",
438        "a",
439        ".",
440        "a.",
441        "c.d",
442        ".e",
443        "a/x\\\\\\/y\\\\\\/z/b",
444        "a\\\\.b.c\\\\.d/e.f",
445        "a/b\\\\\\\\/c",
446        "x\\\\\\.y",
447        "x\\\\.y",
448        "x.\\\\\\\\y",
449        "x.y\\\\\\\\",
450        "\\\\\\\\x.y"
451    };
452
453    private static String[][] compositeComps = {
454        {"a", "b", "c"},
455        {"a.b", "c.d"},
456        {"a"},
457        {"."},
458        {"a."},  // unlike compound, kind sep is not consumed
459        {"c.d"},
460        {".e"},
461        {"a", "x\\/y\\/z", "b"},
462        {"a\\.b.c\\.d", "e.f"},
463        {"a", "b\\\\", "c"},
464        {"x\\\\.y"},
465        {"x\\.y"},
466        {"x.\\\\y"},
467        {"x.y\\\\"},
468        {"\\\\x.y"}
469    };
470
471    public static void main(String[] args) throws Exception {
472        if (args.length > 0) {
473            for (int i = 0; i < args.length; i++) {
474                print(args[0]);
475            }
476        } else {
477            print("x\\\\.y");
478            print("x\\.y");
479            print("x.\\\\y");
480            print("x.y\\\\");
481            print("\\\\x.y");
482        }
483
484        NameParser parser = new com.sun.jndi.cosnaming.CNNameParser();
485        for (int i = 0; i < compounds.length; i++) {
486            checkCompound(parser, compounds[i], compoundComps[i]);
487        }
488        for (int i = 0; i < composites.length; i++) {
489            checkComposite(composites[i], compositeComps[i]);
490        }
491
492        System.out.println("hardwire");
493        NameComponent[] foo = new NameComponent[1];
494        foo[0] = new NameComponent("foo\\", "bar");
495
496        System.out.println(cosNameToInsString(foo));
497        System.out.println(cosNameToName(foo));
498    }
499*/
500}
501