You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ch...@apache.org on 2008/09/02 22:44:44 UTC
svn commit: r691373 - in
/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main:
java/org/apache/activemq/protobuf/
java/org/apache/activemq/protobuf/compiler/ javacc/
Author: chirino
Date: Tue Sep 2 13:44:44 2008
New Revision: 691373
URL: http://svn.apache.org/viewvc?rev=691373&view=rev
Log:
Added better compatibility with the existing protobuf java api.. made implementing it's unit tests easier.
Added:
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java
Modified:
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java
activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/javacc/proto-parser.jj
Modified: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java?rev=691373&r1=691372&r2=691373&view=diff
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java (original)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java Tue Sep 2 13:44:44 2008
@@ -16,18 +16,38 @@
*/
package org.apache.activemq.protobuf;
+import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.ExtensionRegistry;
+import com.google.protobuf.InvalidProtocolBufferException;
import static org.apache.activemq.protobuf.WireInfo.*;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
abstract public class Message<T> {
protected int memoizedSerializedSize = -1;
+ static protected <T> void addAll(Iterable<T> values, Collection<? super T> list) {
+ if (values instanceof Collection) {
+ @SuppressWarnings("unsafe")
+ Collection<T> collection = (Collection<T>)values;
+ list.addAll(collection);
+ } else {
+ for (T value : values) {
+ list.add(value);
+ }
+ }
+ }
+
static protected void writeGroup(CodedOutputStream output, int tag, Message message) throws IOException {
output.writeTag(tag, WIRETYPE_START_GROUP);
message.writeTo(output);
@@ -46,7 +66,7 @@
return group;
}
- static protected <T extends Message> T readMessage(CodedInputStream input, ExtensionRegistry extensionRegistry, T message) throws IOException {
+ static protected <T extends Message> T readMessage(CodedInputStream input, ExtensionRegistry extensionRegistry, T message) throws IOException {
int length = input.readRawVarint32();
int oldLimit = input.pushLimit(length);
message.mergeFrom(input, extensionRegistry);
@@ -54,17 +74,15 @@
input.popLimit(oldLimit);
return message;
}
-
+
static protected int computeGroupSize(int tag, Message message) {
return CodedOutputStream.computeTagSize(tag) * 2 + message.serializedSize();
}
-
+
static protected int computeMessageSize(int tag, Message message) {
int t = message.serializedSize();
- return CodedOutputStream.computeTagSize(tag) +
- CodedOutputStream.computeRawVarint32Size(t)+t;
+ return CodedOutputStream.computeTagSize(tag) + CodedOutputStream.computeRawVarint32Size(t) + t;
}
-
abstract public T mergeFrom(T other);
@@ -82,5 +100,98 @@
abstract public void clear();
- abstract public boolean isInitialized();
+ abstract public T assertInitialized() throws com.google.protobuf.UninitializedMessageException;
+
+ public byte[] toByteArray() {
+ try {
+ byte[] result = new byte[serializedSize()];
+ CodedOutputStream output = CodedOutputStream.newInstance(result);
+ writeTo(output);
+ output.checkNoSpaceLeft();
+ return result;
+ } catch (IOException e) {
+ throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e);
+ }
+ }
+
+ protected List<String> prefix(List<String> missingFields, String prefix) {
+ ArrayList<String> rc = new ArrayList<String>(missingFields.size());
+ for (String v : missingFields) {
+ rc.add(prefix+v);
+ }
+ return rc;
+ }
+
+ public void writeTo(OutputStream output) throws IOException {
+ CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
+ writeTo(codedOutput);
+ codedOutput.flush();
+ }
+
+ public T mergeFrom(ByteString data) throws InvalidProtocolBufferException {
+ try {
+ CodedInputStream input = data.newCodedInput();
+ mergeFrom(input);
+ input.checkLastTagWas(0);
+ return (T)this;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("Reading from a ByteString threw an IOException (should " + "never happen).", e);
+ }
+ }
+
+ public T mergeFrom(ByteString data, ExtensionRegistry extensionRegistry) throws InvalidProtocolBufferException {
+ try {
+ CodedInputStream input = data.newCodedInput();
+ mergeFrom(input, extensionRegistry);
+ input.checkLastTagWas(0);
+ return (T)this;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("Reading from a ByteString threw an IOException (should " + "never happen).", e);
+ }
+ }
+
+ public T mergeFrom(byte[] data) throws InvalidProtocolBufferException {
+ try {
+ CodedInputStream input = CodedInputStream.newInstance(data);
+ mergeFrom(input);
+ input.checkLastTagWas(0);
+ return (T)this;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("Reading from a byte array threw an IOException (should " + "never happen).", e);
+ }
+ }
+
+ public T mergeFrom(byte[] data, ExtensionRegistry extensionRegistry) throws InvalidProtocolBufferException {
+ try {
+ CodedInputStream input = CodedInputStream.newInstance(data);
+ mergeFrom(input, extensionRegistry);
+ input.checkLastTagWas(0);
+ return (T)this;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("Reading from a byte array threw an IOException (should " + "never happen).", e);
+ }
+ }
+
+ public T mergeFrom(InputStream input) throws IOException {
+ CodedInputStream codedInput = CodedInputStream.newInstance(input);
+ mergeFrom(codedInput);
+ codedInput.checkLastTagWas(0);
+ return (T)this;
+ }
+
+ public T mergeFrom(InputStream input, ExtensionRegistry extensionRegistry) throws IOException {
+ CodedInputStream codedInput = CodedInputStream.newInstance(input);
+ mergeFrom(codedInput, extensionRegistry);
+ codedInput.checkLastTagWas(0);
+ return (T)this;
+ }
+
}
Added: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java?rev=691373&view=auto
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java (added)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java Tue Sep 2 13:44:44 2008
@@ -0,0 +1,82 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.
+// http://code.google.com/p/protobuf/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.activemq.protobuf;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Thrown when attempting to build a protocol message that is missing required
+ * fields. This is a {@code RuntimeException} because it normally represents
+ * a programming error: it happens when some code which constructs a message
+ * fails to set all the fields. {@code parseFrom()} methods <b>do not</b>
+ * throw this; they throw an {@link InvalidProtocolBufferException} if
+ * required fields are missing, because it is not a programming error to
+ * receive an incomplete message. In other words,
+ * {@code UninitializedMessageException} should never be thrown by correct
+ * code, but {@code InvalidProtocolBufferException} might be.
+ *
+ * @author kenton@google.com Kenton Varda
+ */
+public class UninitializedMessageException extends RuntimeException {
+
+ public UninitializedMessageException(List<String> missingFields) {
+ super(buildDescription(missingFields));
+ this.missingFields = missingFields;
+ }
+
+ private final List<String> missingFields;
+
+ /**
+ * Get a list of human-readable names of required fields missing from this
+ * message. Each name is a full path to a field, e.g. "foo.bar[5].baz".
+ */
+ public List<String> getMissingFields() {
+ return Collections.unmodifiableList(missingFields);
+ }
+
+ /**
+ * Converts this exception to an {@link InvalidProtocolBufferException}.
+ * When a parsed message is missing required fields, this should be thrown
+ * instead of {@code UninitializedMessageException}.
+ */
+ public com.google.protobuf.InvalidProtocolBufferException asInvalidProtocolBufferException() {
+ return new InvalidProtocolBufferException(getMessage());
+ }
+
+ /** Construct the description string for this exception. */
+ private static String buildDescription(List<String> missingFields) {
+ StringBuilder description =
+ new StringBuilder("Message missing required fields: ");
+ boolean first = true;
+ for (String field : missingFields) {
+ if (first) {
+ first = false;
+ } else {
+ description.append(", ");
+ }
+ description.append(field);
+ }
+ return description.toString();
+ }
+
+}
Modified: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java?rev=691373&r1=691372&r2=691373&view=diff
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java (original)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java Tue Sep 2 13:44:44 2008
@@ -51,6 +51,9 @@
public static final Set<String> NUMBER_TYPES = new HashSet<String>();
public static final Set<String> SCALAR_TYPES = new HashSet<String>();
+ public static final Set<String> SIGNED_TYPES = new HashSet<String>();
+ public static final Set<String> UNSIGNED_TYPES = new HashSet<String>();
+
static {
INT32_TYPES.add(INT32_TYPE);
INT32_TYPES.add(UINT32_TYPE);
Modified: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java?rev=691373&r1=691372&r2=691373&view=diff
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java (original)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java Tue Sep 2 13:44:44 2008
@@ -16,14 +16,19 @@
*/
package org.apache.activemq.protobuf.compiler;
-import com.google.protobuf.Message;
-import com.google.protobuf.WireFormat;
+import static org.apache.activemq.protobuf.WireInfo.WIRETYPE_FIXED32;
+import static org.apache.activemq.protobuf.WireInfo.WIRETYPE_FIXED64;
+import static org.apache.activemq.protobuf.WireInfo.WIRETYPE_LENGTH_DELIMITED;
+import static org.apache.activemq.protobuf.WireInfo.WIRETYPE_START_GROUP;
+import static org.apache.activemq.protobuf.WireInfo.WIRETYPE_VARINT;
+import static org.apache.activemq.protobuf.WireInfo.makeTag;
+
+import com.google.protobuf.ByteString;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -33,8 +38,6 @@
import org.apache.activemq.protobuf.compiler.parser.ParseException;
import org.apache.activemq.protobuf.compiler.parser.ProtoParser;
-import static org.apache.activemq.protobuf.WireInfo.*;
-
public class JavaGenerator {
private File out = new File(".");
@@ -43,11 +46,11 @@
private ProtoDescriptor proto;
private String javaPackage;
private String outerClassName;
- private File outputFile;
private PrintWriter w;
private int indent;
private String optimizeFor;
private ArrayList<String> errors = new ArrayList<String>();
+ private boolean multipleFiles;
public static void main(String[] args) {
@@ -87,6 +90,10 @@
}
}
+ interface Closure {
+ void execute() throws CompilerException;
+ }
+
public void compile(File file) throws CompilerException {
// Parse the proto file
@@ -106,8 +113,7 @@
try { is.close(); } catch (Throwable ignore){}
}
- // This would be too fatal to continue..
- if (proto==null) {
+ if (!errors.isEmpty()) {
throw new CompilerException(errors);
}
@@ -115,19 +121,34 @@
javaPackage = javaPackage(proto);
outerClassName = javaClassName(proto);
optimizeFor = getOption(proto, "optimize_for", "SPEED");
+ multipleFiles = isMultipleFilesEnabled(proto);
+
+ if( multipleFiles ) {
+ generateProtoFile();
+ } else {
+ writeFile(outerClassName, new Closure(){
+ public void execute() throws CompilerException {
+ generateProtoFile();
+ }
+ });
+ }
+
+ if (!errors.isEmpty()) {
+ throw new CompilerException(errors);
+ }
+
+ }
+ private void writeFile(String className, Closure closure) throws CompilerException {
+ PrintWriter oldWriter = w;
// Figure out the java file name..
- outputFile = out;
+ File outputFile = out;
if (javaPackage != null) {
String packagePath = javaPackage.replace('.', '/');
outputFile = new File(outputFile, packagePath);
}
- outputFile = new File(outputFile, outerClassName + ".java");
-
+ outputFile = new File(outputFile, className + ".java");
- if (!errors.isEmpty()) {
- throw new CompilerException(errors);
- }
// Start writing the output file..
outputFile.getParentFile().mkdirs();
@@ -135,17 +156,14 @@
try {
fos = new FileOutputStream(outputFile);
w = new PrintWriter(fos);
- generateProtoFile();
+ closure.execute();
w.flush();
} catch (FileNotFoundException e) {
errors.add("Failed to write to: "+outputFile.getPath()+":"+e.getMessage());
} finally {
try { fos.close(); } catch (Throwable ignore){}
+ w = oldWriter;
}
- if (!errors.isEmpty()) {
- throw new CompilerException(errors);
- }
-
}
private void loadImports(ProtoDescriptor proto, File protoDir) {
@@ -179,38 +197,69 @@
}
- private void generateProtoFile() {
- generateFileHeader();
- if (javaPackage != null) {
- p("package " + javaPackage + ";");
- p("");
- }
+ private void generateProtoFile() throws CompilerException {
+ if( multipleFiles ) {
+ for (EnumDescriptor value : proto.getEnums().values()) {
+ final EnumDescriptor o = value;
+ String className = uCamel(o.getName());
+ writeFile(className, new Closure(){
+ public void execute() throws CompilerException {
+ generateFileHeader();
+ generateEnum(o);
+ }
+ });
+ }
+ for (MessageDescriptor value : proto.getMessages().values()) {
+ final MessageDescriptor o = value;
+ String className = uCamel(o.getName());
+ writeFile(className, new Closure(){
+ public void execute() throws CompilerException {
+ generateFileHeader();
+ generateMessageBean(o);
+ }
+ });
+ }
- p("public class " + outerClassName + " {");
- indent();
+ } else {
+ generateFileHeader();
- for (EnumDescriptor enumType : proto.getEnums().values()) {
- generateEnum(enumType);
- }
- for (MessageDescriptor m : proto.getMessages().values()) {
- generateMessageBean(m);
- }
+ p("public class " + outerClassName + " {");
+ indent();
- unindent();
- p("}");
+ for (EnumDescriptor enumType : proto.getEnums().values()) {
+ generateEnum(enumType);
+ }
+ for (MessageDescriptor m : proto.getMessages().values()) {
+ generateMessageBean(m);
+ }
+
+ unindent();
+ p("}");
+ }
}
private void generateFileHeader() {
p("//");
p("// Generated by protoc, do not edit by hand.");
p("//");
+ if (javaPackage != null) {
+ p("package " + javaPackage + ";");
+ p("");
+ }
}
private void generateMessageBean(MessageDescriptor m) {
String className = uCamel(m.getName());
p();
- p("public static final class " + className + " extends org.apache.activemq.protobuf.Message<" + className + "> {");
+
+ String staticOption = "static ";
+ if( multipleFiles && m.getParent()==null ) {
+ staticOption="";
+ }
+
+
+ p("public "+staticOption+"final class " + className + " extends org.apache.activemq.protobuf.Message<" + className + "> {");
p();
indent();
@@ -236,7 +285,7 @@
generateFieldAccessor(className, field);
}
- generateMethodIsInitialized(m);
+ generateMethodAssertInitialized(m, className);
generateMethodClear(m);
@@ -253,11 +302,59 @@
generateMethodWriteTo(m);
+ generateMethodParseFrom(m, className);
+
+ generateMethodToString(m);
+
unindent();
p("}");
p();
}
+ private void generateMethodParseFrom(MessageDescriptor m, String className) {
+ p("public static "+className+" parseFrom(com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+
+ p("public static "+className+" parseFrom(com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistry extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data, extensionRegistry).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+
+ p("public static "+className+" parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+
+ p("public static "+className+" parseFrom(byte[] data, com.google.protobuf.ExtensionRegistry extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data,extensionRegistry).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+
+ p("public static "+className+" parseFrom(java.io.InputStream data) throws com.google.protobuf.InvalidProtocolBufferException, java.io.IOException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+
+ p("public static "+className+" parseFrom(java.io.InputStream data, com.google.protobuf.ExtensionRegistry extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException, java.io.IOException {");
+ indent();
+ p("return new "+className+"().mergeFrom(data,extensionRegistry).checktInitialized();");
+ unindent();
+ p("}");
+ p();
+ }
+
/**
* @param m
*/
@@ -423,7 +520,13 @@
p("while (true) {");
indent(); {
p("int tag = input.readTag();");
+ // Is it an end group tag?
+ p("if ((tag & 0x07) == 4) {");
+ p(" return this;");
+ p("}");
+
p("switch (tag) {");
+ // The end of stream..
p("case 0:");
// p(" this.setUnknownFields(unknownFields.build());");
p(" return this;");
@@ -638,24 +741,153 @@
p();
}
- /**
- * @param m
- */
- private void generateMethodIsInitialized(MessageDescriptor m) {
+ private void generateMethodAssertInitialized(MessageDescriptor m, String className) {
+
+
+
p("public final boolean isInitialized() {");
indent();
+ p("return missingFields().isEmpty();");
+ unindent();
+ p("}");
+ p();
+
+ p("public final "+className+" assertInitialized() throws org.apache.activemq.protobuf.UninitializedMessageException {");
+ indent();
+ p("java.util.ArrayList<String> missingFields = missingFields();");
+ p("if( !missingFields.isEmpty()) {");
+ indent();
+ p("throw new org.apache.activemq.protobuf.UninitializedMessageException(missingFields);");
+ unindent();
+ p("}");
+ p("return this;");
+ unindent();
+ p("}");
+ p();
+
+ p("private final "+className+" checktInitialized() throws com.google.protobuf.InvalidProtocolBufferException {");
+ indent();
+ p("java.util.ArrayList<String> missingFields = missingFields();");
+ p("if( !missingFields.isEmpty()) {");
+ indent();
+ p("throw new org.apache.activemq.protobuf.UninitializedMessageException(missingFields).asInvalidProtocolBufferException();");
+ unindent();
+ p("}");
+ p("return this;");
+ unindent();
+ p("}");
+ p();
+
+ p("public final java.util.ArrayList<String> missingFields() {");
+ indent();
+ p("java.util.ArrayList<String> missingFields = new java.util.ArrayList<String>();");
+
for (FieldDescriptor field : m.getFields().values()) {
String uname = uCamel(field.getName());
if( field.isRequired() ) {
p("if( !has" + uname + "() ) {");
- p(" return false;");
+ indent();
+ p("missingFields.add(\""+field.getName()+"\");");
+ unindent();
p("}");
}
}
- p("return true;");
+
+ for (FieldDescriptor field : m.getFields().values()) {
+ if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
+ String uname = uCamel(field.getName());
+ p("if( has" + uname + "() ) {");
+ indent();
+ if( !field.isRepeated() ) {
+ p("try {");
+ indent();
+ p("get" + uname + "().assertInitialized();");
+ unindent();
+ p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){");
+ indent();
+ p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+".\"));");
+ unindent();
+ p("}");
+ } else {
+ String type = javaCollectionType(field);
+ p("java.util.List<"+type+"> l = get" + uname + "List();");
+ p("for( int i=0; i < l.size(); i++ ) {");
+ indent();
+ p("try {");
+ indent();
+ p("l.get(i).assertInitialized();");
+ unindent();
+ p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){");
+ indent();
+ p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+"[\"+i+\"]\"));");
+ unindent();
+ p("}");
+ unindent();
+ p("}");
+ }
+ unindent();
+ p("}");
+ }
+ }
+ p("return missingFields;");
+ unindent();
+ p("}");
+ p();
+ }
+
+ private void generateMethodToString(MessageDescriptor m) {
+
+ p("public String toString() {");
+ indent();
+ p("return toString(new java.lang.StringBuilder(), \"\").toString();");
+ unindent();
+ p("}");
+ p();
+
+ p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {");
+ indent();
+
+ for (FieldDescriptor field : m.getFields().values()) {
+ String uname = uCamel(field.getName());
+ p("if( has" + uname + "() ) {");
+ indent();
+ if( field.isRepeated() ) {
+ String type = javaCollectionType(field);
+ p("java.util.List<"+type+"> l = get" + uname + "List();");
+ p("for( int i=0; i < l.size(); i++ ) {");
+ indent();
+ if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
+ p("sb.append(prefix+\""+field.getName()+"[\"+i+\"] {\\n\");");
+ p("l.get(i).toString(sb, prefix+\" \");");
+ p("sb.append(\"}\\n\");");
+ } else {
+ p("sb.append(prefix+\""+field.getName()+"[\"+i+\"]: \");");
+ p("sb.append(l.get(i));");
+ p("sb.append(\"\\n\");");
+ }
+ unindent();
+ p("}");
+ } else {
+ if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
+ p("sb.append(prefix+\""+field.getName()+" {\\n\");");
+ p("get" + uname + "().toString(sb, prefix+\" \");");
+ p("sb.append(\"}\\n\");");
+ } else {
+ p("sb.append(prefix+\""+field.getName()+": \");");
+ p("sb.append(get" + uname + "());");
+ p("sb.append(\"\\n\");");
+ }
+ }
+ unindent();
+ p("}");
+ }
+
+
+ p("return sb;");
unindent();
p("}");
p();
+
}
/**
@@ -668,7 +900,7 @@
String uname = uCamel(field.getName());
String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field);
String typeDefault = javaTypeDefault(field);
- boolean primitive = isPrimitive(field);
+ boolean primitive = field.getTypeDescriptor()==null || field.getTypeDescriptor().isEnum();
boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE;
// Create the fields..
@@ -704,16 +936,66 @@
p("return this;");
unindent();
p("}");
+ p();
+
+ p("public int get" + uname + "Count() {");
+ indent();
+ p("if( this.f_" + lname + " == null ) {");
+ indent();
+ p("return 0;");
+ unindent();
+ p("}");
+ p("return this.f_" + lname + ".size();");
+ unindent();
+ p("}");
+ p();
+
+ p("public " + type + " get" + uname + "(int index) {");
+ indent();
+ p("if( this.f_" + lname + " == null ) {");
+ indent();
+ p("return null;");
+ unindent();
+ p("}");
+ p("return this.f_" + lname + ".get(index);");
+ unindent();
+ p("}");
+ p();
+
+ p("public "+className+" set" + uname + "(int index, " + type + " value) {");
+ indent();
+ p("get" + uname + "List().set(index, value);");
+ p("return this;");
+ unindent();
+ p("}");
+ p();
+
+ p("public "+className+" add" + uname + "(" + type + " value) {");
+ indent();
+ p("get" + uname + "List().add(value);");
+ p("return this;");
+ unindent();
+ p("}");
+ p();
+
+ p("public "+className+" addAll" + uname + "(java.lang.Iterable<? extends " + type + "> collection) {");
+ indent();
+ p("super.addAll(collection, get" + uname + "List());");
+ p("return this;");
+ unindent();
+ p("}");
+ p();
p("public void clear" + uname + "() {");
indent();
p("this.f_" + lname + " = null;");
unindent();
p("}");
+ p();
} else {
- p("private " + type + " f_" + lname + "= "+typeDefault+";");
+ p("private " + type + " f_" + lname + " = "+typeDefault+";");
if (primitive) {
p("private boolean b_" + lname + ";");
}
@@ -733,6 +1015,13 @@
p("public " + type + " get" + uname + "() {");
indent();
+ if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
+ p("if( this.f_" + lname + " == null ) {");
+ indent();
+ p("this.f_" + lname + " = new " + type + "();");
+ unindent();
+ p("}");
+ }
p("return this.f_" + lname + ";");
unindent();
p("}");
@@ -747,6 +1036,7 @@
p("return this;");
unindent();
p("}");
+ p();
p("public void clear" + uname + "() {");
indent();
@@ -756,27 +1046,105 @@
p("this.f_" + lname + " = " + typeDefault + ";");
unindent();
p("}");
+ p();
}
}
private String javaTypeDefault(FieldDescriptor field) {
-// OptionDescriptor defaultOption = field.getOptions().get("default");
- if( field.isNumberType() ) {
- return "0";
- }
- if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
- return "false";
+ OptionDescriptor defaultOption = field.getOptions().get("default");
+ if( defaultOption!=null ) {
+ if( field.isStringType() ) {
+ return asJavaString(defaultOption.getValue());
+ } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) {
+ return "com.google.protobuf.ByteString.copyFromUtf8("+asJavaString(defaultOption.getValue())+")";
+ } else if( field.isInteger32Type() ) {
+ int v;
+ if( field.getType() == FieldDescriptor.UINT32_TYPE ) {
+ v = TextFormat.parseUInt32(defaultOption.getValue());
+ } else {
+ v = TextFormat.parseInt32(defaultOption.getValue());
+ }
+ return ""+v;
+ } else if( field.isInteger64Type() ) {
+ long v;
+ if( field.getType() == FieldDescriptor.UINT64_TYPE ) {
+ v = TextFormat.parseUInt64(defaultOption.getValue());
+ } else {
+ v = TextFormat.parseInt64(defaultOption.getValue());
+ }
+ return ""+v+"l";
+ } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) {
+ double v = Double.valueOf(defaultOption.getValue());
+ return ""+v+"d";
+ } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) {
+ float v = Float.valueOf(defaultOption.getValue());
+ return ""+v+"f";
+ } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
+ boolean v = Boolean.valueOf(defaultOption.getValue());
+ return ""+v;
+ } else if( field.getTypeDescriptor()!=null && field.getTypeDescriptor().isEnum() ) {
+ return javaType(field)+"."+defaultOption.getValue();
+ }
+ return defaultOption.getValue();
+ } else {
+ if( field.isNumberType() ) {
+ return "0";
+ }
+ if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
+ return "false";
+ }
+ return "null";
}
- return "null";
}
+
+ static final char HEX_TABLE[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ private String asJavaString(String value) {
+ StringBuilder sb = new StringBuilder(value.length()+2);
+ sb.append("\"");
+ for (int i = 0; i < value.length(); i++) {
+
+ char b = value.charAt(i);
+ switch (b) {
+ // Java does not recognize \a or \v, apparently.
+ case '\b': sb.append("\\b" ); break;
+ case '\f': sb.append("\\f" ); break;
+ case '\n': sb.append("\\n" ); break;
+ case '\r': sb.append("\\r" ); break;
+ case '\t': sb.append("\\t" ); break;
+ case '\\': sb.append("\\\\"); break;
+ case '\'': sb.append("\\\'"); break;
+ case '"' : sb.append("\\\""); break;
+ default:
+ if (b >= 0x20 && b <'Z') {
+ sb.append((char) b);
+ } else {
+ sb.append("\\u");
+ sb.append(HEX_TABLE[(b >>> 12) & 0x0F] );
+ sb.append(HEX_TABLE[(b >>> 8) & 0x0F] );
+ sb.append(HEX_TABLE[(b >>> 4) & 0x0F] );
+ sb.append(HEX_TABLE[b & 0x0F] );
+ }
+ break;
+ }
+
+ }
+ sb.append("\"");
+ return sb.toString();
+ }
+
private void generateEnum(EnumDescriptor ed) {
String uname = uCamel(ed.getName());
+ String staticOption = "static ";
+ if( multipleFiles && ed.getParent()==null ) {
+ staticOption="";
+ }
+
// TODO Auto-generated method stub
p();
- p("public static enum " +uname + " {");
+ p("public "+staticOption+"enum " +uname + " {");
indent();
@@ -784,15 +1152,15 @@
int counter=0;
for (EnumFieldDescriptor field : ed.getFields().values()) {
boolean last = counter+1 == ed.getFields().size();
- p(field.getName()+"("+counter+", "+field.getValue()+")"+(last?";":","));
+ p(field.getName()+"(\""+field.getName()+"\", "+field.getValue()+")"+(last?";":","));
counter++;
}
p();
- p("private final int index;");
+ p("private final String name;");
p("private final int value;");
p();
- p("private "+uname+"(int index, int value) {");
- p(" this.index = index;");
+ p("private "+uname+"(String name, int value) {");
+ p(" this.name = name;");
p(" this.value = value;");
p("}");
p();
@@ -800,6 +1168,10 @@
p(" return value;");
p("}");
p();
+ p("public final String toString() {");
+ p(" return name;");
+ p("}");
+ p();
p("public static "+uname+" valueOf(int value) {");
p(" switch (value) {");
@@ -825,10 +1197,6 @@
p();
}
-
- private boolean isPrimitive(FieldDescriptor field) {
- return field.isNumberType() || field.getType()==FieldDescriptor.BOOL_TYPE;
- }
private String javaCollectionType(FieldDescriptor field) {
if( field.isInteger32Type() ) {
@@ -909,12 +1277,15 @@
private String javaClassName(ProtoDescriptor proto) {
return getOption(proto, "java_outer_classname", uCamel(removeFileExtension(proto.getName())));
}
+
+ private boolean isMultipleFilesEnabled(ProtoDescriptor proto) {
+ return "true".equals(getOption(proto, "java_multiple_files", "false"));
+ }
private String javaPackage(ProtoDescriptor proto) {
String name = proto.getPackageName();
if( name!=null ) {
- name = name.replace('_', '.');
name = name.replace('-', '.');
name = name.replace('/', '.');
}
@@ -980,7 +1351,8 @@
static private String lCamel(String name) {
if( name == null || name.length()<1 )
return name;
- return uCamel(name.substring(0,1).toLowerCase()+name.substring(1));
+ String uCamel = uCamel(name);
+ return uCamel.substring(0,1).toLowerCase()+uCamel.substring(1);
}
public File getOut() {
Modified: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java?rev=691373&r1=691372&r2=691373&view=diff
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java (original)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java Tue Sep 2 13:44:44 2008
@@ -16,39 +16,69 @@
*/
package org.apache.activemq.protobuf.compiler;
+import org.apache.activemq.protobuf.compiler.TextFormat.InvalidEscapeSequence;
+import org.apache.activemq.protobuf.compiler.parser.ParseException;
+import org.apache.activemq.protobuf.compiler.parser.Token;
+import com.google.protobuf.ByteString;
+
public class ParserSupport {
- public static String decodeString(String value) {
- StringBuilder sb = new StringBuilder();
- for (int i = 1; i < value.length() - 1; i++) {
- char c = value.charAt(i);
- if (c == '\'') {
- if( i+1 < (value.length() - 1) ) {
- char e = value.charAt(i+1);
- switch(e) {
- case 'n':
- sb.append("\n");
- break;
- case 'r':
- sb.append("\r");
- break;
- case 't':
- sb.append("\t");
- break;
- case 'b':
- sb.append("\b");
- break;
- default:
- sb.append(e);
- break;
- }
- } else {
- throw new RuntimeException("Invalid string litteral: "+value);
- }
- }
- sb.append(c);
+ public static String decodeString(Token token) throws ParseException {
+
+// StringBuilder sb = new StringBuilder();
+// for (int i = 1; i < value.length() - 1; i++) {
+// char c = value.charAt(i);
+// if (c == '\'') {
+// if( i+1 < (value.length() - 1) ) {
+// char e = value.charAt(i+1);
+// switch(e) {
+// case 'a':
+// sb.append((char)0x07);
+// break;
+// case 'b':
+// sb.append("\b");
+// break;
+// case 'f':
+// sb.append("\f");
+// break;
+// case 'n':
+// sb.append("\n");
+// break;
+// case 'r':
+// sb.append("\r");
+// break;
+// case 't':
+// sb.append("\t");
+// break;
+// case 'v':
+// sb.append((char)0x0b);
+// break;
+// case '\\':
+// sb.append("\\");
+// break;
+// case '\'':
+// sb.append("'");
+// break;
+// case '\"':
+// sb.append("\"");
+// break;
+// default:
+// sb.append(e);
+// break;
+// }
+// } else {
+// throw new RuntimeException("Invalid string litteral: "+value);
+// }
+// }
+// sb.append(c);
+// }
+// return sb.toString();
+
+ try {
+ return TextFormat.unescapeText(token.image.substring(1, token.image.length()-1));
+ } catch (InvalidEscapeSequence e) {
+ throw new ParseException("Invalid string litteral at line " + token.next.beginLine + ", column " + token.next.beginColumn+": "+e.getMessage());
}
- return sb.toString();
}
}
Added: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java?rev=691373&view=auto
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java (added)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java Tue Sep 2 13:44:44 2008
@@ -0,0 +1,773 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.
+// http://code.google.com/p/protobuf/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.activemq.protobuf.compiler;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Provide ascii text parsing and formatting support for proto2 instances.
+ * The implementation largely follows google/protobuf/text_format.cc.
+ *
+ * HRC: I wish the original class was not package protected so we did not need
+ * to copy this file over. We need to request that the protobuf folks open
+ * this class up amoung a few others.
+ *
+ * @author wenboz@google.com Wenbo Zhu
+ * @author kenton@google.com Kenton Varda
+ */
+public final class TextFormat {
+
+ /** Convert an unsigned 32-bit integer to a string. */
+ private static String unsignedToString(int value) {
+ if (value >= 0) {
+ return Integer.toString(value);
+ } else {
+ return Long.toString(((long) value) & 0x00000000FFFFFFFFL);
+ }
+ }
+
+ /** Convert an unsigned 64-bit integer to a string. */
+ private static String unsignedToString(long value) {
+ if (value >= 0) {
+ return Long.toString(value);
+ } else {
+ // Pull off the most-significant bit so that BigInteger doesn't think
+ // the number is negative, then set it again using setBit().
+ return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL)
+ .setBit(63).toString();
+ }
+ }
+
+ // =================================================================
+ // Parsing
+
+ /**
+ * Represents a stream of tokens parsed from a {@code String}.
+ *
+ * <p>The Java standard library provides many classes that you might think
+ * would be useful for implementing this, but aren't. For example:
+ *
+ * <ul>
+ * <li>{@code java.io.StreamTokenizer}: This almost does what we want -- or,
+ * at least, something that would get us close to what we want -- except
+ * for one fatal flaw: It automatically un-escapes strings using Java
+ * escape sequences, which do not include all the escape sequences we
+ * need to support (e.g. '\x').
+ * <li>{@code java.util.Scanner}: This seems like a great way at least to
+ * parse regular expressions out of a stream (so we wouldn't have to load
+ * the entire input into a single string before parsing). Sadly,
+ * {@code Scanner} requires that tokens be delimited with some delimiter.
+ * Thus, although the text "foo:" should parse to two tokens ("foo" and
+ * ":"), {@code Scanner} would recognize it only as a single token.
+ * Furthermore, {@code Scanner} provides no way to inspect the contents
+ * of delimiters, making it impossible to keep track of line and column
+ * numbers.
+ * </ul>
+ *
+ * <p>Luckily, Java's regular expression support does manage to be useful to
+ * us. (Barely: We need {@code Matcher.usePattern()}, which is new in
+ * Java 1.5.) So, we can use that, at least. Unfortunately, this implies
+ * that we need to have the entire input in one contiguous string.
+ */
+ private static final class Tokenizer {
+ private final CharSequence text;
+ private final Matcher matcher;
+ private String currentToken;
+
+ // The character index within this.text at which the current token begins.
+ private int pos = 0;
+
+ // The line and column numbers of the current token.
+ private int line = 0;
+ private int column = 0;
+
+ // The line and column numbers of the previous token (allows throwing
+ // errors *after* consuming).
+ private int previousLine = 0;
+ private int previousColumn = 0;
+
+ private static Pattern WHITESPACE =
+ Pattern.compile("(\\s|(#.*$))+", Pattern.MULTILINE);
+ private static Pattern TOKEN = Pattern.compile(
+ "[a-zA-Z_][0-9a-zA-Z_+-]*|" + // an identifier
+ "[0-9+-][0-9a-zA-Z_.+-]*|" + // a number
+ "\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|" + // a double-quoted string
+ "\'([^\"\n\\\\]|\\\\.)*(\'|\\\\?$)", // a single-quoted string
+ Pattern.MULTILINE);
+
+ private static Pattern DOUBLE_INFINITY = Pattern.compile(
+ "-?inf(inity)?",
+ Pattern.CASE_INSENSITIVE);
+ private static Pattern FLOAT_INFINITY = Pattern.compile(
+ "-?inf(inity)?f?",
+ Pattern.CASE_INSENSITIVE);
+ private static Pattern FLOAT_NAN = Pattern.compile(
+ "nanf?",
+ Pattern.CASE_INSENSITIVE);
+
+ /** Construct a tokenizer that parses tokens from the given text. */
+ public Tokenizer(CharSequence text) {
+ this.text = text;
+ this.matcher = WHITESPACE.matcher(text);
+ skipWhitespace();
+ nextToken();
+ }
+
+ /** Are we at the end of the input? */
+ public boolean atEnd() {
+ return currentToken.length() == 0;
+ }
+
+ /** Advance to the next token. */
+ public void nextToken() {
+ previousLine = line;
+ previousColumn = column;
+
+ // Advance the line counter to the current position.
+ while (pos < matcher.regionStart()) {
+ if (text.charAt(pos) == '\n') {
+ ++line;
+ column = 0;
+ } else {
+ ++column;
+ }
+ ++pos;
+ }
+
+ // Match the next token.
+ if (matcher.regionStart() == matcher.regionEnd()) {
+ // EOF
+ currentToken = "";
+ } else {
+ matcher.usePattern(TOKEN);
+ if (matcher.lookingAt()) {
+ currentToken = matcher.group();
+ matcher.region(matcher.end(), matcher.regionEnd());
+ } else {
+ // Take one character.
+ currentToken = String.valueOf(text.charAt(pos));
+ matcher.region(pos + 1, matcher.regionEnd());
+ }
+
+ skipWhitespace();
+ }
+ }
+
+ /**
+ * Skip over any whitespace so that the matcher region starts at the next
+ * token.
+ */
+ private void skipWhitespace() {
+ matcher.usePattern(WHITESPACE);
+ if (matcher.lookingAt()) {
+ matcher.region(matcher.end(), matcher.regionEnd());
+ }
+ }
+
+ /**
+ * If the next token exactly matches {@code token}, consume it and return
+ * {@code true}. Otherwise, return {@code false} without doing anything.
+ */
+ public boolean tryConsume(String token) {
+ if (currentToken.equals(token)) {
+ nextToken();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * If the next token exactly matches {@code token}, consume it. Otherwise,
+ * throw a {@link ParseException}.
+ */
+ public void consume(String token) throws ParseException {
+ if (!tryConsume(token)) {
+ throw parseException("Expected \"" + token + "\".");
+ }
+ }
+
+ /**
+ * Returns {@code true} if the next token is an integer, but does
+ * not consume it.
+ */
+ public boolean lookingAtInteger() {
+ if (currentToken.length() == 0) {
+ return false;
+ }
+
+ char c = currentToken.charAt(0);
+ return ('0' <= c && c <= '9') ||
+ c == '-' || c == '+';
+ }
+
+ /**
+ * If the next token is an identifier, consume it and return its value.
+ * Otherwise, throw a {@link ParseException}.
+ */
+ public String consumeIdentifier() throws ParseException {
+ for (int i = 0; i < currentToken.length(); i++) {
+ char c = currentToken.charAt(i);
+ if (('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z') ||
+ ('0' <= c && c <= '9') ||
+ (c == '_') || (c == '.')) {
+ // OK
+ } else {
+ throw parseException("Expected identifier.");
+ }
+ }
+
+ String result = currentToken;
+ nextToken();
+ return result;
+ }
+
+ /**
+ * If the next token is a 32-bit signed integer, consume it and return its
+ * value. Otherwise, throw a {@link ParseException}.
+ */
+ public int consumeInt32() throws ParseException {
+ try {
+ int result = parseInt32(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw integerParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a 32-bit unsigned integer, consume it and return its
+ * value. Otherwise, throw a {@link ParseException}.
+ */
+ public int consumeUInt32() throws ParseException {
+ try {
+ int result = parseUInt32(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw integerParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a 64-bit signed integer, consume it and return its
+ * value. Otherwise, throw a {@link ParseException}.
+ */
+ public long consumeInt64() throws ParseException {
+ try {
+ long result = parseInt64(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw integerParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a 64-bit unsigned integer, consume it and return its
+ * value. Otherwise, throw a {@link ParseException}.
+ */
+ public long consumeUInt64() throws ParseException {
+ try {
+ long result = parseUInt64(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw integerParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a double, consume it and return its value.
+ * Otherwise, throw a {@link ParseException}.
+ */
+ public double consumeDouble() throws ParseException {
+ // We need to parse infinity and nan separately because
+ // Double.parseDouble() does not accept "inf", "infinity", or "nan".
+ if (DOUBLE_INFINITY.matcher(currentToken).matches()) {
+ boolean negative = currentToken.startsWith("-");
+ nextToken();
+ return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+ if (currentToken.equalsIgnoreCase("nan")) {
+ nextToken();
+ return Double.NaN;
+ }
+ try {
+ double result = Double.parseDouble(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw floatParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a float, consume it and return its value.
+ * Otherwise, throw a {@link ParseException}.
+ */
+ public float consumeFloat() throws ParseException {
+ // We need to parse infinity and nan separately because
+ // Float.parseFloat() does not accept "inf", "infinity", or "nan".
+ if (FLOAT_INFINITY.matcher(currentToken).matches()) {
+ boolean negative = currentToken.startsWith("-");
+ nextToken();
+ return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
+ }
+ if (FLOAT_NAN.matcher(currentToken).matches()) {
+ nextToken();
+ return Float.NaN;
+ }
+ try {
+ float result = Float.parseFloat(currentToken);
+ nextToken();
+ return result;
+ } catch (NumberFormatException e) {
+ throw floatParseException(e);
+ }
+ }
+
+ /**
+ * If the next token is a boolean, consume it and return its value.
+ * Otherwise, throw a {@link ParseException}.
+ */
+ public boolean consumeBoolean() throws ParseException {
+ if (currentToken.equals("true")) {
+ nextToken();
+ return true;
+ } else if (currentToken.equals("false")) {
+ nextToken();
+ return false;
+ } else {
+ throw parseException("Expected \"true\" or \"false\".");
+ }
+ }
+
+ /**
+ * If the next token is a string, consume it and return its (unescaped)
+ * value. Otherwise, throw a {@link ParseException}.
+ */
+ public String consumeString() throws ParseException {
+ return consumeByteString().toStringUtf8();
+ }
+
+ /**
+ * If the next token is a string, consume it, unescape it as a
+ * {@link ByteString}, and return it. Otherwise, throw a
+ * {@link ParseException}.
+ */
+ public ByteString consumeByteString() throws ParseException {
+ char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0';
+ if (quote != '\"' && quote != '\'') {
+ throw parseException("Expected string.");
+ }
+
+ if (currentToken.length() < 2 ||
+ currentToken.charAt(currentToken.length() - 1) != quote) {
+ throw parseException("String missing ending quote.");
+ }
+
+ try {
+ String escaped = currentToken.substring(1, currentToken.length() - 1);
+ ByteString result = unescapeBytes(escaped);
+ nextToken();
+ return result;
+ } catch (InvalidEscapeSequence e) {
+ throw parseException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a {@link ParseException} with the current line and column
+ * numbers in the description, suitable for throwing.
+ */
+ public ParseException parseException(String description) {
+ // Note: People generally prefer one-based line and column numbers.
+ return new ParseException(
+ (line + 1) + ":" + (column + 1) + ": " + description);
+ }
+
+ /**
+ * Returns a {@link ParseException} with the line and column numbers of
+ * the previous token in the description, suitable for throwing.
+ */
+ public ParseException parseExceptionPreviousToken(String description) {
+ // Note: People generally prefer one-based line and column numbers.
+ return new ParseException(
+ (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description);
+ }
+
+ /**
+ * Constructs an appropriate {@link ParseException} for the given
+ * {@code NumberFormatException} when trying to parse an integer.
+ */
+ private ParseException integerParseException(NumberFormatException e) {
+ return parseException("Couldn't parse integer: " + e.getMessage());
+ }
+
+ /**
+ * Constructs an appropriate {@link ParseException} for the given
+ * {@code NumberFormatException} when trying to parse a float or double.
+ */
+ private ParseException floatParseException(NumberFormatException e) {
+ return parseException("Couldn't parse number: " + e.getMessage());
+ }
+ }
+
+ /** Thrown when parsing an invalid text format message. */
+ public static class ParseException extends IOException {
+ public ParseException(String message) {
+ super(message);
+ }
+ }
+
+ private static final int BUFFER_SIZE = 4096;
+
+ // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer)
+ // overhead is worthwhile
+ private static StringBuilder toStringBuilder(Readable input)
+ throws IOException {
+ StringBuilder text = new StringBuilder();
+ CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE);
+ while (true) {
+ int n = input.read(buffer);
+ if (n == -1) {
+ break;
+ }
+ buffer.flip();
+ text.append(buffer, 0, n);
+ }
+ return text;
+ }
+
+
+ // =================================================================
+ // Utility functions
+ //
+ // Some of these methods are package-private because Descriptors.java uses
+ // them.
+
+ /**
+ * Escapes bytes in the format used in protocol buffer text format, which
+ * is the same as the format used for C string literals. All bytes
+ * that are not printable 7-bit ASCII characters are escaped, as well as
+ * backslash, single-quote, and double-quote characters. Characters for
+ * which no defined short-hand escape sequence is defined will be escaped
+ * using 3-digit octal sequences.
+ */
+ static String escapeBytes(ByteString input) {
+ StringBuilder builder = new StringBuilder(input.size());
+ for (int i = 0; i < input.size(); i++) {
+ byte b = input.byteAt(i);
+ switch (b) {
+ // Java does not recognize \a or \v, apparently.
+ case 0x07: builder.append("\\a" ); break;
+ case '\b': builder.append("\\b" ); break;
+ case '\f': builder.append("\\f" ); break;
+ case '\n': builder.append("\\n" ); break;
+ case '\r': builder.append("\\r" ); break;
+ case '\t': builder.append("\\t" ); break;
+ case 0x0b: builder.append("\\v" ); break;
+ case '\\': builder.append("\\\\"); break;
+ case '\'': builder.append("\\\'"); break;
+ case '"' : builder.append("\\\""); break;
+ default:
+ if (b >= 0x20) {
+ builder.append((char) b);
+ } else {
+ builder.append('\\');
+ builder.append((char) ('0' + ((b >>> 6) & 3)));
+ builder.append((char) ('0' + ((b >>> 3) & 7)));
+ builder.append((char) ('0' + (b & 7)));
+ }
+ break;
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Un-escape a byte sequence as escaped using
+ * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with
+ * "\x") are also recognized.
+ */
+ static ByteString unescapeBytes(CharSequence input)
+ throws InvalidEscapeSequence {
+ byte[] result = new byte[input.length()];
+ int pos = 0;
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (c == '\\') {
+ if (i + 1 < input.length()) {
+ ++i;
+ c = input.charAt(i);
+ if (isOctal(c)) {
+ // Octal escape.
+ int code = digitValue(c);
+ if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
+ ++i;
+ code = code * 8 + digitValue(input.charAt(i));
+ }
+ if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) {
+ ++i;
+ code = code * 8 + digitValue(input.charAt(i));
+ }
+ result[pos++] = (byte)code;
+ } else {
+ switch (c) {
+ case 'a' : result[pos++] = 0x07; break;
+ case 'b' : result[pos++] = '\b'; break;
+ case 'f' : result[pos++] = '\f'; break;
+ case 'n' : result[pos++] = '\n'; break;
+ case 'r' : result[pos++] = '\r'; break;
+ case 't' : result[pos++] = '\t'; break;
+ case 'v' : result[pos++] = 0x0b; break;
+ case '\\': result[pos++] = '\\'; break;
+ case '\'': result[pos++] = '\''; break;
+ case '"' : result[pos++] = '\"'; break;
+
+ case 'x':
+ // hex escape
+ int code = 0;
+ if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
+ ++i;
+ code = digitValue(input.charAt(i));
+ } else {
+ throw new InvalidEscapeSequence(
+ "Invalid escape sequence: '\\x' with no digits");
+ }
+ if (i + 1 < input.length() && isHex(input.charAt(i + 1))) {
+ ++i;
+ code = code * 16 + digitValue(input.charAt(i));
+ }
+ result[pos++] = (byte)code;
+ break;
+
+ default:
+ throw new InvalidEscapeSequence(
+ "Invalid escape sequence: '\\" + c + "'");
+ }
+ }
+ } else {
+ throw new InvalidEscapeSequence(
+ "Invalid escape sequence: '\\' at end of string.");
+ }
+ } else {
+ result[pos++] = (byte)c;
+ }
+ }
+
+ return ByteString.copyFrom(result, 0, pos);
+ }
+
+ /**
+ * Thrown by {@link TextFormat#unescapeBytes} and
+ * {@link TextFormat#unescapeText} when an invalid escape sequence is seen.
+ */
+ static class InvalidEscapeSequence extends IOException {
+ public InvalidEscapeSequence(String description) {
+ super(description);
+ }
+ }
+
+ /**
+ * Like {@link #escapeBytes(ByteString)}, but escapes a text string.
+ * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped
+ * individually as a 3-digit octal escape. Yes, it's weird.
+ */
+ static String escapeText(String input) {
+ return escapeBytes(ByteString.copyFromUtf8(input));
+ }
+
+ /**
+ * Un-escape a text string as escaped using {@link #escapeText(String)}.
+ * Two-digit hex escapes (starting with "\x") are also recognized.
+ */
+ static String unescapeText(String input) throws InvalidEscapeSequence {
+ return unescapeBytes(input).toStringUtf8();
+ }
+
+ /** Is this an octal digit? */
+ private static boolean isOctal(char c) {
+ return '0' <= c && c <= '7';
+ }
+
+ /** Is this a hex digit? */
+ private static boolean isHex(char c) {
+ return ('0' <= c && c <= '9') ||
+ ('a' <= c && c <= 'f') ||
+ ('A' <= c && c <= 'F');
+ }
+
+ /**
+ * Interpret a character as a digit (in any base up to 36) and return the
+ * numeric value. This is like {@code Character.digit()} but we don't accept
+ * non-ASCII digits.
+ */
+ private static int digitValue(char c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ } else if ('a' <= c && c <= 'z') {
+ return c - 'a' + 10;
+ } else {
+ return c - 'A' + 10;
+ }
+ }
+
+ /**
+ * Parse a 32-bit signed integer from the text. Unlike the Java standard
+ * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
+ * and "0" to signify hexidecimal and octal numbers, respectively.
+ */
+ static int parseInt32(String text) throws NumberFormatException {
+ return (int) parseInteger(text, true, false);
+ }
+
+ /**
+ * Parse a 32-bit unsigned integer from the text. Unlike the Java standard
+ * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
+ * and "0" to signify hexidecimal and octal numbers, respectively. The
+ * result is coerced to a (signed) {@code int} when returned since Java has
+ * no unsigned integer type.
+ */
+ static int parseUInt32(String text) throws NumberFormatException {
+ return (int) parseInteger(text, false, false);
+ }
+
+ /**
+ * Parse a 64-bit signed integer from the text. Unlike the Java standard
+ * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
+ * and "0" to signify hexidecimal and octal numbers, respectively.
+ */
+ static long parseInt64(String text) throws NumberFormatException {
+ return parseInteger(text, true, true);
+ }
+
+ /**
+ * Parse a 64-bit unsigned integer from the text. Unlike the Java standard
+ * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
+ * and "0" to signify hexidecimal and octal numbers, respectively. The
+ * result is coerced to a (signed) {@code long} when returned since Java has
+ * no unsigned long type.
+ */
+ static long parseUInt64(String text) throws NumberFormatException {
+ return parseInteger(text, false, true);
+ }
+
+ private static long parseInteger(String text,
+ boolean isSigned,
+ boolean isLong)
+ throws NumberFormatException {
+ int pos = 0;
+
+ boolean negative = false;
+ if (text.startsWith("-", pos)) {
+ if (!isSigned) {
+ throw new NumberFormatException("Number must be positive: " + text);
+ }
+ ++pos;
+ negative = true;
+ }
+
+ int radix = 10;
+ if (text.startsWith("0x", pos)) {
+ pos += 2;
+ radix = 16;
+ } else if (text.startsWith("0", pos)) {
+ radix = 8;
+ }
+
+ String numberText = text.substring(pos);
+
+ long result = 0;
+ if (numberText.length() < 16) {
+ // Can safely assume no overflow.
+ result = Long.parseLong(numberText, radix);
+ if (negative) {
+ result = -result;
+ }
+
+ // Check bounds.
+ // No need to check for 64-bit numbers since they'd have to be 16 chars
+ // or longer to overflow.
+ if (!isLong) {
+ if (isSigned) {
+ if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) {
+ throw new NumberFormatException(
+ "Number out of range for 32-bit signed integer: " + text);
+ }
+ } else {
+ if (result >= (1L << 32) || result < 0) {
+ throw new NumberFormatException(
+ "Number out of range for 32-bit unsigned integer: " + text);
+ }
+ }
+ }
+ } else {
+ BigInteger bigValue = new BigInteger(numberText, radix);
+ if (negative) {
+ bigValue = bigValue.negate();
+ }
+
+ // Check bounds.
+ if (!isLong) {
+ if (isSigned) {
+ if (bigValue.bitLength() > 31) {
+ throw new NumberFormatException(
+ "Number out of range for 32-bit signed integer: " + text);
+ }
+ } else {
+ if (bigValue.bitLength() > 32) {
+ throw new NumberFormatException(
+ "Number out of range for 32-bit unsigned integer: " + text);
+ }
+ }
+ } else {
+ if (isSigned) {
+ if (bigValue.bitLength() > 63) {
+ throw new NumberFormatException(
+ "Number out of range for 64-bit signed integer: " + text);
+ }
+ } else {
+ if (bigValue.bitLength() > 64) {
+ throw new NumberFormatException(
+ "Number out of range for 64-bit unsigned integer: " + text);
+ }
+ }
+ }
+
+ result = bigValue.longValue();
+ }
+
+ return result;
+ }
+}
Modified: activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/javacc/proto-parser.jj
URL: http://svn.apache.org/viewvc/activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/javacc/proto-parser.jj?rev=691373&r1=691372&r2=691373&view=diff
==============================================================================
--- activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/javacc/proto-parser.jj (original)
+++ activemq/sandbox/activemq-protobuf/activemq-protobuf/src/main/javacc/proto-parser.jj Tue Sep 2 13:44:44 2008
@@ -588,6 +588,6 @@
{
t = <STRING>
{
- return ParserSupport.decodeString(t.image);
+ return ParserSupport.decodeString(t);
}
}