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);
     }    
 }