Merge branch 'disable-repid-class-cache' into 'ibm-trunk'
Disable TypeDescriptorCache lookup for ClassDesc to Class resolution
Doing this requires creating a standard utility for converting from repid to class names / classes in yoko-util, which can then be used by code in both yoko-core and yoko-rmi-impl (thus eliminating duplication and inconsistency).
See merge request !50
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/Util.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/Util.java
index 2b3195a..49aa61f 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/Util.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/Util.java
@@ -337,253 +337,6 @@
}
}
- //
- // Convert a repository ID into a class name.
- // See the IDL-to-Java mapping, section 1.13.8.
- //
- public static String idToClassName(String id, String suffix) {
- String result = null;
-
- if (id.startsWith("IDL:")) {
- try {
- StringBuffer buf = new StringBuffer();
-
- int end = id.lastIndexOf(':');
- String s;
- if (end < 0)
- s = id.substring(4);
- else
- s = id.substring(4, end);
-
- //
- // If the ID contains a prefix, then fix each of the
- // dotted components of the prefix
- //
- int slash = s.indexOf('/');
- if (slash > 0) {
- String prefix = s.substring(0, slash);
- String rest = s.substring(slash + 1);
- java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
- prefix, ".");
- while (tokenizer.hasMoreTokens()) {
- String tok = tokenizer.nextToken();
- buf.append(fixName(tok));
- buf.append('.');
- }
- s = rest;
- }
-
- //
- // "Fix" the remainder of the ID
- //
- java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
- s, "/");
- while (tokenizer.hasMoreTokens()) {
- String tok = tokenizer.nextToken();
- buf.append(fixName(tok));
- if (tokenizer.hasMoreTokens())
- buf.append('.');
- }
-
- result = buf.toString() + suffix;
- } catch (IndexOutOfBoundsException ex) // if id has bad format
- {
- result = null;
- }
- }
- else if (id.startsWith ("RMI:")) {
- int end = id.indexOf (':', 4);
- result = end < 0
- ? id.substring (4)
- : id.substring (4, end);
- }
- if (result != null) {
- result = removeUnicodeEscapes(result);
- }
- return result;
- }
-
-
- /**
- * Remove Unicode \Uxxxx escape sequences from a string
- * received from the client ORB.
- *
- * @param in The input string.
- *
- * @return The string with any unicode escape sequences converted
- * into the appropriate character values.
- */
- public static String removeUnicodeEscapes(String in) {
- // if no escape sequences are in the string, this is easy
- int escape = in.indexOf("\\U");
- if (escape < 0) {
- return in;
- }
-
- // get a string buffer at least as long as the input string
- StringBuffer out = new StringBuffer(in.length());
- int start = 0;
-
- while (escape >= 0) {
- // add the next real segment to the buffer
- out.append(in.substring(start, escape));
- // step over the escape sequence
- escape += 2;
-
- int value = 0;
- for (int i=0; i<4; i++) {
- char ch = in.charAt(escape++);
- switch (ch) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- value = (value << 4) + ch - '0';
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- value = (value << 4) + 10 + ch - 'a';
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- value = (value << 4) + 10 + ch - 'A';
- break;
- default:
- // not sure what to do here. Just treat it as a 0 nibble
- value = (value << 4);
- }
- }
- // now append this as a char value
- out.append((char)value);
- // now step and find the next one
- start = escape;
- escape = in.indexOf("\\U", escape);
- }
- // don't forget the trailing segment
- if (start < in.length()) {
- out.append(in.substring(start));
- }
- return out.toString();
- }
-
- //
- // Convert a repository ID into a class.
- // See the IDL-to-Java mapping, section 1.13.8.
- //
- public static Class idToClass(String id, String suffix) {
- logger.fine("Searching for class from " + id + " using suffix " + suffix);
- Class result = null;
- String className = idToClassName(id, suffix);
-
- logger.fine("Converted class name is " + className);
-
- if (className != null) {
- try {
- // get the appropriate class for the loading.
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- result = javax.rmi.CORBA.Util.loadClass(className, null, loader);
- } catch (ClassNotFoundException ex) {
- logger.fine("Converted class name not found");
- // ignore
- }
- }
-
- return result;
- }
-
- //
- // Check the given name against Java keywords and reserved suffixes
- //
- public static String fixName(String name) {
- Assert._OB_assert(name.indexOf('.') == -1); // Not for absolute names
-
- int nameLen = name.length();
- if (nameLen == 0)
- return name;
-
- final String[] kwds = {
- //
- // Note: This list must be sorted alphabetically
- //
- "abstract", "assert", "boolean", "break", "byte", "case",
- "catch", "char", "class", "clone", "const", "continue",
- "default", "do", "double", "else", "equals", "extends",
- "false", "final", "finalize", "finally", "float", "for",
- "getClass", "goto", "hashCode", "if", "implements", "import",
- "instanceof", "int", "interface", "long", "native", "new",
- "notify", "notifyAll", "null", "package", "private",
- "protected", "public", "return", "short", "static", "super",
- "switch", "synchronized", "this", "throw", "throws",
- "toString", "transient", "true", "try", "void", "volatile",
- "wait", "while" };
-
- int l = 0;
- int r = kwds.length;
-
- while (l < r) {
- int m = (l + r) / 2;
- int res = kwds[m].compareTo(name);
- if (res == 0)
- return "_" + name;
- else if (res > 0)
- r = m;
- else
- l = m + 1;
- }
-
- //
- // Prepend an underscore for each of the reserved suffixes below
- //
- final String[] reserved = { "Helper", "Holder", "Operations", "POA",
- "POATie", "Package", "ValueFactory" };
-
- String result = name;
- String curr = name;
-
- boolean match;
- do {
- int currLen = curr.length();
-
- match = false;
- for (int i = 0; i < reserved.length; i++) {
- if (curr.endsWith(reserved[i])) {
- //
- // Prepend an underscore to result
- //
- result = "_" + result;
-
- //
- // Remove suffix from curr
- //
- int resLen = reserved[i].length();
- if (currLen > resLen)
- curr = curr.substring(0, currLen - resLen);
- else
- curr = "";
-
- match = true;
- break;
- }
- }
- } while (match);
-
- return result;
- }
-
public static void insertException(org.omg.CORBA.Any any,
java.lang.Exception ex) {
//
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java
index c1b9840..6f6ed0d 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueFactoryManager.java
@@ -20,6 +20,8 @@
import java.util.logging.Logger;
import java.util.logging.Level;
+import org.apache.yoko.util.cmsf.RepIds;
+
public final class ValueFactoryManager {
static final Logger logger = Logger.getLogger(ValueFactoryManager.class.getName());
//
@@ -176,7 +178,7 @@
//
// Try to convert the repository ID into a class name.
//
- Class c = Util.idToClass(id, "DefaultFactory");
+ Class c = RepIds.query(id).suffix("DefaultFactory").toClass();
if (c != null) {
try {
logger.finer("Attempting to create value factory from class " + c.getName());
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueReader.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueReader.java
index bcaf2aa..8796418 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueReader.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueReader.java
@@ -29,6 +29,7 @@
import org.apache.yoko.orb.CORBA.InputStream;
import org.apache.yoko.orb.CORBA.OutputStream;
import org.apache.yoko.orb.OCI.Buffer;
+import org.apache.yoko.util.cmsf.RepIds;
import org.omg.CORBA.Any;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.CustomMarshal;
@@ -272,7 +273,7 @@
if (WStringValueHelper.id().equals(id))
return new WStringValueHelper();
- final Class helperClass = Util.idToClass(id, "Helper");
+ final Class helperClass = RepIds.query(id).suffix("Helper").toClass();
if (helperClass != null) {
try {
@@ -942,7 +943,7 @@
}
}
- final String className = Util.idToClassName(repid, "");
+ final String className = RepIds.query(repid).toClassName();
String codebase = h.codebase;
if (codebase == null) {
@@ -1319,14 +1320,14 @@
// that is compatible with the base type. This will require resolving the classes.
if (id == null) {
// see if we can resolve the type for the stored type code
- final Class<?> baseType = Util.idToClass(tcId, "");
+ final Class<?> baseType = RepIds.query(tcId).toClass();
if (baseType != null) {
for (idPos = 0; idPos < h.ids.length; idPos++) {
if (logger.isLoggable(Level.FINER))
logger.finer(String.format(
"Considering base types of id \"%s\" against \"%s\"",
tcId, h.ids[idPos]));
- final Class idType = Util.idToClass(h.ids[idPos], "");
+ final Class idType = RepIds.query(h.ids[idPos]).toClass();
if (idType != null) {
// if these classes are assignment compatible, go with that as the type.
if (logger.isLoggable(Level.FINER))
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
index 2b7d927..b4df52f 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
@@ -22,6 +22,7 @@
import javax.rmi.CORBA.ValueHandler;
import org.apache.yoko.orb.CORBA.ORB;
+import org.apache.yoko.util.cmsf.RepIds;
import org.apache.yoko.util.osgi.ProviderLocator;
import org.omg.CORBA.WStringValueHelper;
import org.omg.CORBA.portable.BoxedValueHelper;
@@ -218,7 +219,7 @@
org.omg.CORBA.TypeCode origType = org.apache.yoko.orb.CORBA.TypeCode
._OB_getOrigType(type);
String id = origType.id();
- helperClass = Util.idToClass(id, "Helper");
+ helperClass = RepIds.query(id).suffix("Helper").toClass();
} catch (org.omg.CORBA.TypeCodePackage.BadKind ex) {
Assert._OB_assert(ex);
}
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ClassDescDescriptor.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ClassDescDescriptor.java
index c2ff742..eff9cb1 100644
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ClassDescDescriptor.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ClassDescDescriptor.java
@@ -10,6 +10,7 @@
import javax.rmi.CORBA.ClassDesc;
import javax.rmi.CORBA.Util;
+import org.apache.yoko.util.cmsf.RepIds;
import org.omg.CORBA.MARSHAL;
public class ClassDescDescriptor extends ClassBaseDescriptor {
@@ -40,21 +41,10 @@
String repid = (String) repid_field.get(desc);
String codebase = (String) codebase_field.get(desc);
- TypeDescriptor typeDesc = repository.getDescriptor(repid);
- if (null != typeDesc) {
- Class<?> type = typeDesc.getJavaClass();
- if (null != type) return type;
- }
+ Class<?> result = RepIds.query(repid).codebase(codebase).toClass();
+ if (null != result) return result;
- int beg = repid.indexOf(':');
- int end = repid.indexOf(':', beg + 1);
-
- className = repid.substring(beg + 1, end);
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
-
- return Util.loadClass(className, codebase, loader);
- } catch (ClassNotFoundException ex) {
- throw (MARSHAL)new MARSHAL("cannot load class " + className).initCause(ex);
+ throw new MARSHAL(String.format("Cannot load class \"%s\"", className));
} catch (IllegalAccessException ex) {
throw (MARSHAL)new MARSHAL("no such field: " + ex).initCause(ex);
}
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
index 72d2d75..2663c2c 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
@@ -363,136 +363,4 @@
return clzdesc;
}
-
- public static String idToClass(String repid) {
- // debug
- logger.finer("idToClass " + repid);
-
- if (repid.startsWith("IDL:")) {
-
- ByteString id = new ByteString(repid);
-
- try {
- int end = id.lastIndexOf(':');
- ByteString s = end < 0 ? id.substring(4) : id.substring(4, end);
-
- ByteBuffer bb = new ByteBuffer();
-
- //
- // reverse order of dot-separated name components up
- // till the first slash.
- //
- int firstSlash = s.indexOf('/');
- if (firstSlash > 0) {
- ByteString prefix = s.substring(0, firstSlash);
- ByteString[] elems = prefix.split('.');
-
- for (int i = elems.length - 1; i >= 0; i--) {
- bb.append(fixName(elems[i]));
- bb.append('.');
- }
-
- s = s.substring(firstSlash + 1);
- }
-
- //
- // Append slash-separated name components ...
- //
- ByteString[] elems = s.split('/');
- for (int i = 0; i < elems.length; i++) {
- bb.append(fixName(elems[i]));
- if (i != elems.length - 1)
- bb.append('.');
- }
-
- String result = bb.toString();
-
- logger.finer("idToClassName " + repid + " => " + result);
-
- return result;
- } catch (IndexOutOfBoundsException ex) {
- logger.log(Level.FINE, "idToClass " + ex.getMessage(), ex);
- return null;
- }
-
- } else if (repid.startsWith("RMI:")) {
- int end = repid.indexOf(':', 4);
- return end < 0 ? repid.substring(4) : repid.substring(4, end);
- }
-
- return null;
- }
-
- static String fixName(String name) {
- return (new ByteString(name)).toString();
- }
-
- static ByteString fixName(ByteString name) {
- if (keyWords.contains(name)) {
- ByteBuffer buf = new ByteBuffer();
- buf.append('_');
- buf.append(name);
- return buf.toByteString();
- }
-
- ByteString result = name;
- ByteString current = name;
-
- boolean match = true;
- while (match) {
-
- int len = current.length();
- match = false;
-
- for (ByteString reservedPostfix : reservedPostfixes) {
- if (current.endsWith(reservedPostfix)) {
- ByteBuffer buf = new ByteBuffer();
- buf.append('_');
- buf.append(result);
- result = buf.toByteString();
-
- int resultLen = reservedPostfix.length();
- if (len > resultLen)
- current = current.substring(0, len - resultLen);
- else
- current = new ByteString("");
-
- match = true;
- break;
- }
- }
-
- }
-
- return name;
- }
-
- private static final Set<ByteString> keyWords;
- private static final Set<ByteString> reservedPostfixes;
-
- static {
- keyWords = createByteStringSet(
- "abstract", "boolean", "break", "byte", "case",
- "catch", "char", "class", "clone", "const", "continue",
- "default", "do", "double", "else", "equals", "extends",
- "false", "final", "finalize", "finally", "float", "for",
- "getClass", "goto", "hashCode", "if", "implements", "import",
- "instanceof", "int", "interface", "long", "native", "new",
- "notify", "notifyAll", "null", "package", "private",
- "protected", "public", "return", "short", "static", "super",
- "switch", "synchronized", "this", "throw", "throws",
- "toString", "transient", "true", "try", "void", "volatile",
- "wait", "while");
- reservedPostfixes = createByteStringSet(
- "Helper", "Holder", "Operations", "POA", "POATie", "Package", "ValueFactory");
- }
-
- private static Set<ByteString> createByteStringSet(String...words) {
- final Set<ByteString> set = new HashSet<>(words.length);
- for (String word : words) {
- set.add(new ByteString(word));
- }
- return Collections.unmodifiableSet(set);
- }
-
}
diff --git a/yoko-util/src/main/java/org/apache/yoko/util/cmsf/RepIds.java b/yoko-util/src/main/java/org/apache/yoko/util/cmsf/RepIds.java
new file mode 100644
index 0000000..3fd7d4a
--- /dev/null
+++ b/yoko-util/src/main/java/org/apache/yoko/util/cmsf/RepIds.java
@@ -0,0 +1,288 @@
+package org.apache.yoko.util.cmsf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import javax.rmi.CORBA.Util;
+
+public enum RepIds {
+ ;
+
+ public interface Query {
+ public Query suffix(String suffix);
+ public Query codebase(String codebase);
+ public Class<?> toClass();
+ public String toClassName();
+ }
+
+ private static final class QueryImpl implements Query {
+ public final String repid;
+ public final String suffix;
+ public final String codebase;
+
+ private QueryImpl(String repid) {
+ this(Objects.requireNonNull(repid), "", null);
+ }
+
+ private QueryImpl(String repid, String suffix, String codebase) {
+ this.repid = repid;
+ this.suffix = suffix;
+ this.codebase = codebase;
+ }
+
+ @Override
+ public QueryImpl suffix(String suffix) {
+ return new QueryImpl(repid, Objects.requireNonNull(suffix), codebase);
+ }
+
+ @Override
+ public QueryImpl codebase(String codebase) {
+ return new QueryImpl(repid, suffix, codebase);
+ }
+
+ @Override
+ public Class<?> toClass() {
+ return RepIds.toClass(this);
+ }
+
+ @Override
+ public String toClassName() {
+ return RepIds.toClassName(this);
+ }
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(RepIds.class.getName());
+
+ public static Query query(String repid) {
+ return new QueryImpl(repid);
+ }
+
+ private static Class<?> toClass(final QueryImpl query) {
+ final String repid = query.repid;
+ final String suffix = query.suffix;
+ final String codebase = query.codebase;
+ if (LOGGER.isLoggable(Level.FINE))
+ LOGGER.fine(String.format("Searching for class from repid \"%s\" using suffix \"%s\"", repid, suffix));
+ Class<?> result = null;
+
+ //Special case IDL:omg.org/CORBA/WStringValue:1.0
+ if ("IDL:omg.org/CORBA/WStringValue:1.0".equals(repid) && "".equals(suffix)) return String.class;
+
+ final String className = toClassName(query);
+
+ if (LOGGER.isLoggable(Level.FINE))
+ LOGGER.fine(String.format("Class name from repid \"%s\" using suffix \"%s\" is \"%s\"", repid, suffix, className));
+
+ if (className != null) {
+ try {
+ // get the appropriate class for the loading.
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ result = Util.loadClass(className, codebase, loader);
+ } catch (ClassNotFoundException ex) {
+ if (LOGGER.isLoggable(Level.FINE))
+ LOGGER.fine(String.format("Class \"%s\" not found", className));
+ // ignore
+ }
+ }
+
+ return result;
+ }
+
+ private static final Pattern dotPattern = Pattern.compile(Pattern.quote("."));
+ private static final Pattern slashPattern = Pattern.compile(Pattern.quote("/"));
+
+ private static String toClassName(QueryImpl query) {
+ final String repid = query.repid;
+ final String suffix = query.suffix;
+
+ //Special case IDL:omg.org/CORBA/WStringValue:1.0
+ if ("IDL:omg.org/CORBA/WStringValue:1.0".equals(repid) && "".equals(suffix)) return String.class.getName();
+
+ String result = null;
+ if (repid.startsWith("IDL:")) {
+ result = idlToClassName(repid);
+ } else if (repid.startsWith("RMI:")) {
+ result = rmiToClassName(repid);
+ }
+ if (result != null) {
+ result += suffix;
+ result = removeUnicodeEscapes(result);
+ }
+ return result;
+ }
+
+ private static String rmiToClassName(final String repid) {
+ String result;
+ final int end = repid.indexOf (':', 4);
+ result = end < 0 ? repid.substring (4) : repid.substring (4, end);
+ return result;
+ }
+
+ private static String idlToClassName(final String repid) {
+ try {
+ final StringBuilder sb = new StringBuilder(repid.length());
+
+ final int end = repid.lastIndexOf(':');
+ String s = end < 0 ? repid.substring(4) : repid.substring(4, end);
+
+ //
+ // reverse order of dot-separated name components up
+ // till the first slash.
+ //
+ final int firstSlash = s.indexOf('/');
+ if (firstSlash > 0) {
+ String prefix = s.substring(0, firstSlash);
+ String[] elems = dotPattern.split(prefix);
+ Collections.reverse(Arrays.asList(elems)); //reverses the order in the underlying array - i.e. 'elems'
+ for (String elem: elems) {
+ sb.append(fixName(elem)).append('.');
+ }
+
+ s = s.substring(firstSlash + 1);
+ }
+
+ //
+ // Append slash-separated name components ...
+ //
+ for (String elem: slashPattern.split(s)) {
+ sb.append(fixName(elem)).append('.');
+ }
+ sb.deleteCharAt(sb.length() - 1); // eliminate final '.'
+
+ return sb.toString();
+ } catch (IndexOutOfBoundsException ex) {
+ // id has bad format
+ return null;
+ }
+ }
+
+ private static String removeUnicodeEscapes(String in) {
+ // if no escape sequences are in the string, this is easy
+ int escape = in.indexOf("\\U");
+ if (escape < 0) {
+ return in;
+ }
+
+ StringBuilder out = new StringBuilder(in.length());
+ int start = 0;
+
+ while (escape >= 0) {
+ out.append(in.substring(start, escape));
+ // step over the escape sequence
+ escape += 2;
+
+ int value = 0;
+ for (int i=0; i<4; i++) {
+ char ch = in.charAt(escape++);
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ value = (value << 4) + ch - '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ value = (value << 4) + 10 + ch - 'a';
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ value = (value << 4) + 10 + ch - 'A';
+ break;
+ default:
+ // not sure what to do here. Just treat it as a 0 nibble
+ value = (value << 4);
+ }
+ }
+ // now append this as a char value
+ out.append((char)value);
+ // now step and find the next one
+ start = escape;
+ escape = in.indexOf("\\U", escape);
+ }
+ // don't forget the trailing segment
+ if (start < in.length()) {
+ out.append(in.substring(start));
+ }
+ return out.toString();
+ }
+
+ private static final Set<String> keywords = createStringSet(
+ "abstract", "assert", "boolean", "break", "byte", "case",
+ "catch", "char", "class", "clone", "const", "continue",
+ "default", "do", "double", "else", "equals", "extends",
+ "false", "final", "finalize", "finally", "float", "for",
+ "getClass", "goto", "hashCode", "if", "implements", "import",
+ "instanceof", "int", "interface", "long", "native", "new",
+ "notify", "notifyAll", "null", "package", "private",
+ "protected", "public", "return", "short", "static", "super",
+ "switch", "synchronized", "this", "throw", "throws",
+ "toString", "transient", "true", "try", "void", "volatile",
+ "wait", "while");
+
+ private static Set<String> createStringSet(String...strings) {
+ return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(strings)));
+ }
+
+ private static final List<String> reservedSuffixes = createStringList(
+ "Helper", "Holder", "Operations", "POA",
+ "POATie", "Package", "ValueFactory");
+
+ private static List<String> createStringList(String...strings) {
+ return Collections.unmodifiableList(Arrays.asList(strings));
+ }
+
+ private static String fixName(String name) {
+ assert(name.indexOf('.') == -1); // Not for absolute names
+
+ int nameLen = name.length();
+ if (nameLen == 0)
+ return name;
+
+ if (keywords.contains(name)) return "_" + name;
+
+ //
+ // Prepend an underscore for each of the reserved suffixes
+ //
+ String result = name;
+ String curr = name;
+
+ OUTER_LOOP: while (true) {
+ for (String reservedSuffix: reservedSuffixes) {
+ if (curr.endsWith(reservedSuffix)) {
+ result = "_" + result;
+
+ int currLength = curr.length();
+ int resLength = reservedSuffix.length();
+ if (currLength == resLength)
+ return result;
+ curr = curr.substring(0, currLength - resLength);
+ continue OUTER_LOOP;
+ }
+ }
+ return result;
+ }
+ }
+}