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 }