001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.fusesource.hawtbuf.proto.compiler;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.FileNotFoundException;
022    import java.io.FileOutputStream;
023    import java.io.PrintWriter;
024    import java.util.ArrayList;
025    import java.util.HashSet;
026    import java.util.LinkedHashMap;
027    import java.util.Map;
028    import java.util.StringTokenizer;
029    
030    import org.fusesource.hawtbuf.proto.compiler.parser.ParseException;
031    import org.fusesource.hawtbuf.proto.compiler.parser.ProtoParser;
032    
033    import static org.fusesource.hawtbuf.proto.WireFormat.*;
034    
035    public class JavaGenerator {
036    
037        private File out = new File(".");
038        private File[] path = new File[]{new File(".")};
039    
040        private ProtoDescriptor proto;
041        private String javaPackage;
042        private String outerClassName;
043        private PrintWriter w;
044        private int indent;
045        private ArrayList<String> errors = new ArrayList<String>();
046        private boolean multipleFiles;
047            private boolean deferredDecode;
048        private boolean auto_clear_optional_fields;
049    
050        public static void main(String[] args) {
051            
052            JavaGenerator generator = new JavaGenerator();
053            args = CommandLineSupport.setOptions(generator, args);
054            
055            if (args.length == 0) {
056                System.out.println("No proto files specified.");
057            }
058            for (int i = 0; i < args.length; i++) {
059                try {
060                    System.out.println("Compiling: "+args[i]);
061                    generator.compile(new File(args[i]));
062                } catch (CompilerException e) {
063                    System.out.println("Protocol Buffer Compiler failed with the following error(s):");
064                    for (String error : e.getErrors() ) {
065                        System.out.println("");
066                        System.out.println(error);
067                    }
068                    System.out.println("");
069                    System.out.println("Compile failed.  For more details see error messages listed above.");
070                    return;
071                }
072            }
073    
074        }
075    
076        interface Closure {
077            void execute() throws CompilerException;
078        }
079        
080        public void compile(File file) throws CompilerException {
081    
082            // Parse the proto file
083            FileInputStream is=null;
084            try {
085                is = new FileInputStream(file);
086                ProtoParser parser = new ProtoParser(is);
087                proto = parser.ProtoDescriptor();
088                proto.setName(file.getName());
089                loadImports(proto, file.getParentFile());
090                proto.validate(errors);
091            } catch (FileNotFoundException e) {
092                errors.add("Failed to open: "+file.getPath()+":"+e.getMessage());
093            } catch (ParseException e) {
094                errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage());
095            } finally {
096                try { is.close(); } catch (Throwable ignore){}
097            }
098    
099            if (!errors.isEmpty()) {
100                throw new CompilerException(errors);
101            }
102    
103            // Load the options..
104            javaPackage = javaPackage(proto);
105            outerClassName = javaClassName(proto);
106    //        optimizeFor = getOption(proto.getOptions(), "optimize_for", "SPEED");
107            multipleFiles = isMultipleFilesEnabled(proto);
108                    deferredDecode = Boolean.parseBoolean(getOption(proto.getOptions(), "deferred_decode", "false"));
109            auto_clear_optional_fields = Boolean.parseBoolean(getOption(proto.getOptions(), "auto_clear_optional_fields", "false"));
110                    
111            if( multipleFiles ) {
112                generateProtoFile();
113            } else {
114                writeFile(outerClassName, new Closure(){
115                    public void execute() throws CompilerException {
116                        generateProtoFile();
117                    }
118                });
119            }
120            
121            if (!errors.isEmpty()) {
122                throw new CompilerException(errors);
123            }
124    
125        }
126    
127        private void writeFile(String className, Closure closure) throws CompilerException {
128            PrintWriter oldWriter = w;
129            // Figure out the java file name..
130            File outputFile = out;
131            if (javaPackage != null) {
132                String packagePath = javaPackage.replace('.', '/');
133                outputFile = new File(outputFile, packagePath);
134            }
135            outputFile = new File(outputFile, className + ".java");
136            
137            // Start writing the output file..
138            outputFile.getParentFile().mkdirs();
139            
140            FileOutputStream fos=null;
141            try {
142                fos = new FileOutputStream(outputFile);
143                w = new PrintWriter(fos);
144                closure.execute();
145                w.flush();
146            } catch (FileNotFoundException e) {
147                errors.add("Failed to write to: "+outputFile.getPath()+":"+e.getMessage());
148            } finally {
149                try { fos.close(); } catch (Throwable ignore){}
150                w = oldWriter;
151            }
152        }
153    
154        private void loadImports(ProtoDescriptor proto, File protoDir) {
155            LinkedHashMap<String,ProtoDescriptor> children = new LinkedHashMap<String,ProtoDescriptor>(); 
156            for (String imp : proto.getImports()) {
157                File file = new File(protoDir, imp);
158                for (int i = 0; i < path.length && !file.exists(); i++) {
159                    file = new File(path[i], imp);
160                } 
161                if ( !file.exists() ) {
162                    errors.add("Cannot load import: "+imp);
163                }
164                
165                FileInputStream is=null;
166                try {
167                    is = new FileInputStream(file);
168                    ProtoParser parser = new ProtoParser(is);
169                    ProtoDescriptor child = parser.ProtoDescriptor();
170                    child.setName(file.getName());
171                    loadImports(child, file.getParentFile());
172                    children.put(imp, child);
173                } catch (ParseException e) {
174                    errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage());
175                } catch (FileNotFoundException e) {
176                    errors.add("Failed to open: "+file.getPath()+":"+e.getMessage());
177                } finally {
178                    try { is.close(); } catch (Throwable ignore){}
179                }
180            }
181            proto.setImportProtoDescriptors(children);
182        }
183    
184    
185        private void generateProtoFile() throws CompilerException {
186            if( multipleFiles ) {
187                for (EnumDescriptor value : proto.getEnums().values()) {
188                    final EnumDescriptor o = value;
189                    String className = uCamel(o.getName());
190                    writeFile(className, new Closure(){
191                        public void execute() throws CompilerException {
192                            generateFileHeader();
193                            generateEnum(o);
194                        }
195                    });
196                }
197                for (MessageDescriptor value : proto.getMessages().values()) {
198                    final MessageDescriptor o = value;
199                    String className = uCamel(o.getName());
200                    writeFile(className, new Closure(){
201                        public void execute() throws CompilerException {
202                            generateFileHeader();
203                            generateMessageBean(o);
204                        }
205                    });
206                }
207    
208            } else {
209                generateFileHeader();
210    
211                p("public class " + outerClassName + " {");
212                indent();
213    
214                for (EnumDescriptor enumType : proto.getEnums().values()) {
215                    generateEnum(enumType);
216                }
217                for (MessageDescriptor m : proto.getMessages().values()) {
218                    generateMessageBean(m);
219                }
220    
221                unindent();
222                p("}");
223            }
224        }
225    
226        private void generateFileHeader() {
227            p("//");
228            p("// Generated by protoc, do not edit by hand.");
229            p("//");
230            if (javaPackage != null) {
231                p("package " + javaPackage + ";");
232                p("");
233            }
234        }
235    
236        private void generateMessageBean(MessageDescriptor m) {
237            
238            String className = uCamel(m.getName());
239            p();
240            
241            String staticOption = "static ";
242            if( multipleFiles && m.getParent()==null ) {
243                staticOption="";
244            }
245            
246            String javaImplements = getOption(m.getOptions(), "java_implments", null);
247            
248            String implementsExpression = "";
249            if( javaImplements!=null ) {
250                implementsExpression = "implements "+javaImplements+" ";
251            }
252            
253            String baseClass = "org.fusesource.hawtbuf.proto.BaseMessage";
254            if( deferredDecode ) {
255                baseClass = "org.fusesource.hawtbuf.proto.DeferredDecodeMessage";
256            }
257            if( m.getBaseType()!=null ) {
258                baseClass = javaType(m.getBaseType())+"Base";
259            }
260            
261            p(staticOption+"public final class " + className + " extends "+className+"Base<"+className+"> "+implementsExpression+"{");
262            p();
263    
264            indent();
265            
266            for (EnumDescriptor enumType : m.getEnums().values()) {
267                generateEnum(enumType);
268            }
269    
270            // Generate the Nested Messages.
271            for (MessageDescriptor subMessage : m.getMessages().values()) {
272                generateMessageBean(subMessage);
273            }
274    
275            // Generate the Group Messages
276            for (FieldDescriptor field : m.getFields().values()) {
277                    if( isInBaseClass(m, field) ) {
278                            continue;
279                    }
280                if( field.isGroup() ) {
281                    generateMessageBean(field.getGroup());
282                }
283            }
284    
285            
286            generateMethodAssertInitialized(m, className);
287    
288            generateMethodClear(m);
289    
290            p("public "+className+" clone() {");
291            p("   return new "+className+"().mergeFrom(this);");
292            p("}");
293            p();
294            
295            generateMethodMergeFromBean(m, className);
296    
297            generateMethodSerializedSize(m);
298            
299            generateMethodMergeFromStream(m, className);
300    
301            generateMethodWriteTo(m);
302    
303            generateMethodParseFrom(m, className);
304    
305            generateMethodToString(m);
306            
307            generateMethodVisitor(m);
308                    
309            generateMethodType(m, className);
310            
311            generateMethodEquals(m, className);
312                    
313            unindent();
314            p("}");
315            p();
316            
317            p(staticOption+"abstract class " + className + "Base<T> extends "+baseClass+"<T> {");
318            p();
319            indent();
320            
321            // Generate the field accessors..
322            for (FieldDescriptor field : m.getFields().values()) {
323                if( isInBaseClass(m, field) ) {
324                    continue;
325                }
326                generateFieldAccessor(field);
327            }
328            
329            unindent();
330            p("}");
331            p();
332        }
333    
334            private boolean isInBaseClass(MessageDescriptor m, FieldDescriptor field) {
335                    if( m.getBaseType() ==null )
336                            return false;
337                    return m.getBaseType().getFields().containsKey(field.getName());
338            }
339    
340            /**
341         * If the java_visitor message option is set, then this method generates a visitor method.  The option 
342         * speifiies the class name of the visitor and optionally the return value and exceptions thrown by the visitor.
343         * 
344         * Examples:
345         * 
346         *   option java_visitor = "org.apache.kahadb.store.Visitor";
347         *   generates:
348         *     public void visit(org.apache.kahadb.store.Visitor visitor) {
349         *       visitor.visit(this);
350         *     }
351         *   
352         *   option java_visitor = "org.apache.kahadb.store.Visitor:int:java.io.IOException";
353         *   generates:
354         *     public int visit(org.apache.kahadb.store.Visitor visitor) throws java.io.IOException {
355         *       return visitor.visit(this);
356         *     }
357         * 
358         * @param m
359         */
360        private void generateMethodVisitor(MessageDescriptor m) {
361            String javaVisitor = getOption(m.getOptions(), "java_visitor", null);        
362            if( javaVisitor!=null ) {
363                String returns = "void";
364                String throwsException = null;
365                
366                StringTokenizer st = new StringTokenizer(javaVisitor, ":");
367                String vistorClass = st.nextToken();
368                if( st.hasMoreTokens() ) {
369                    returns = st.nextToken();
370                }
371                if( st.hasMoreTokens() ) {
372                    throwsException = st.nextToken();
373                }
374                
375                String throwsClause = "";
376                if( throwsException!=null ) {
377                    throwsClause = "throws "+throwsException+" ";
378                }
379                
380                p("public "+returns+" visit("+vistorClass+" visitor) "+throwsClause+ "{");
381                indent();
382                if( "void".equals(returns) ) {
383                    p("visitor.visit(this);");
384                } else {
385                    p("return visitor.visit(this);");
386                }
387                unindent();
388                p("}");
389                p();
390            }
391        }
392        
393        private void generateMethodType(MessageDescriptor m, String className) {
394            String typeEnum = getOption(m.getOptions(), "java_type_method", null);        
395            if( typeEnum!=null ) {
396                
397                TypeDescriptor typeDescriptor = m.getType(typeEnum);
398                if( typeDescriptor == null ) {
399                    typeDescriptor = m.getProtoDescriptor().getType(typeEnum);
400                }
401                if( typeDescriptor == null || !typeDescriptor.isEnum() ) {
402                    errors.add("The java_type_method option on the "+m.getName()+" message does not point to valid enum type");
403                    return;
404                }
405                
406                
407                String constant = constantCase(className);
408                EnumDescriptor enumDescriptor = (EnumDescriptor)typeDescriptor;
409                if( enumDescriptor.getFields().get(constant) == null ) {
410                    errors.add("The java_type_method option on the "+m.getName()+" message does not points to the "+typeEnum+" enum but it does not have an entry for "+constant);
411                }
412                
413                String type = javaType(typeDescriptor);
414                
415                p("public "+type+" type() {");
416                indent();
417                    p("return "+type+"."+constant+";");
418                unindent();
419                p("}");
420                p();
421            }
422        }
423        
424        private void generateMethodParseFrom(MessageDescriptor m, String className) {
425            
426            String postMergeProcessing = ".checktInitialized()";
427            if( deferredDecode ) {
428                    postMergeProcessing="";
429            }
430            
431            p("public static "+className+" parseUnframed(org.fusesource.hawtbuf.proto.CodedInputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
432            indent();
433            p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";");
434            unindent();
435            p("}");
436            p();
437    
438            p("public static "+className+" parseUnframed(org.fusesource.hawtbuf.Buffer data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
439            indent();
440            p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";");
441            unindent();
442            p("}");
443            p();
444    
445            p("public static "+className+" parseUnframed(byte[] data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
446            indent();
447            p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";");
448            unindent();
449            p("}");
450            p();
451            
452            p("public static "+className+" parseUnframed(java.io.InputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
453            indent();
454            p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";");
455            unindent();
456            p("}");
457            p();
458            
459            p("public static "+className+" parseFramed(org.fusesource.hawtbuf.proto.CodedInputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
460            indent();
461            p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";");
462            unindent();
463            p("}");
464            p();
465            
466            p("public static "+className+" parseFramed(org.fusesource.hawtbuf.Buffer data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
467            indent();
468            p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";");
469            unindent();
470            p("}");
471            p();
472    
473            p("public static "+className+" parseFramed(byte[] data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
474            indent();
475            p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";");
476            unindent();
477            p("}");
478            p();
479            
480            p("public static "+className+" parseFramed(java.io.InputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
481            indent();
482            p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";");
483            unindent();
484            p("}");
485            p();
486        }
487    
488        private void generateMethodEquals(MessageDescriptor m, String className) {
489            p("public boolean equals(Object obj) {");
490            indent();
491            p("if( obj==this )");
492            p("   return true;");
493            p("");
494            p("if( obj==null || obj.getClass()!="+className+".class )");
495            p("   return false;");
496            p("");
497            p("return equals(("+className+")obj);");
498            unindent();
499            p("}");
500            p("");
501            
502            p("public boolean equals("+className+" obj) {");
503            indent();
504            if( deferredDecode ) {
505                    p("return toUnframedBuffer().equals(obj.toUnframedBuffer());");
506            } else {        
507                    for (FieldDescriptor field : m.getFields().values()) {
508                        String uname = uCamel(field.getName());
509                        String getterMethod="get"+uname+"()";     
510                        String hasMethod = "has"+uname+"()";
511            
512                        if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
513                            getterMethod = "get"+uname+"List()";
514                        }
515                        
516                        p("if ("+hasMethod+" ^ obj."+hasMethod+" ) ");
517                        p("   return false;");
518                        
519                        
520                        
521                        if( field.getRule() != FieldDescriptor.REPEATED_RULE && (field.isNumberType() || field.getType()==FieldDescriptor.BOOL_TYPE) ) {
522                            p("if ("+hasMethod+" && ( "+getterMethod+"!=obj."+getterMethod+" ))");
523                        } else {
524                            p("if ("+hasMethod+" && ( !"+getterMethod+".equals(obj."+getterMethod+") ))");
525                        }
526                        p("   return false;");
527                    }
528                    p("return true;");
529            }
530            unindent();
531            p("}");
532            p("");
533            p("public int hashCode() {");
534            indent();
535            int hc = className.hashCode();
536            if( deferredDecode ) {
537                p("return "+hc+" ^ toUnframedBuffer().hashCode();");
538            } else {
539                p("int rc="+hc+";");
540                    int counter=0;
541                    for (FieldDescriptor field : m.getFields().values()) {
542                            counter++;
543                            
544                        String uname = uCamel(field.getName());
545                        String getterMethod="get"+uname+"()";     
546                        String hasMethod = "has"+uname+"()";
547            
548                        if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
549                            getterMethod = "get"+uname+"List()";
550                        }
551                        
552                        p("if ("+hasMethod+") {");
553                        indent();
554                        
555                        if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
556                            p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );");
557                        } else if( field.isInteger32Type() ) {
558                            p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+" );");
559                        } else if( field.isInteger64Type() ) {
560                            p("rc ^= ( "+uname.hashCode()+"^(new Long("+getterMethod+")).hashCode() );");
561                        } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) {
562                            p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );");
563                        } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) {
564                            p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );");
565                        } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) {
566                            p("rc ^= ( "+uname.hashCode()+"^ ("+getterMethod+"? "+counter+":-"+counter+") );");
567                        } else {
568                            p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );");
569                        }
570                        
571                        unindent();
572                        p("}");
573                        
574                    }
575                    p("return rc;");
576            }
577            unindent();
578            p("}");
579            p("");
580            }
581        
582        /**
583         * @param m
584         */
585        private void generateMethodSerializedSize(MessageDescriptor m) {
586            p("public int serializedSizeUnframed() {");
587            indent();
588            if( deferredDecode ) {
589                            p("if (encodedForm != null) {");
590                            indent();
591                p("return encodedForm.length;");
592                            unindent();
593                            p("}");
594            }
595            p("if (memoizedSerializedSize != -1)");
596            p("   return memoizedSerializedSize;");
597            p();
598            p("int size = 0;");
599            for (FieldDescriptor field : m.getFields().values()) {
600                
601                String uname = uCamel(field.getName());
602                String getter="get"+uname+"()";            
603                String type = javaType(field);
604                p("if (has"+uname+"()) {");
605                indent();
606                
607                if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
608                    p("for ("+type+" i : get"+uname+"List()) {");
609                    indent();
610                    getter = "i";
611                }
612    
613                if( field.getType()==FieldDescriptor.STRING_TYPE ) {
614                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeStringSize("+field.getTag()+", "+getter+");");
615                } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) {
616                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeBytesSize("+field.getTag()+", "+getter+");");
617                } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) {
618                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeBoolSize("+field.getTag()+", "+getter+");");
619                } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) {
620                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeDoubleSize("+field.getTag()+", "+getter+");");
621                } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) {
622                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFloatSize("+field.getTag()+", "+getter+");");
623                } else if( field.getType()==FieldDescriptor.INT32_TYPE ) {
624                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeInt32Size("+field.getTag()+", "+getter+");");
625                } else if( field.getType()==FieldDescriptor.INT64_TYPE ) {
626                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeInt64Size("+field.getTag()+", "+getter+");");
627                } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) {
628                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSInt32Size("+field.getTag()+", "+getter+");");
629                } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) {
630                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSInt64Size("+field.getTag()+", "+getter+");");
631                } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) {
632                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeUInt32Size("+field.getTag()+", "+getter+");");
633                } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) {
634                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeUInt64Size("+field.getTag()+", "+getter+");");
635                } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) {
636                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFixed32Size("+field.getTag()+", "+getter+");");
637                } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) {
638                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFixed64Size("+field.getTag()+", "+getter+");");
639                } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) {
640                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSFixed32Size("+field.getTag()+", "+getter+");");
641                } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) {
642                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSFixed64Size("+field.getTag()+", "+getter+");");
643                } else if( field.getTypeDescriptor().isEnum() ) {
644                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeEnumSize("+field.getTag()+", "+getter+".getNumber());");
645                } else if ( field.getGroup()!=null ) {
646                    p("size += computeGroupSize("+field.getTag()+", "+getter+");");
647                } else {
648                    p("size += computeMessageSize("+field.getTag()+", "+getter+");");
649                }
650                if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
651                    unindent();
652                    p("}");
653                }
654                //TODO: finish this up.
655                unindent();
656                p("}");
657    
658            }
659            // TODO: handle unknown fields
660            // size += getUnknownFields().getSerializedSize();");
661            p("memoizedSerializedSize = size;");
662            p("return size;");
663            unindent();
664            p("}");
665            p();
666        }
667    
668        /**
669         * @param m
670         */
671        private void generateMethodWriteTo(MessageDescriptor m) {
672            p("public void writeUnframed(org.fusesource.hawtbuf.proto.CodedOutputStream output) throws java.io.IOException {");
673            indent();
674            
675            if( deferredDecode ) {
676                            p("if (encodedForm == null) {");
677                            indent();
678                p("int size = serializedSizeUnframed();");
679                p("encodedForm = output.getNextBuffer(size);");
680                p("org.fusesource.hawtbuf.proto.CodedOutputStream original=null;");
681                p("if( encodedForm == null ) {");
682                indent();
683                p("encodedForm = new org.fusesource.hawtbuf.Buffer(new byte[size]);");
684                p("original = output;");
685                p("output = new org.fusesource.hawtbuf.proto.CodedOutputStream(encodedForm);");
686                unindent();
687                p("}");
688            }
689            
690    
691            for (FieldDescriptor field : m.getFields().values()) {
692                String uname = uCamel(field.getName());
693                String getter="get"+uname+"()";            
694                String type = javaType(field);
695                p("if (has"+uname+"()) {");
696                indent();
697                
698                if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
699                    p("for ("+type+" i : get"+uname+"List()) {");
700                    indent();
701                    getter = "i";
702                }
703    
704                if( field.getType()==FieldDescriptor.STRING_TYPE ) {
705                    p("output.writeString("+field.getTag()+", "+getter+");");
706                } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) {
707                    p("output.writeBytes("+field.getTag()+", "+getter+");");
708                } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) {
709                    p("output.writeBool("+field.getTag()+", "+getter+");");
710                } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) {
711                    p("output.writeDouble("+field.getTag()+", "+getter+");");
712                } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) {
713                    p("output.writeFloat("+field.getTag()+", "+getter+");");
714                } else if( field.getType()==FieldDescriptor.INT32_TYPE ) {
715                    p("output.writeInt32("+field.getTag()+", "+getter+");");
716                } else if( field.getType()==FieldDescriptor.INT64_TYPE ) {
717                    p("output.writeInt64("+field.getTag()+", "+getter+");");
718                } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) {
719                    p("output.writeSInt32("+field.getTag()+", "+getter+");");
720                } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) {
721                    p("output.writeSInt64("+field.getTag()+", "+getter+");");
722                } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) {
723                    p("output.writeUInt32("+field.getTag()+", "+getter+");");
724                } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) {
725                    p("output.writeUInt64("+field.getTag()+", "+getter+");");
726                } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) {
727                    p("output.writeFixed32("+field.getTag()+", "+getter+");");
728                } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) {
729                    p("output.writeFixed64("+field.getTag()+", "+getter+");");
730                } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) {
731                    p("output.writeSFixed32("+field.getTag()+", "+getter+");");
732                } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) {
733                    p("output.writeSFixed64("+field.getTag()+", "+getter+");");
734                } else if( field.getTypeDescriptor().isEnum() ) {
735                    p("output.writeEnum("+field.getTag()+", "+getter+".getNumber());");
736                } else if ( field.getGroup()!=null ) {
737                    p("writeGroup(output, "+field.getTag()+", "+getter+");");
738                } else {
739                    p("writeMessage(output, "+field.getTag()+", "+getter+");");
740                }
741                
742                if( field.getRule() == FieldDescriptor.REPEATED_RULE ) {
743                    unindent();
744                    p("}");
745                }
746                
747                unindent();
748                p("}");
749            }
750            
751            if( deferredDecode ) {
752                p("if( original !=null ) {");
753                indent();
754                p("output.checkNoSpaceLeft();");
755                p("output = original;");
756                p("output.writeRawBytes(encodedForm);");
757                unindent();
758                p("}");
759                unindent();
760                p("} else {");
761                indent();
762                p("output.writeRawBytes(encodedForm);");
763                unindent();
764                p("}");
765            }
766    
767            unindent();
768            p("}");
769            p();        
770        }
771    
772        /**
773         * @param m
774         * @param className
775         */
776        private void generateMethodMergeFromStream(MessageDescriptor m, String className) {
777            p("public "+className+" mergeUnframed(org.fusesource.hawtbuf.proto.CodedInputStream input) throws java.io.IOException {");
778            indent();
779                    {        
780                            p("while (true) {");
781                            indent();
782                            {
783                                    p("int tag = input.readTag();");
784                                    p("if ((tag & 0x07) == 4) {");
785                                    p("   return this;");
786                                    p("}");
787    
788                                    p("switch (tag) {");
789                                    p("case 0:");
790                                    p("   return this;");
791                                    p("default: {");
792    
793                                    p("   break;");
794                                    p("}");
795    
796                                    for (FieldDescriptor field : m.getFields().values()) {
797                                            String uname = uCamel(field.getName());
798                                            String setter = "set" + uname;
799                                            boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
800                                            if (repeated) {
801                                                    setter = "get" + uname + "List().add";
802                                            }
803                                            if (field.getType() == FieldDescriptor.STRING_TYPE) {
804                                                    p("case "
805                                                                    + makeTag(field.getTag(),
806                                                                                    WIRETYPE_LENGTH_DELIMITED) + ":");
807                                                    indent();
808                                                    p(setter + "(input.readString());");
809                                            } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
810                                                    p("case "
811                                                                    + makeTag(field.getTag(),
812                                                                                    WIRETYPE_LENGTH_DELIMITED) + ":");
813                                                    indent();
814                                                    p(setter + "(input.readBytes());");
815                                            } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
816                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
817                                                                    + ":");
818                                                    indent();
819                                                    p(setter + "(input.readBool());");
820                                            } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
821                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
822                                                                    + ":");
823                                                    indent();
824                                                    p(setter + "(input.readDouble());");
825                                            } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
826                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
827                                                                    + ":");
828                                                    indent();
829                                                    p(setter + "(input.readFloat());");
830                                            } else if (field.getType() == FieldDescriptor.INT32_TYPE) {
831                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
832                                                                    + ":");
833                                                    indent();
834                                                    p(setter + "(input.readInt32());");
835                                            } else if (field.getType() == FieldDescriptor.INT64_TYPE) {
836                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
837                                                                    + ":");
838                                                    indent();
839                                                    p(setter + "(input.readInt64());");
840                                            } else if (field.getType() == FieldDescriptor.SINT32_TYPE) {
841                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
842                                                                    + ":");
843                                                    indent();
844                                                    p(setter + "(input.readSInt32());");
845                                            } else if (field.getType() == FieldDescriptor.SINT64_TYPE) {
846                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
847                                                                    + ":");
848                                                    indent();
849                                                    p(setter + "(input.readSInt64());");
850                                            } else if (field.getType() == FieldDescriptor.UINT32_TYPE) {
851                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
852                                                                    + ":");
853                                                    indent();
854                                                    p(setter + "(input.readUInt32());");
855                                            } else if (field.getType() == FieldDescriptor.UINT64_TYPE) {
856                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
857                                                                    + ":");
858                                                    indent();
859                                                    p(setter + "(input.readUInt64());");
860                                            } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) {
861                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
862                                                                    + ":");
863                                                    indent();
864                                                    p(setter + "(input.readFixed32());");
865                                            } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) {
866                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
867                                                                    + ":");
868                                                    indent();
869                                                    p(setter + "(input.readFixed64());");
870                                            } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) {
871                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
872                                                                    + ":");
873                                                    indent();
874                                                    p(setter + "(input.readSFixed32());");
875                                            } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) {
876                                                    p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
877                                                                    + ":");
878                                                    indent();
879                                                    p(setter + "(input.readSFixed64());");
880                                            } else if (field.getTypeDescriptor().isEnum()) {
881                                                    p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
882                                                                    + ":");
883                                                    indent();
884                                                    String type = javaType(field);
885                                                    p("{");
886                                                    indent();
887                                                    p("int t = input.readEnum();");
888                                                    p("" + type + " value = " + type + ".valueOf(t);");
889                                                    p("if( value !=null ) {");
890                                                    indent();
891                                                    p(setter + "(value);");
892                                                    unindent();
893                                                    p("}");
894                                                    // TODO: else store it as an known
895    
896                                                    unindent();
897                                                    p("}");
898    
899                                            } else if (field.getGroup() != null) {
900                                                    p("case "
901                                                                    + makeTag(field.getTag(), WIRETYPE_START_GROUP)
902                                                                    + ":");
903                                                    indent();
904                                                    String type = javaType(field);
905                                                    if (repeated) {
906                                                            p(setter + "(readGroup(input, " + field.getTag()
907                                                                            + ", new " + type + "()));");
908                                                    } else {
909                                                            p("if (has" + uname + "()) {");
910                                                            indent();
911                                                            p("readGroup(input, " + field.getTag() + ", get"
912                                                                            + uname + "());");
913                                                            unindent();
914                                                            p("} else {");
915                                                            indent();
916                                                            p(setter + "(readGroup(input, " + field.getTag()
917                                                                            + ",new " + type + "()));");
918                                                            unindent();
919                                                            p("}");
920                                                    }
921                                                    p("");
922                                            } else {
923                                                    p("case "
924                                                                    + makeTag(field.getTag(),
925                                                                                    WIRETYPE_LENGTH_DELIMITED) + ":");
926                                                    indent();
927                                                    String type = javaType(field);
928                                                    if (repeated) {
929                                                            p(setter + "(new " + type
930                                                                            + "().mergeFramed(input));");
931                                                    } else {
932                                                            p("if (has" + uname + "()) {");
933                                                            indent();
934                                                            p("get" + uname + "().mergeFramed(input);");
935                                                            unindent();
936                                                            p("} else {");
937                                                            indent();
938                                                            p(setter + "(new " + type
939                                                                            + "().mergeFramed(input));");
940                                                            unindent();
941                                                            p("}");
942                                                    }
943                                            }
944                                            p("break;");
945                                            unindent();
946                                    }
947                                    p("}");
948                            }
949                            unindent();
950                            p("}");
951                    }
952                    unindent();
953                    p("}");
954        }
955    
956        /**
957             * @param m
958             * @param className
959             */
960        private void generateMethodMergeFromBean(MessageDescriptor m, String className) {
961            p("public "+className+" mergeFrom("+className+" other) {");
962            indent();
963            for (FieldDescriptor field : m.getFields().values()) {
964                String uname = uCamel(field.getName());
965                p("if (other.has"+uname+"()) {");
966                indent();
967    
968                if( field.isScalarType() || field.getTypeDescriptor().isEnum() ) {
969                    if( field.isRepeated() ) {
970                        p("get"+uname+"List().addAll(other.get"+uname+"List());");
971                    } else {
972                        p("set"+uname+"(other.get"+uname+"());");
973                    }
974                } else {
975                    
976                    String type = javaType(field);
977                    // It's complex type...
978                    if( field.isRepeated() ) {
979                        p("for("+type+" element: other.get"+uname+"List() ) {");
980                        indent();
981                            p("get"+uname+"List().add(element.clone());");
982                        unindent();
983                        p("}");
984                    } else {
985                        p("if (has"+uname+"()) {");
986                        indent();
987                        p("get"+uname+"().mergeFrom(other.get"+uname+"());");
988                        unindent();
989                        p("} else {");
990                        indent();
991                        p("set"+uname+"(other.get"+uname+"().clone());");
992                        unindent();
993                        p("}");
994                    }
995                }
996                unindent();
997                p("}");
998            }
999            p("return this;");
1000            unindent();
1001            p("}");
1002            p();
1003        }
1004    
1005        /**
1006             * @param m
1007             */
1008        private void generateMethodClear(MessageDescriptor m) {
1009            p("public void clear() {");
1010            indent();
1011            p("super.clear();");
1012            for (FieldDescriptor field : m.getFields().values()) {
1013                String uname = uCamel(field.getName());
1014                p("clear" + uname + "();");
1015            }
1016            unindent();
1017            p("}");
1018            p();
1019        }
1020    
1021        private void generateMethodAssertInitialized(MessageDescriptor m, String className) {
1022            
1023            p("public java.util.ArrayList<String> missingFields() {");
1024            indent();
1025            p("java.util.ArrayList<String> missingFields = super.missingFields();");
1026            
1027            for (FieldDescriptor field : m.getFields().values()) {
1028                String uname = uCamel(field.getName());
1029                if( field.isRequired() ) {
1030                    p("if(  !has" + uname + "() ) {");
1031                    indent();
1032                    p("missingFields.add(\""+field.getName()+"\");");
1033                    unindent();
1034                    p("}");
1035                }
1036            }
1037            
1038            if( !deferredDecode ) {
1039                    for (FieldDescriptor field : m.getFields().values()) {
1040                        if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
1041                            String uname = uCamel(field.getName());
1042                            p("if( has" + uname + "() ) {");
1043                            indent();
1044                            if( !field.isRepeated() ) {
1045                                p("try {");
1046                                indent();
1047                                p("get" + uname + "().assertInitialized();");
1048                                unindent();
1049                                p("} catch (org.fusesource.hawtbuf.proto.UninitializedMessageException e){");
1050                                indent();
1051                                p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+".\"));");
1052                                unindent();
1053                                p("}");
1054                            } else {
1055                                String type = javaCollectionType(field);
1056                                p("java.util.List<"+type+"> l = get" + uname + "List();");
1057                                p("for( int i=0; i < l.size(); i++ ) {");
1058                                indent();
1059                                p("try {");
1060                                indent();
1061                                p("l.get(i).assertInitialized();");
1062                                unindent();
1063                                p("} catch (org.fusesource.hawtbuf.proto.UninitializedMessageException e){");
1064                                indent();
1065                                p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+"[\"+i+\"]\"));");
1066                                unindent();
1067                                p("}");
1068                                unindent();
1069                                p("}");
1070                            }
1071                            unindent();
1072                            p("}");
1073                        }
1074                    }
1075            }
1076            p("return missingFields;");
1077            unindent();
1078            p("}");
1079            p();
1080        }
1081    
1082        private void generateMethodToString(MessageDescriptor m) {
1083            
1084            p("public String toString() {");
1085            indent();
1086            p("return toString(new java.lang.StringBuilder(), \"\").toString();");
1087            unindent();
1088            p("}");
1089            p();
1090    
1091            p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {");
1092            indent();
1093            
1094            if( deferredDecode ) {
1095                    p("load();");
1096            }        
1097            for (FieldDescriptor field : m.getFields().values()) {
1098                String uname = uCamel(field.getName());
1099                p("if(  has" + uname + "() ) {");
1100                indent();
1101                if( field.isRepeated() ) {
1102                    String type = javaCollectionType(field);
1103                    p("java.util.List<"+type+"> l = get" + uname + "List();");
1104                    p("for( int i=0; i < l.size(); i++ ) {");
1105                    indent();
1106                    if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
1107                        p("sb.append(prefix+\""+field.getName()+"[\"+i+\"] {\\n\");");
1108                        p("l.get(i).toString(sb, prefix+\"  \");");
1109                        p("sb.append(prefix+\"}\\n\");");
1110                    } else {
1111                        p("sb.append(prefix+\""+field.getName()+"[\"+i+\"]: \");");
1112                        p("sb.append(l.get(i));");
1113                        p("sb.append(\"\\n\");");
1114                    }
1115                    unindent();
1116                    p("}");
1117                } else {
1118                    if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
1119                        p("sb.append(prefix+\""+field.getName()+" {\\n\");");
1120                        p("get" + uname + "().toString(sb, prefix+\"  \");");
1121                        p("sb.append(prefix+\"}\\n\");");
1122                    } else {
1123                        p("sb.append(prefix+\""+field.getName()+": \");");
1124                        p("sb.append(get" + uname + "());");
1125                        p("sb.append(\"\\n\");");
1126                    }
1127                }
1128                unindent();
1129                p("}");
1130            }
1131    
1132            
1133            p("return sb;");
1134            unindent();
1135            p("}");
1136            p();
1137    
1138        }
1139    
1140        /**
1141         * @param field
1142         * @param className 
1143         */
1144        private void generateFieldAccessor(FieldDescriptor field) {
1145            
1146            String lname = lCamel(field.getName());
1147            String uname = uCamel(field.getName());
1148            String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field);
1149            String typeDefault = javaTypeDefault(field);
1150            boolean primitive = field.getTypeDescriptor()==null || field.getTypeDescriptor().isEnum();
1151            boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE;
1152    
1153            // Create the fields..
1154            p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";");
1155            
1156            if( repeated ) {
1157                p("private java.util.List<" + type + "> f_" + lname + ";");
1158                p();
1159                
1160                // Create the field accessors
1161                p("public boolean has" + uname + "() {");
1162                indent();
1163                if( deferredDecode ) {
1164                    p("load();");
1165                }        
1166                p("return this.f_" + lname + "!=null && !this.f_" + lname + ".isEmpty();");
1167                unindent();
1168                p("}");
1169                p();
1170    
1171                p("public java.util.List<" + type + "> get" + uname + "List() {");
1172                indent();
1173                if( deferredDecode ) {
1174                    p("load();");
1175                }        
1176                p("if( this.f_" + lname + " == null ) {");
1177                indent();
1178                p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">();");
1179                unindent();
1180                p("}");
1181                p("return this.f_" + lname + ";");
1182                unindent();
1183                p("}");
1184                p();
1185    
1186                p("public T set" + uname + "List(java.util.List<" + type + "> " + lname + ") {");
1187                indent();
1188                    p("loadAndClear();");
1189                p("this.f_" + lname + " = " + lname + ";");
1190                p("return (T)this;");
1191                unindent();
1192                p("}");
1193                p();
1194                
1195                p("public int get" + uname + "Count() {");
1196                indent();
1197                if( deferredDecode ) {
1198                    p("load();");
1199                }        
1200                p("if( this.f_" + lname + " == null ) {");
1201                indent();
1202                p("return 0;");
1203                unindent();
1204                p("}");
1205                p("return this.f_" + lname + ".size();");
1206                unindent();
1207                p("}");
1208                p();
1209                
1210                p("public " + type + " get" + uname + "(int index) {");
1211                indent();
1212                if( deferredDecode ) {
1213                    p("load();");
1214                }        
1215                p("if( this.f_" + lname + " == null ) {");
1216                indent();
1217                p("return null;");
1218                unindent();
1219                p("}");
1220                p("return this.f_" + lname + ".get(index);");
1221                unindent();
1222                p("}");
1223                p();
1224                                
1225                p("public T set" + uname + "(int index, " + type + " value) {");
1226                indent();
1227                    p("loadAndClear();");
1228                p("get" + uname + "List().set(index, value);");
1229                p("return (T)this;");
1230                unindent();
1231                p("}");
1232                p();
1233                
1234                p("public T add" + uname + "(" + type + " value) {");
1235                indent();
1236                    p("loadAndClear();");
1237                p("get" + uname + "List().add(value);");
1238                p("return (T)this;");
1239                unindent();
1240                p("}");
1241                p();
1242                
1243                p("public T addAll" + uname + "(java.lang.Iterable<? extends " + type + "> collection) {");
1244                indent();
1245                    p("loadAndClear();");
1246                p("super.addAll(collection, get" + uname + "List());");
1247                p("return (T)this;");
1248                unindent();
1249                p("}");
1250                p();
1251    
1252                p("public void clear" + uname + "() {");
1253                indent();
1254                    p("loadAndClear();");
1255                p("this.f_" + lname + " = null;");
1256                unindent();
1257                p("}");
1258                p();
1259    
1260            } else {
1261                
1262                p("private " + type + " f_" + lname + " = "+typeDefault+";");
1263                if (primitive) {
1264                    p("private boolean b_" + lname + ";");
1265                }
1266                p();
1267                
1268                // Create the field accessors
1269                p("public boolean has" + uname + "() {");
1270                indent();
1271                if( deferredDecode ) {
1272                    p("load();");
1273                }        
1274                if (primitive) {
1275                    p("return this.b_" + lname + ";");
1276                } else {
1277                    p("return this.f_" + lname + "!=null;");
1278                }
1279                unindent();
1280                p("}");
1281                p();
1282    
1283                p("public " + type + " get" + uname + "() {");
1284                indent();
1285                if( deferredDecode ) {
1286                    p("load();");
1287                }        
1288                if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
1289                    p("if( this.f_" + lname + " == null ) {");
1290                    indent();
1291                    p("this.f_" + lname + " = new " + type + "();");
1292                    unindent();
1293                    p("}");
1294                }
1295                p("return this.f_" + lname + ";");
1296                unindent();
1297                p("}");
1298                p();
1299    
1300                p("public T set" + uname + "(" + type + " " + lname + ") {");
1301                indent();
1302                    p("loadAndClear();");
1303                if (primitive) {
1304                    if( auto_clear_optional_fields && field.isOptional() ) {
1305                        if( field.isStringType() && !"null".equals(typeDefault)) {
1306                            p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");");
1307                        } else {
1308                            p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");");
1309                        }
1310                    } else {
1311                        p("this.b_" + lname + " = true;");
1312                    }
1313                }
1314                p("this.f_" + lname + " = " + lname + ";");
1315                p("return (T)this;");
1316                unindent();
1317                p("}");
1318                p();
1319    
1320                p("public void clear" + uname + "() {");
1321                indent();
1322                    p("loadAndClear();");
1323                if (primitive) {
1324                    p("this.b_" + lname + " = false;");
1325                }
1326                p("this.f_" + lname + " = " + typeDefault + ";");
1327                unindent();
1328                p("}");
1329                p();
1330            }
1331    
1332        }
1333    
1334        private String javaTypeDefault(FieldDescriptor field) {
1335            OptionDescriptor defaultOption = field.getOptions().get("default");
1336            if( defaultOption!=null ) {
1337                if( field.isStringType() ) {
1338                    return asJavaString(defaultOption.getValue());
1339                } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) {
1340                    return "new org.fusesource.hawtbuf.Buffer(org.fusesource.hawtbuf.UTF8Buffer.encode("+asJavaString(defaultOption.getValue())+"))";
1341                } else if( field.isInteger32Type() ) {
1342                    int v;
1343                    if( field.getType() == FieldDescriptor.UINT32_TYPE ) {
1344                        v = TextFormat.parseUInt32(defaultOption.getValue());
1345                    } else {
1346                        v = TextFormat.parseInt32(defaultOption.getValue());
1347                    }
1348                    return ""+v;
1349                } else if( field.isInteger64Type() ) {
1350                    long v;
1351                    if( field.getType() == FieldDescriptor.UINT64_TYPE ) {
1352                        v = TextFormat.parseUInt64(defaultOption.getValue());
1353                    } else {
1354                        v = TextFormat.parseInt64(defaultOption.getValue());
1355                    }
1356                    return ""+v+"l";
1357                } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) {
1358                    double v = Double.valueOf(defaultOption.getValue());
1359                    return ""+v+"d";
1360                } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) {
1361                    float v = Float.valueOf(defaultOption.getValue());
1362                    return ""+v+"f";
1363                } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
1364                    boolean v = Boolean.valueOf(defaultOption.getValue());
1365                    return ""+v;
1366                } else if( field.getTypeDescriptor()!=null && field.getTypeDescriptor().isEnum() ) {
1367                    return javaType(field)+"."+defaultOption.getValue();
1368                }
1369                return defaultOption.getValue();
1370            } else {
1371                if( field.isNumberType() ) {
1372                    return "0";
1373                }
1374                if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
1375                    return "false";
1376                }
1377                return "null";
1378            }
1379        }
1380            
1381        static final char HEX_TABLE[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1382        
1383        private String asJavaString(String value) {
1384            StringBuilder sb = new StringBuilder(value.length()+2);
1385            sb.append("\"");
1386            for (int i = 0; i < value.length(); i++) {
1387                
1388              char b = value.charAt(i);
1389              switch (b) {
1390                // Java does not recognize \a or \v, apparently.
1391                case '\b': sb.append("\\b" ); break;
1392                case '\f': sb.append("\\f" ); break;
1393                case '\n': sb.append("\\n" ); break;
1394                case '\r': sb.append("\\r" ); break;
1395                case '\t': sb.append("\\t" ); break;
1396                case '\\': sb.append("\\\\"); break;
1397                case '\'': sb.append("\\\'"); break;
1398                case '"' : sb.append("\\\""); break;
1399                default:
1400                  if (b >= 0x20 && b <'Z') {
1401                    sb.append((char) b);
1402                  } else {
1403                    sb.append("\\u");
1404                    sb.append(HEX_TABLE[(b >>> 12) & 0x0F] );
1405                    sb.append(HEX_TABLE[(b >>> 8) & 0x0F] );
1406                    sb.append(HEX_TABLE[(b >>> 4) & 0x0F] );
1407                    sb.append(HEX_TABLE[b & 0x0F] );
1408                  }
1409                  break;
1410              }
1411              
1412            }
1413            sb.append("\"");
1414            return sb.toString();
1415        }
1416    
1417        private void generateEnum(EnumDescriptor ed) {
1418            String uname = uCamel(ed.getName());
1419    
1420            String staticOption = "static ";
1421            if( multipleFiles && ed.getParent()==null ) {
1422                staticOption="";
1423            }
1424    
1425            // TODO Auto-generated method stub
1426            p();
1427            p("public "+staticOption+"enum " +uname + " {");
1428            indent();
1429            
1430            
1431            p();
1432            int counter=0;
1433            for (EnumFieldDescriptor field : ed.getFields().values()) {
1434                boolean last = counter+1 == ed.getFields().size();
1435                p(field.getName()+"(\""+field.getName()+"\", "+field.getValue()+")"+(last?";":",")); 
1436                counter++;
1437            }
1438            p();
1439            p("private final String name;");
1440            p("private final int value;");
1441            p();
1442            p("private "+uname+"(String name, int value) {");
1443            p("   this.name = name;");
1444            p("   this.value = value;");
1445            p("}");
1446            p();
1447            p("public final int getNumber() {");
1448            p("   return value;");
1449            p("}");
1450            p();
1451            p("public final String toString() {");
1452            p("   return name;");
1453            p("}");
1454            p();
1455            p("public static "+uname+" valueOf(int value) {");
1456            p("   switch (value) {");
1457            
1458            // It's possible to define multiple ENUM fields with the same value.. 
1459            //   we only want to put the first one into the switch statement.
1460            HashSet<Integer> values = new HashSet<Integer>();
1461            for (EnumFieldDescriptor field : ed.getFields().values()) {
1462                if( !values.contains(field.getValue()) ) {
1463                    p("   case "+field.getValue()+":");
1464                    p("      return "+field.getName()+";");
1465                    values.add(field.getValue());
1466                }
1467                
1468            }
1469            p("   default:");
1470            p("      return null;");
1471            p("   }");
1472            p("}");
1473            p();
1474            
1475            
1476            String createMessage = getOption(ed.getOptions(), "java_create_message", null);        
1477            if( "true".equals(createMessage) ) {
1478                p("public org.fusesource.hawtbuf.proto.Message createMessage() {");
1479                indent();
1480                p("switch (this) {");
1481                indent();
1482                for (EnumFieldDescriptor field : ed.getFields().values()) {
1483                    p("case "+field.getName()+":");
1484                    String type = constantToUCamelCase(field.getName());
1485                    p("   return new "+type+"();");
1486                }
1487                p("default:");
1488                p("   return null;");
1489                unindent();
1490                p("}");
1491                unindent();
1492                p("}");
1493                p();
1494            }
1495            
1496            unindent();
1497            p("}");
1498            p();
1499        }
1500        
1501        
1502        
1503        private String javaCollectionType(FieldDescriptor field) {
1504            if( field.isInteger32Type() ) {
1505                return "java.lang.Integer";
1506            }
1507            if( field.isInteger64Type() ) {
1508                return "java.lang.Long";
1509            }
1510            if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) {
1511                return "java.lang.Double";
1512            }
1513            if( field.getType() == FieldDescriptor.FLOAT_TYPE ) {
1514                return "java.lang.Float";
1515            }
1516            if( field.getType() == FieldDescriptor.STRING_TYPE ) {
1517                return "java.lang.String";
1518            }
1519            if( field.getType() == FieldDescriptor.BYTES_TYPE ) {
1520                return "org.fusesource.hawtbuf.Buffer";
1521            }
1522            if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
1523                return "java.lang.Boolean";
1524            }
1525            
1526            TypeDescriptor descriptor = field.getTypeDescriptor();
1527            return javaType(descriptor);
1528        }
1529    
1530        private String javaType(FieldDescriptor field) {
1531            if( field.isInteger32Type() ) {
1532                return "int";
1533            }
1534            if( field.isInteger64Type() ) {
1535                return "long";
1536            }
1537            if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) {
1538                return "double";
1539            }
1540            if( field.getType() == FieldDescriptor.FLOAT_TYPE ) {
1541                return "float";
1542            }
1543            if( field.getType() == FieldDescriptor.STRING_TYPE ) {
1544                return "java.lang.String";
1545            }
1546            if( field.getType() == FieldDescriptor.BYTES_TYPE ) {
1547                return "org.fusesource.hawtbuf.Buffer";
1548            }
1549            if( field.getType() == FieldDescriptor.BOOL_TYPE ) {
1550                return "boolean";
1551            }
1552            
1553            TypeDescriptor descriptor = field.getTypeDescriptor();
1554            return javaType(descriptor);
1555        }
1556    
1557        private String javaType(TypeDescriptor descriptor) {
1558            ProtoDescriptor p = descriptor.getProtoDescriptor();
1559            if( p != proto ) {
1560                // Try to keep it short..
1561                String othePackage = javaPackage(p);
1562                if( equals(othePackage,javaPackage(proto) ) ) {
1563                    return javaClassName(p)+"."+descriptor.getQName();
1564                }
1565                // Use the fully qualified class name.
1566                return othePackage+"."+javaClassName(p)+"."+descriptor.getQName();
1567            }
1568            return descriptor.getQName();
1569        }
1570        
1571        private boolean equals(String o1, String o2) {
1572            if( o1==o2 )
1573                return true;
1574            if( o1==null || o2==null )
1575                return false;
1576            return o1.equals(o2);
1577        }
1578    
1579        private String javaClassName(ProtoDescriptor proto) {
1580            return getOption(proto.getOptions(), "java_outer_classname", uCamel(removeFileExtension(proto.getName())));
1581        }
1582        
1583        private boolean isMultipleFilesEnabled(ProtoDescriptor proto) {
1584            return "true".equals(getOption(proto.getOptions(), "java_multiple_files", "false"));
1585        }
1586    
1587    
1588        private String javaPackage(ProtoDescriptor proto) {
1589            String name = proto.getPackageName();
1590            if( name!=null ) {
1591                name = name.replace('-', '.');
1592                name = name.replace('/', '.');
1593            }
1594            return getOption(proto.getOptions(), "java_package", name);
1595        }
1596    
1597    
1598        // ----------------------------------------------------------------
1599        // Internal Helper methods
1600        // ----------------------------------------------------------------
1601    
1602        private void indent() {
1603            indent++;
1604        }
1605    
1606        private void unindent() {
1607            indent--;
1608        }
1609    
1610        private void p(String line) {
1611            // Indent...
1612            for (int i = 0; i < indent; i++) {
1613                w.print("   ");
1614            }
1615            // Then print.
1616            w.println(line);
1617        }
1618    
1619        private void p() {
1620            w.println();
1621        }
1622    
1623        private String getOption(Map<String, OptionDescriptor> options, String optionName, String defaultValue) {
1624            OptionDescriptor optionDescriptor = options.get(optionName);
1625            if (optionDescriptor == null) {
1626                return defaultValue;
1627            }
1628            return optionDescriptor.getValue();
1629        }
1630            
1631        static private String removeFileExtension(String name) {
1632            return name.replaceAll("\\..*", "");
1633        }
1634    
1635        static private String uCamel(String name) {
1636            boolean upNext=true;
1637            StringBuilder sb = new StringBuilder();
1638            for (int i = 0; i < name.length(); i++) {
1639                char c = name.charAt(i);
1640                if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) {
1641                    if( upNext ) {
1642                        c = Character.toUpperCase(c);
1643                        upNext=false;
1644                    }
1645                    sb.append(c);
1646                } else {
1647                    upNext=true;
1648                }
1649            }
1650            return sb.toString();
1651        }
1652    
1653        static private String lCamel(String name) {
1654            if( name == null || name.length()<1 )
1655                return name;
1656            String uCamel = uCamel(name);
1657            return uCamel.substring(0,1).toLowerCase()+uCamel.substring(1);
1658        }
1659        
1660        
1661        private String constantToUCamelCase(String name) {
1662            boolean upNext=true;
1663            StringBuilder sb = new StringBuilder();
1664            for (int i = 0; i < name.length(); i++) {
1665                char c = name.charAt(i);
1666                if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) {
1667                    if( upNext ) {
1668                        c = Character.toUpperCase(c);
1669                        upNext=false;
1670                    } else {
1671                        c = Character.toLowerCase(c);
1672                    }
1673                    sb.append(c);
1674                } else {
1675                    upNext=true;
1676                }
1677            }
1678            return sb.toString();
1679        }
1680    
1681    
1682        private String constantCase(String name) {
1683            StringBuilder sb = new StringBuilder();
1684            for (int i = 0; i < name.length(); i++) {
1685                char c = name.charAt(i);
1686                if( i!=0 && Character.isUpperCase(c) ) {
1687                    sb.append("_");
1688                }
1689                sb.append(Character.toUpperCase(c));
1690            }
1691            return sb.toString();
1692        }
1693    
1694        public File getOut() {
1695            return out;
1696        }
1697    
1698        public void setOut(File outputDirectory) {
1699            this.out = outputDirectory;
1700        }
1701    
1702        public File[] getPath() {
1703            return path;
1704        }
1705    
1706        public void setPath(File[] path) {
1707            this.path = path;
1708        }
1709        
1710    }