/*
* Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.cosnaming;
import javax.naming.*;
import java.util.Properties;
import java.util.Vector;
import java.util.Enumeration;
import org.omg.CosNaming.NameComponent;
/**
* Parsing routines for NameParser as well as COS Naming stringified names.
* This is used by CNCtx to create a NameComponent[] object and vice versa.
* It follows Section 4.5 of Interoperable Naming Service (INS) 98-10-11.
* In summary, the stringified form is a left-to-right, forward-slash
* separated name. id and kinds are separated by '.'. backslash is the
* escape character.
*
* @author Rosanna Lee
*/
final public class CNNameParser implements NameParser {
private static final Properties mySyntax = new Properties();
private static final char kindSeparator = '.';
private static final char compSeparator = '/';
private static final char escapeChar = '\\';
static {
mySyntax.put("jndi.syntax.direction", "left_to_right");
mySyntax.put("jndi.syntax.separator", ""+compSeparator);
mySyntax.put("jndi.syntax.escape", ""+escapeChar);
};
/**
* Constructs a new name parser for parsing names in INS syntax.
*/
public CNNameParser() {
}
/**
* Returns a CompoundName given a string in INS syntax.
* @param name The non-null string representation of the name.
* @return a non-null CompoundName
*/
public Name parse(String name) throws NamingException {
Vector comps = insStringToStringifiedComps(name);
return new CNCompoundName(comps.elements());
}
/**
* Creates a NameComponent[] from a Name structure.
* Used by CNCtx to convert the input Name arg into a NameComponent[].
* @param a CompoundName or a CompositeName;
* each component must be the stringified form of a NameComponent.
*/
static NameComponent[] nameToCosName(Name name)
throws InvalidNameException {
int len = name.size();
if (len == 0) {
return new NameComponent[0];
}
NameComponent[] answer = new NameComponent[len];
for (int i = 0; i < len; i++) {
answer[i] = parseComponent(name.get(i));
}
return answer;
}
/**
* Returns the INS stringified form of a NameComponent[].
* Used by CNCtx.getNameInNamespace(), CNCompoundName.toString().
*/
static String cosNameToInsString(NameComponent[] cname) {
StringBuffer str = new StringBuffer();
for ( int i = 0; i < cname.length; i++) {
if ( i > 0) {
str.append(compSeparator);
}
str.append(stringifyComponent(cname[i]));
}
return str.toString();
}
/**
* Creates a CompositeName from a NameComponent[].
* Used by ExceptionMapper and CNBindingEnumeration to convert
* a NameComponent[] into a composite name.
*/
static Name cosNameToName(NameComponent[] cname) {
Name nm = new CompositeName();
for ( int i = 0; cname != null && i < cname.length; i++) {
try {
nm.add(stringifyComponent(cname[i]));
} catch (InvalidNameException e) {
// ignore
}
}
return nm;
}
/**
* Converts an INS-syntax string name into a Vector in which
* each element of the vector contains a stringified form of
* a NameComponent.
*/
private static Vector insStringToStringifiedComps(String str)
throws InvalidNameException {
int len = str.length();
Vector components = new Vector(10);
char[] id = new char[len];
char[] kind = new char[len];
int idCount, kindCount;
boolean idMode;
for (int i = 0; i < len; ) {
idCount = kindCount = 0; // reset for new component
idMode = true; // always start off parsing id
while (i < len) {
if (str.charAt(i) == compSeparator) {
break;
} else if (str.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(str +
": unescaped \\ at end of component");
} else if (isMeta(str.charAt(i+1))) {
++i; // skip escape and let meta through
if (idMode) {
id[idCount++] = str.charAt(i++);
} else {
kind[kindCount++] = str.charAt(i++);
}
} else {
throw new InvalidNameException(str +
": invalid character being escaped");
}
} else if (idMode && str.charAt(i) == kindSeparator) {
// just look for the first kindSeparator
++i; // skip kind separator
idMode = false;
} else {
if (idMode) {
id[idCount++] = str.charAt(i++);
} else {
kind[kindCount++] = str.charAt(i++);
}
}
}
components.addElement(stringifyComponent(
new NameComponent(new String(id, 0, idCount),
new String(kind, 0, kindCount))));
if (i < len) {
++i; // skip separator
}
}
return components;
}
/**
* Return a NameComponent given its stringified form.
*/
private static NameComponent parseComponent(String compStr)
throws InvalidNameException {
NameComponent comp = new NameComponent();
int kindSep = -1;
int len = compStr.length();
int j = 0;
char[] newStr = new char[len];
boolean escaped = false;
// Find the kind separator
for (int i = 0; i < len && kindSep < 0; i++) {
if (escaped) {
newStr[j++] = compStr.charAt(i);
escaped = false;
} else if (compStr.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(compStr +
": unescaped \\ at end of component");
} else if (isMeta(compStr.charAt(i+1))) {
escaped = true;
} else {
throw new InvalidNameException(compStr +
": invalid character being escaped");
}
} else if (compStr.charAt(i) == kindSeparator) {
kindSep = i;
} else {
newStr[j++] = compStr.charAt(i);
}
}
// Set id
comp.id = new String(newStr, 0, j);
// Set kind
if (kindSep < 0) {
comp.kind = ""; // no kind separator
} else {
// unescape kind
j = 0;
escaped = false;
for (int i = kindSep+1; i < len; i++) {
if (escaped) {
newStr[j++] = compStr.charAt(i);
escaped = false;
} else if (compStr.charAt(i) == escapeChar) {
if (i + 1 >= len) {
throw new InvalidNameException(compStr +
": unescaped \\ at end of component");
} else if (isMeta(compStr.charAt(i+1))) {
escaped = true;
} else {
throw new InvalidNameException(compStr +
": invalid character being escaped");
}
} else {
newStr[j++] = compStr.charAt(i);
}
}
comp.kind = new String(newStr, 0, j);
}
return comp;
}
private static String stringifyComponent(NameComponent comp) {
StringBuffer one = new StringBuffer(escape(comp.id));
if (comp.kind != null && !comp.kind.equals("")) {
one.append(kindSeparator + escape(comp.kind));
}
if (one.length() == 0) {
return ""+kindSeparator; // if neither id nor kind specified
} else {
return one.toString();
}
}
/**
* Returns a string with '.', '\', '/' escaped. Used when
* stringifying the name into its INS stringified form.
*/
private static String escape(String str) {
if (str.indexOf(kindSeparator) < 0 &&
str.indexOf(compSeparator) < 0 &&
str.indexOf(escapeChar) < 0) {
return str; // no meta characters to escape
} else {
int len = str.length();
int j = 0;
char[] newStr = new char[len+len];
for (int i = 0; i < len; i++) {
if (isMeta(str.charAt(i))) {
newStr[j++] = escapeChar; // escape meta character
}
newStr[j++] = str.charAt(i);
}
return new String(newStr, 0, j);
}
}
/**
* In INS, there are three meta characters: '.', '/' and '\'.
*/
private static boolean isMeta(char ch) {
switch (ch) {
case kindSeparator:
case compSeparator:
case escapeChar:
return true;
}
return false;
}
/**
* An implementation of CompoundName that bypasses the parsing
* and stringifying code of the default CompoundName.
*/
static final class CNCompoundName extends CompoundName {
CNCompoundName(Enumeration enum_) {
super(enum_, CNNameParser.mySyntax);
}
public Object clone() {
return new CNCompoundName(getAll());
}
public Name getPrefix(int posn) {
Enumeration comps = super.getPrefix(posn).getAll();
return new CNCompoundName(comps);
}
public Name getSuffix(int posn) {
Enumeration comps = super.getSuffix(posn).getAll();
return new CNCompoundName(comps);
}
public String toString() {
try {
// Convert Name to NameComponent[] then stringify
return cosNameToInsString(nameToCosName(this));
} catch (InvalidNameException e) {
return super.toString();
}
}
private static final long serialVersionUID = -6599252802678482317L;
}
// for testing only
/*
private static void print(String input) {
try {
System.out.println("\n >>>>>> input: " + input);
System.out.println("--Compound Name: ");
NameParser parser = new CNNameParser();
Name name = parser.parse(input);
for (int i = 0; i < name.size(); i++) {
System.out.println("\t" + i + ": " + name.get(i));
NameComponent cp = parseComponent(name.get(i));
System.out.println("\t\t" + "id: " + cp.id + ";kind: " + cp.kind);
}
System.out.println("\t" + name.toString());
System.out.println("--Composite Name: ");
Name composite = new CompositeName(input);
for (int i = 0; i < composite.size(); i++) {
System.out.println("\t" + i+": " + composite.get(i));
}
System.out.println("\t" + composite.toString());
System.out.println("--Composite To NameComponent");
NameComponent[] names = nameToCosName(composite);
for (int i = 0; i < composite.size(); i++) {
System.out.println("\t" + i+": id: " + names[i].id + "; kind: " + names[i].kind);
}
System.out.println("\t" + cosNameToInsString(names));
} catch (NamingException e) {
System.out.println(e);
}
}
private static void checkName(Name name, String[] comps) throws Exception {
if (name.size() != comps.length) {
throw new Exception(
"test failed; incorrect component count in " + name + "; " +
"expecting " + comps.length + " got " + name.size());
}
for (int i = 0; i < name.size(); i++) {
if (!comps[i].equals(name.get(i))) {
throw new Exception (
"test failed; invalid component in " + name + "; " +
"expecting '" + comps[i] + "' got '" + name.get(i) + "'");
}
}
}
private static void checkCompound(NameParser parser,
String input, String[] comps) throws Exception {
checkName(parser.parse(input), comps);
}
private static void checkComposite(String input, String[] comps)
throws Exception {
checkName(new CompositeName(input), comps);
}
private static String[] compounds = {
"a/b/c",
"a.b/c.d",
"a",
".",
"a.",
"c.d",
".e",
"a/x\\/y\\/z/b",
"a\\.b.c\\.d/e.f",
"a/b\\\\/c",
"x\\\\.y",
"x\\.y",
"x.\\\\y",
"x.y\\\\",
"\\\\x.y",
"a.b\\.c/d"
};
private static String[][] compoundComps = {
{"a", "b", "c"},
{"a.b", "c.d"},
{"a"},
{"."},
{"a"},
{"c.d"},
{".e"},
{"a", "x\\/y\\/z", "b"},
{"a\\.b.c\\.d", "e.f"},
{"a", "b\\\\", "c"},
{"x\\\\.y"},
{"x\\.y"},
{"x.\\\\y"},
{"x.y\\\\"},
{"\\\\x.y"},
{"a.b\\.c", "d"},
};
private static String[] composites = {
"a/b/c",
"a.b/c.d",
"a",
".",
"a.",
"c.d",
".e",
"a/x\\\\\\/y\\\\\\/z/b",
"a\\\\.b.c\\\\.d/e.f",
"a/b\\\\\\\\/c",
"x\\\\\\.y",
"x\\\\.y",
"x.\\\\\\\\y",
"x.y\\\\\\\\",
"\\\\\\\\x.y"
};
private static String[][] compositeComps = {
{"a", "b", "c"},
{"a.b", "c.d"},
{"a"},
{"."},
{"a."}, // unlike compound, kind sep is not consumed
{"c.d"},
{".e"},
{"a", "x\\/y\\/z", "b"},
{"a\\.b.c\\.d", "e.f"},
{"a", "b\\\\", "c"},
{"x\\\\.y"},
{"x\\.y"},
{"x.\\\\y"},
{"x.y\\\\"},
{"\\\\x.y"}
};
public static void main(String[] args) throws Exception {
if (args.length > 0) {
for (int i = 0; i < args.length; i++) {
print(args[0]);
}
} else {
print("x\\\\.y");
print("x\\.y");
print("x.\\\\y");
print("x.y\\\\");
print("\\\\x.y");
}
NameParser parser = new com.sun.jndi.cosnaming.CNNameParser();
for (int i = 0; i < compounds.length; i++) {
checkCompound(parser, compounds[i], compoundComps[i]);
}
for (int i = 0; i < composites.length; i++) {
checkComposite(composites[i], compositeComps[i]);
}
System.out.println("hardwire");
NameComponent[] foo = new NameComponent[1];
foo[0] = new NameComponent("foo\\", "bar");
System.out.println(cosNameToInsString(foo));
System.out.println(cosNameToName(foo));
}
*/
}