Author: cutting
Date: Fri Nov 20 19:22:34 2009
New Revision: 882689
URL: http://svn.apache.org/viewvc?rev=882689&view=rev
Log:
AVRO-221. Mangle Java reserved words in generated code to avoid name
conflicts. Contributed by Philip Zeyliger.
Modified:
hadoop/avro/trunk/CHANGES.txt
hadoop/avro/trunk/src/java/org/apache/avro/specific/SpecificCompiler.java
hadoop/avro/trunk/src/test/java/org/apache/avro/specific/TestSpecificCompiler.java
Modified: hadoop/avro/trunk/CHANGES.txt
URL:
http://svn.apache.org/viewvc/hadoop/avro/trunk/CHANGES.txt?rev=882689&r1=882688&r2=882689&view=diff
==============================================================================
--- hadoop/avro/trunk/CHANGES.txt (original)
+++ hadoop/avro/trunk/CHANGES.txt Fri Nov 20 19:22:34 2009
@@ -135,6 +135,9 @@
AVRO-224. Code cleanup: cleaner distinction between public and private
methods (massie)
+ AVRO-221. Mangle Java reserved words in generated code to avoid
+ name conflicts. (Philip Zeyliger via cutting)
+
Avro 1.2.0 (14 October 2009)
INCOMPATIBLE CHANGES
Modified:
hadoop/avro/trunk/src/java/org/apache/avro/specific/SpecificCompiler.java
URL:
http://svn.apache.org/viewvc/hadoop/avro/trunk/src/java/org/apache/avro/specific/SpecificCompiler.java?rev=882689&r1=882688&r2=882689&view=diff
==============================================================================
--- hadoop/avro/trunk/src/java/org/apache/avro/specific/SpecificCompiler.java
(original)
+++ hadoop/avro/trunk/src/java/org/apache/avro/specific/SpecificCompiler.java
Fri Nov 20 19:22:34 2009
@@ -21,6 +21,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -32,11 +33,30 @@
import org.apache.avro.Protocol.Message;
import org.apache.avro.tool.Tool;
-/** Generate specific Java interfaces and classes for protocols and schemas. */
+/**
+ * Generate specific Java interfaces and classes for protocols and schemas.
+ *
+ * Java reserved keywords are mangled to preserve compilation.
+ */
public class SpecificCompiler {
private final Set<Schema> queue = new HashSet<Schema>();
private final Protocol protocol;
+ /** List of Java reserved words from
+ * <a
href="http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html">Sun</a>.
+ */
+ private static final Set<String> RESERVED_WORDS = new HashSet<String>(
+ Arrays.asList(new String[] {
+ "abstract", "assert", "boolean", "break", "byte", "case", "catch",
+ "char", "class", "const", "continue", "default", "do", "double",
+ "else", "enum", "extends", "false", "final", "finally", "float",
+ "for", "goto", "if", "implements", "import", "instanceof", "int",
+ "interface", "long", "native", "new", "null", "package", "private",
+ "protected", "public", "return", "short", "static", "strictfp",
+ "super", "switch", "synchronized", "this", "throw", "throws",
+ "transient", "true", "try", "void", "volatile", "while"
+ }));
+
public SpecificCompiler(Protocol protocol) {
// enqueue all types
for (Schema s : protocol.getTypes()) {
@@ -91,6 +111,13 @@
compiler.compileToDestination(dest);
}
+ static String mangle(String word) {
+ if (RESERVED_WORDS.contains(word)) {
+ return word + "$";
+ }
+ return word;
+ }
+
/** Recursively enqueue schemas that need a class generated. */
private void enqueue(Schema schema) {
if (queue.contains(schema)) return;
@@ -150,7 +177,7 @@
outputFile.path = makePath(protocol.getName(), protocol.getNamespace());
StringBuilder out = new StringBuilder();
header(out, protocol.getNamespace());
- line(out, 0, "public interface "+protocol.getName()+" {");
+ line(out, 0, "public interface "+mangle(protocol.getName())+" {");
line(out, 1, "public static final Protocol _PROTOCOL = Protocol.parse(\""
+esc(protocol)+"\");");
for (Map.Entry<String,Message> e : protocol.getMessages().entrySet()) {
@@ -158,7 +185,7 @@
Message message = e.getValue();
Schema request = message.getRequest();
Schema response = message.getResponse();
- line(out, 1, unbox(response)+" "+name+"("+params(request)+")");
+ line(out, 1, unbox(response)+" "+ mangle(name)+"("+params(request)+")");
line(out, 2,"throws
AvroRemoteException"+errors(message.getErrors())+";");
}
line(out, 0, "}");
@@ -207,7 +234,7 @@
StringBuilder b = new StringBuilder();
int count = 0;
for (Map.Entry<String, Schema> param : request.getFieldSchemas()) {
- String paramName = param.getKey();
+ String paramName = mangle(param.getKey());
b.append(unbox(param.getValue()));
b.append(" ");
b.append(paramName);
@@ -221,7 +248,7 @@
StringBuilder b = new StringBuilder();
for (Schema error : errs.getTypes().subList(1, errs.getTypes().size())) {
b.append(", ");
- b.append(error.getName());
+ b.append(mangle(error.getName()));
}
return b.toString();
}
@@ -243,8 +270,8 @@
+esc(schema)+"\");");
// field declations
for (Map.Entry<String,Schema.Field> field: schema.getFields().entrySet())
- line(out, 1,
- "public "+unbox(field.getValue().schema())+"
"+field.getKey()+";");
+ line(out, 1, "public " + unbox(field.getValue().schema()) + " "
+ + mangle(field.getKey()) + ";");
// schema method
line(out, 1, "public Schema getSchema() { return _SCHEMA; }");
// get method
@@ -252,7 +279,7 @@
line(out, 2, "switch (_field) {");
int i = 0;
for (Map.Entry<String, Schema> field : schema.getFieldSchemas())
- line(out, 2, "case "+(i++)+": return "+field.getKey()+";");
+ line(out, 2, "case "+(i++)+": return "+mangle(field.getKey())+";");
line(out, 2, "default: throw new AvroRuntimeException(\"Bad index\");");
line(out, 2, "}");
line(out, 1, "}");
@@ -274,7 +301,7 @@
StringBuilder b = new StringBuilder();
int count = 0;
for (String symbol : schema.getEnumSymbols()) {
- b.append(symbol);
+ b.append(mangle(symbol));
if (++count < schema.getEnumSymbols().size())
b.append(", ");
}
@@ -302,7 +329,7 @@
case RECORD:
case ENUM:
case FIXED:
- return schema.getName();
+ return mangle(schema.getName());
case ARRAY:
return "GenericArray<"+type(schema.getElementType())+">";
case MAP:
Modified:
hadoop/avro/trunk/src/test/java/org/apache/avro/specific/TestSpecificCompiler.java
URL:
http://svn.apache.org/viewvc/hadoop/avro/trunk/src/test/java/org/apache/avro/specific/TestSpecificCompiler.java?rev=882689&r1=882688&r2=882689&view=diff
==============================================================================
---
hadoop/avro/trunk/src/test/java/org/apache/avro/specific/TestSpecificCompiler.java
(original)
+++
hadoop/avro/trunk/src/test/java/org/apache/avro/specific/TestSpecificCompiler.java
Fri Nov 20 19:22:34 2009
@@ -22,7 +22,9 @@
import java.io.File;
import java.util.Collection;
+import java.util.Iterator;
+import org.apache.avro.Protocol;
import org.apache.avro.Schema;
import org.apache.avro.TestSchema;
import org.apache.avro.specific.SpecificCompiler.OutputFile;
@@ -62,6 +64,74 @@
assertTrue(o.contents.contains("public enum Test"));
}
+ @Test
+ public void testMangleIfReserved() {
+ assertEquals("foo", SpecificCompiler.mangle("foo"));
+ assertEquals("goto$", SpecificCompiler.mangle("goto"));
+ }
+
+ @Test
+ public void testManglingForProtocols() {
+ String protocolDef = "" +
+ "{ \"protocol\": \"default\",\n" +
+ " \"types\":\n" +
+ " [\n" +
+ " {\n" +
+ " \"name\": \"finally\",\n" +
+ " \"type\": \"error\",\n" +
+ " \"fields\": [{\"name\": \"catch\", \"type\": \"boolean\"}]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"messages\": { \"goto\":\n" +
+ " { \"request\": [{\"name\": \"break\", \"type\": \"string\"}],\n" +
+ " \"response\": \"string\",\n" +
+ " \"errors\": [\"finally\"]\n" +
+ " }" +
+ " }\n" +
+ "}\n";
+ Iterator<OutputFile> i =
+ new SpecificCompiler(Protocol.parse(protocolDef)).compile().iterator();
+ String errType = i.next().contents;
+ String protocol = i.next().contents;
+
+ assertTrue(errType.contains("public class finally$ extends
SpecificExceptionBase"));
+ assertTrue(errType.contains("public boolean catch$;"));
+
+ assertTrue(protocol.contains("Utf8 goto$(Utf8 break$)"));
+ assertTrue(protocol.contains("public interface default$"));
+ assertTrue(protocol.contains("throws AvroRemoteException, finally$"));
+
+ }
+
+ @Test
+ public void testManglingForRecords() {
+ String schema = "" +
+ "{ \"name\": \"volatile\", \"type\": \"record\", " +
+ " \"fields\": [ {\"name\": \"package\", \"type\": \"string\" }," +
+ " {\"name\": \"short\", \"type\": \"volatile\" } ] }";
+ Collection<OutputFile> c =
+ new SpecificCompiler(Schema.parse(schema)).compile();
+ assertEquals(1, c.size());
+ String contents = c.iterator().next().contents;
+
+ assertTrue(contents.contains("public Utf8 package$;"));
+ assertTrue(contents.contains("class volatile$ extends"));
+ assertTrue(contents.contains("volatile$ short$;"));
+ }
+
+ @Test
+ public void testManglingForEnums() {
+ String enumSchema = "" +
+ "{ \"name\": \"instanceof\", \"type\": \"enum\"," +
+ " \"symbols\": [\"new\", \"super\", \"switch\"] }";
+ Collection<OutputFile> c =
+ new SpecificCompiler(Schema.parse(enumSchema)).compile();
+ assertEquals(1, c.size());
+ String contents = c.iterator().next().contents;
+
+ assertTrue(contents.contains("new$"));
+ }
+
/**
* Called from TestSchema as part of its comprehensive checks.
*/