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 
  26 package com.sun.jndi.cosnaming;
  27 
  28 import javax.naming.*;
  29 import java.util.Properties;
  30 import java.util.Vector;
  31 import java.util.Enumeration;
  32 
  33 import 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 
  46 final 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 }