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 static org.fusesource.hawtbuf.proto.WireFormat.WIRETYPE_FIXED32;
020    import static org.fusesource.hawtbuf.proto.WireFormat.WIRETYPE_FIXED64;
021    import static org.fusesource.hawtbuf.proto.WireFormat.WIRETYPE_LENGTH_DELIMITED;
022    import static org.fusesource.hawtbuf.proto.WireFormat.WIRETYPE_VARINT;
023    import static org.fusesource.hawtbuf.proto.WireFormat.makeTag;
024    
025    import java.io.File;
026    import java.io.FileInputStream;
027    import java.io.FileNotFoundException;
028    import java.io.FileOutputStream;
029    import java.io.PrintWriter;
030    import java.util.ArrayList;
031    import java.util.HashSet;
032    import java.util.LinkedHashMap;
033    import java.util.Map;
034    import java.util.StringTokenizer;
035    
036    import org.fusesource.hawtbuf.proto.compiler.parser.ParseException;
037    import org.fusesource.hawtbuf.proto.compiler.parser.ProtoParser;
038    
039    public class AltJavaGenerator {
040    
041        private File out = new File(".");
042        private File[] path = new File[]{new File(".")};
043    
044        private ProtoDescriptor proto;
045        private String javaPackage;
046        private String outerClassName;
047        private PrintWriter w;
048        private int indent;
049        private ArrayList<String> errors = new ArrayList<String>();
050        private boolean multipleFiles;
051        private boolean auto_clear_optional_fields;
052    
053        private String factoryFeild = "FACTORY";
054        private String getterCN = "Getter";
055        private String beanCN = "Bean";
056        private String bufferCN = "Buffer";
057    
058        public static void main(String[] args) {
059    
060            AltJavaGenerator generator = new AltJavaGenerator();
061            args = CommandLineSupport.setOptions(generator, args);
062    
063            if (args.length == 0) {
064                System.out.println("No proto files specified.");
065            }
066            for (int i = 0; i < args.length; i++) {
067                try {
068                    System.out.println("Compiling: " + args[i]);
069                    generator.compile(new File(args[i]));
070                } catch (CompilerException e) {
071                    System.out.println("Protocol Buffer Compiler failed with the following error(s):");
072                    for (String error : e.getErrors()) {
073                        System.out.println("");
074                        System.out.println(error);
075                    }
076                    System.out.println("");
077                    System.out.println("Compile failed.  For more details see error messages listed above.");
078                    return;
079                }
080            }
081    
082        }
083    
084        interface Closure {
085            void execute() throws CompilerException;
086        }
087    
088        public void compile(File file) throws CompilerException {
089    
090            // Parse the proto file
091            FileInputStream is = null;
092            try {
093                is = new FileInputStream(file);
094                ProtoParser parser = new ProtoParser(is);
095                proto = parser.ProtoDescriptor();
096                proto.setName(file.getName());
097                loadImports(proto, file.getParentFile());
098                proto.validate(errors);
099            } catch (FileNotFoundException e) {
100                errors.add("Failed to open: " + file.getPath() + ":" + e.getMessage());
101            } catch (ParseException e) {
102                errors.add("Failed to parse: " + file.getPath() + ":" + e.getMessage());
103            } finally {
104                try {
105                    is.close();
106                } catch (Throwable ignore) {
107                }
108            }
109    
110            if (!errors.isEmpty()) {
111                throw new CompilerException(errors);
112            }
113    
114            // Load the options..
115            javaPackage = javaPackage(proto);
116            outerClassName = javaClassName(proto);
117    //        optimizeFor = getOption(proto.getOptions(), "optimize_for", "SPEED");
118            multipleFiles = isMultipleFilesEnabled(proto);
119    //              deferredDecode = Boolean.parseBoolean(getOption(proto.getOptions(), "deferred_decode", "false"));
120            auto_clear_optional_fields = Boolean.parseBoolean(getOption(proto.getOptions(), "auto_clear_optional_fields", "false"));
121    
122            if (multipleFiles) {
123                generateProtoFile();
124            } else {
125                writeFile(outerClassName, new Closure() {
126                    public void execute() throws CompilerException {
127                        generateProtoFile();
128                    }
129                });
130            }
131    
132            if (!errors.isEmpty()) {
133                throw new CompilerException(errors);
134            }
135    
136        }
137    
138        private void writeFile(String className, Closure closure) throws CompilerException {
139            PrintWriter oldWriter = w;
140            // Figure out the java file name..
141            File outputFile = out;
142            if (javaPackage != null) {
143                String packagePath = javaPackage.replace('.', '/');
144                outputFile = new File(outputFile, packagePath);
145            }
146            outputFile = new File(outputFile, className + ".java");
147    
148            // Start writing the output file..
149            outputFile.getParentFile().mkdirs();
150    
151            FileOutputStream fos = null;
152            try {
153                fos = new FileOutputStream(outputFile);
154                w = new PrintWriter(fos);
155                closure.execute();
156                w.flush();
157            } catch (FileNotFoundException e) {
158                errors.add("Failed to write to: " + outputFile.getPath() + ":" + e.getMessage());
159            } finally {
160                try {
161                    fos.close();
162                } catch (Throwable ignore) {
163                }
164                w = oldWriter;
165            }
166        }
167    
168        private void loadImports(ProtoDescriptor proto, File protoDir) {
169            LinkedHashMap<String, ProtoDescriptor> children = new LinkedHashMap<String, ProtoDescriptor>();
170            for (String imp : proto.getImports()) {
171                File file = new File(protoDir, imp);
172                for (int i = 0; i < path.length && !file.exists(); i++) {
173                    file = new File(path[i], imp);
174                }
175                if (!file.exists()) {
176                    errors.add("Cannot load import: " + imp);
177                }
178    
179                FileInputStream is = null;
180                try {
181                    is = new FileInputStream(file);
182                    ProtoParser parser = new ProtoParser(is);
183                    ProtoDescriptor child = parser.ProtoDescriptor();
184                    child.setName(file.getName());
185                    loadImports(child, file.getParentFile());
186                    children.put(imp, child);
187                } catch (ParseException e) {
188                    errors.add("Failed to parse: " + file.getPath() + ":" + e.getMessage());
189                } catch (FileNotFoundException e) {
190                    errors.add("Failed to open: " + file.getPath() + ":" + e.getMessage());
191                } finally {
192                    try {
193                        is.close();
194                    } catch (Throwable ignore) {
195                    }
196                }
197            }
198            proto.setImportProtoDescriptors(children);
199        }
200    
201    
202        private void generateProtoFile() throws CompilerException {
203            if (multipleFiles) {
204                for (EnumDescriptor value : proto.getEnums().values()) {
205                    final EnumDescriptor o = value;
206                    String className = uCamel(o.getName());
207                    writeFile(className, new Closure() {
208                        public void execute() throws CompilerException {
209                            generateFileHeader();
210                            generateEnum(o);
211                        }
212                    });
213                }
214                for (MessageDescriptor value : proto.getMessages().values()) {
215                    final MessageDescriptor o = value;
216                    String className = uCamel(o.getName());
217                    writeFile(className, new Closure() {
218                        public void execute() throws CompilerException {
219                            generateFileHeader();
220                            generateMessageBean(o);
221                        }
222                    });
223                }
224    
225            } else {
226                generateFileHeader();
227    
228                p("public class " + outerClassName + " {");
229                indent();
230    
231                for (EnumDescriptor enumType : proto.getEnums().values()) {
232                    generateEnum(enumType);
233                }
234                for (MessageDescriptor m : proto.getMessages().values()) {
235                    generateMessageBean(m);
236                }
237    
238                unindent();
239                p("}");
240            }
241        }
242    
243        private void generateFileHeader() {
244            p("//");
245            p("// Generated by protoc, do not edit by hand.");
246            p("//");
247            if (javaPackage != null) {
248                p("package " + javaPackage + ";");
249                p("");
250            }
251        }
252    
253        private void generateMessageBean(MessageDescriptor m) {
254    
255            String type = uCamel(m.getName());
256            p();
257    
258            String staticOption = "static ";
259            if (multipleFiles && m.getParent() == null) {
260                staticOption = "";
261            }
262    
263            p(staticOption + "public class " + type + " implements org.fusesource.hawtbuf.proto.PBMessageFactory<" + qualified(type, beanCN) + ", " + qualified(type, bufferCN) + "> {");
264            p();
265            indent();
266    
267            for (EnumDescriptor enumType : m.getEnums().values()) {
268                generateEnum(enumType);
269            }
270    
271            // Generate the Nested Messages.
272            for (MessageDescriptor subMessage : m.getMessages().values()) {
273                generateMessageBean(subMessage);
274            }
275    
276            // Generate the Group Messages
277            for (FieldDescriptor field : m.getFields().values()) {
278                if (field.isGroup()) {
279                    generateMessageBean(field.getGroup());
280                }
281            }
282    
283            p("public static final " + type + " "+factoryFeild+" = new " + type + "();");
284            p("public static final org.fusesource.hawtbuf.proto.PBMessageFramedCodec<"+bufferCN+"> FRAMED_CODEC = new org.fusesource.hawtbuf.proto.PBMessageFramedCodec<"+bufferCN+">("+factoryFeild+");");
285            p("public static final org.fusesource.hawtbuf.proto.PBMessageUnframedCodec<"+bufferCN+"> UNFRAMED_CODEC = new org.fusesource.hawtbuf.proto.PBMessageUnframedCodec<"+bufferCN+">("+factoryFeild+");");
286            p();
287    
288            p("public " + beanCN + " create()  {");
289            indent();
290            p("return new " + beanCN + "();");
291            unindent();
292            p("}");
293            p();
294            generateMethodParseFrom(m, bufferCN, beanCN);
295    
296            // Generate the field getters
297            String gettrsExtendsClause = " extends org.fusesource.hawtbuf.proto.PBMessage<" + qualified(type, beanCN) + ", " + qualified(type, bufferCN) + ">";
298            for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) {
299                String name = uCamel(enumFeild.getParent().getName());
300                name = name + "." + name + "Creatable";
301                gettrsExtendsClause += ", " + name;
302            }
303            p(staticOption + "public interface " + getterCN + gettrsExtendsClause + " {");
304            p();
305            indent();
306            {
307                for (FieldDescriptor field : m.getFields().values()) {
308                    generateFieldGetterSignatures(field);
309                }
310                p("public " + beanCN + " copy();");
311                p("public " + bufferCN + " freeze();");
312                p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix);");
313            }
314            unindent();
315            p("}");
316    
317            p();
318            p("static public final class " + beanCN + " implements " + getterCN + " {");
319            p();
320            indent();
321    
322            p("" + bufferCN + " frozen;");
323            p("" + beanCN + " bean;");
324            p();
325            p("public " + beanCN + "() {");
326            indent();
327            p("this.bean = this;");
328            unindent();
329            p("}");
330            p();
331            p("public " + beanCN + "(" + beanCN + " copy) {");
332            indent();
333            p("this.bean = copy;");
334            unindent();
335            p("}");
336            p();
337            p("public " + beanCN + " copy() {");
338            indent();
339            p("return new " + beanCN + "(bean);");
340            unindent();
341            p("}");
342            p();
343    
344            generateMethodFreeze(m, bufferCN);
345    
346            p("private void copyCheck() {");
347            indent();
348            p("assert frozen==null : org.fusesource.hawtbuf.proto.MessageBufferSupport.FORZEN_ERROR_MESSAGE;");
349            p("if (bean != this) {");
350            indent();
351            p("copy(bean);");
352            unindent();
353            p("}");
354            unindent();
355            p("}");
356            p();
357    
358            generateMethodCopyFromBean(m, beanCN);
359    
360            // Generate the field accessors..
361            for (FieldDescriptor field : m.getFields().values()) {
362                generateFieldAccessor(beanCN, field);
363            }
364    
365            generateMethodToString(m);
366    
367            generateMethodMergeFromStream(m, beanCN);
368    
369            generateBeanEquals(m, beanCN);
370    
371            generateMethodMergeFromBean(m, getterCN, beanCN);
372    
373            generateMethodClear(m);
374    
375            generateReadWriteExternal(m);
376    
377            for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) {
378                String enumName = uCamel(enumFeild.getParent().getName());
379                p("public " + enumName + " to" + enumName + "() {");
380                indent();
381                p("return " + enumName + "." + enumFeild.getName() + ";");
382                unindent();
383                p("}");
384                p();
385            }
386    
387            unindent();
388            p("}");
389            p();
390    
391            p("static public final class " + bufferCN + " implements org.fusesource.hawtbuf.proto.MessageBuffer<" + qualified(type, beanCN) + ", " + qualified(type, bufferCN) + ">, " + getterCN + " {");
392            p();
393            indent();
394    
395            p("private " + beanCN + " bean;");
396            p("private org.fusesource.hawtbuf.Buffer buffer;");
397            p("private int size=-1;");
398            p("private int hashCode;");
399            p();
400            p("private " + bufferCN + "(org.fusesource.hawtbuf.Buffer buffer) {");
401            indent();
402            p("this.buffer = buffer;");
403            unindent();
404            p("}");
405            p();
406            p("private " + bufferCN + "(" + beanCN + " bean) {");
407            indent();
408            p("this.bean = bean;");
409            unindent();
410            p("}");
411            p();
412            p("public " + beanCN + " copy() {");
413            indent();
414            p("return bean().copy();");
415            unindent();
416            p("}");
417            p();
418            p("public " + bufferCN + " freeze() {");
419            indent();
420            p("return this;");
421            unindent();
422            p("}");
423            p();
424            p("private " + beanCN + " bean() {");
425            indent();
426            p("if (bean == null) {");
427            indent();
428            p("try {");
429            indent();
430            p("bean = new " + beanCN + "().mergeUnframed(new org.fusesource.hawtbuf.proto.CodedInputStream(buffer));");
431            p("bean.frozen=this;");
432            unindent();
433            p("} catch (org.fusesource.hawtbuf.proto.InvalidProtocolBufferException e) {");
434            indent();
435            p("throw new RuntimeException(e);");
436            unindent();
437            p("} catch (java.io.IOException e) {");
438            indent();
439            p("throw new RuntimeException(\"An IOException was thrown (should never happen in this method).\", e);");
440            unindent();
441            p("}");
442            unindent();
443            p("}");
444            p("return bean;");
445            unindent();
446            p("}");
447            p();
448    
449            p("public String toString() {");
450            indent();
451            p("return bean().toString();");
452            unindent();
453            p("}");
454            p();
455            p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {");
456            indent();
457            p("return bean().toString(sb, prefix);");
458            unindent();
459            p("}");
460            p();
461    
462            for (FieldDescriptor field : m.getFields().values()) {
463                generateBufferGetters(field);
464            }
465    
466            generateMethodWrite(m);
467    
468            generateMethodSerializedSize(m);
469    
470            generateBufferEquals(m, bufferCN);
471    
472            p("public boolean frozen() {");
473            indent();
474            p("return true;");
475            unindent();
476            p("}");
477    
478            for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) {
479                String enumName = uCamel(enumFeild.getParent().getName());
480                p("public " + enumName + " to" + enumName + "() {");
481                indent();
482                p("return " + enumName + "." + enumFeild.getName() + ";");
483                unindent();
484                p("}");
485                p();
486            }
487    
488            unindent();
489            p("}");
490            p();
491    
492    
493    //        generateMethodVisitor(m);
494    //        generateMethodType(m, className);
495    
496    
497            unindent();
498            p("}");
499            p();
500    
501        }
502    
503    
504        private void generateMethodFreeze(MessageDescriptor m, String bufferClassName) {
505            p("public boolean frozen() {");
506            indent();
507            p("return frozen!=null;");
508            unindent();
509            p("}");
510            p();
511            p("public " + bufferClassName + " freeze() {");
512            indent();
513            p("if( frozen==null ) {");
514            indent();
515            p("frozen = new " + bufferClassName + "(bean);");
516            p("assert deepFreeze();");
517            unindent();
518            p("}");
519            p("return frozen;");
520            unindent();
521            p("}");
522            p();
523            p("private boolean deepFreeze() {");
524            indent();
525            p("frozen.serializedSizeUnframed();");
526            p("return true;");
527            unindent();
528            p("}");
529            p();
530    
531        }
532    
533    
534        private boolean isPrimitive(String type) {
535            return type.equals("int") ||
536                    type.equals("long") ||
537                    type.equals("double") ||
538                    type.equals("float") ||
539                    type.equals("boolean");
540        }
541    
542        private boolean isBuferOrString(String type) {
543            return type.equals("org.fusesource.hawtbuf.AsciiBuffer") ||
544                    type.equals("org.fusesource.hawtbuf.UTF8Buffer") ||
545                    type.equals("org.fusesource.hawtbuf.Buffer") ||
546                    type.equals("String");
547        }
548    
549        /**
550         * @param m
551         * @param className
552         */
553        private void generateMethodCopyFromBean(MessageDescriptor m, String className) {
554            p("private void copy(" + className + " other) {");
555            indent();
556            p("this.bean = this;");
557            for (FieldDescriptor field : m.getFields().values()) {
558                String lname = lCamel(field.getName());
559                String type = field.getRule() == FieldDescriptor.REPEATED_RULE ? javaCollectionType(field) : javaType(field);
560                boolean primitive = isPrimitive(type);
561                if (field.isRepeated()) {
562                    if (primitive || isBuferOrString(type) || field.getTypeDescriptor().isEnum()) {
563                        p("this.f_" + lname + " = other.f_" + lname + ";");
564                        p("if( this.f_" + lname + " !=null && !other.frozen()) {");
565                        indent();
566                        p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">(this.f_" + lname + ");");
567                        unindent();
568                        p("}");
569                    } else {
570                        p("this.f_" + lname + " = other.f_" + lname + ";");
571                        p("if( this.f_" + lname + " !=null) {");
572                        indent();
573                        p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">(other.f_" + lname + ".size());");
574                        p("for( " + type + " e :  other.f_" + lname + ") {");
575                        indent();
576                        p("this.f_" + lname + ".add(e.copy());");
577                        unindent();
578                        p("}");
579                        unindent();
580                        p("}");
581                    }
582                } else {
583                    if (primitive || isBuferOrString(type) || field.getTypeDescriptor().isEnum()) {
584                        p("this.f_" + lname + " = other.f_" + lname + ";");
585                        if (primitive) {
586                            p("this.b_" + lname + " = other.b_" + lname + ";");
587                        }
588                    } else {
589                        p("this.f_" + lname + " = other.f_" + lname + ";");
590                        p("if( this.f_" + lname + " !=null ) {");
591                        indent();
592                        p("this.f_" + lname + " = this.f_" + lname + ".copy();");
593                        unindent();
594                        p("}");
595                    }
596                }
597            }
598            unindent();
599            p("}");
600            p();
601        }
602    
603    
604        /**
605         * If the java_visitor message option is set, then this method generates a visitor method.  The option
606         * speifiies the class name of the visitor and optionally the return value and exceptions thrown by the visitor.
607         * <p/>
608         * Examples:
609         * <p/>
610         * option java_visitor = "org.apache.kahadb.store.Visitor";
611         * generates:
612         * public void visit(org.apache.kahadb.store.Visitor visitor) {
613         * visitor.visit(this);
614         * }
615         * <p/>
616         * option java_visitor = "org.apache.kahadb.store.Visitor:int:java.io.IOException";
617         * generates:
618         * public int visit(org.apache.kahadb.store.Visitor visitor) throws java.io.IOException {
619         * return visitor.visit(this);
620         * }
621         *
622         * @param m
623         */
624        private void generateMethodVisitor(MessageDescriptor m) {
625            String javaVisitor = getOption(m.getOptions(), "java_visitor", null);
626            if (javaVisitor != null) {
627                String returns = "void";
628                String throwsException = null;
629    
630                StringTokenizer st = new StringTokenizer(javaVisitor, ":");
631                String vistorClass = st.nextToken();
632                if (st.hasMoreTokens()) {
633                    returns = st.nextToken();
634                }
635                if (st.hasMoreTokens()) {
636                    throwsException = st.nextToken();
637                }
638    
639                String throwsClause = "";
640                if (throwsException != null) {
641                    throwsClause = "throws " + throwsException + " ";
642                }
643    
644                p("public " + returns + " visit(" + vistorClass + " visitor) " + throwsClause + "{");
645                indent();
646                if ("void".equals(returns)) {
647                    p("visitor.visit(this);");
648                } else {
649                    p("return visitor.visit(this);");
650                }
651                unindent();
652                p("}");
653                p();
654            }
655        }
656    
657        private void generateMethodType(MessageDescriptor m, String className) {
658            String typeEnum = getOption(m.getOptions(), "java_type_method", null);
659            if (typeEnum != null) {
660    
661                TypeDescriptor typeDescriptor = m.getType(typeEnum);
662                if (typeDescriptor == null) {
663                    typeDescriptor = m.getProtoDescriptor().getType(typeEnum);
664                }
665                if (typeDescriptor == null || !typeDescriptor.isEnum()) {
666                    errors.add("The java_type_method option on the " + m.getName() + " message does not point to valid enum type");
667                    return;
668                }
669    
670    
671                String constant = constantCase(className);
672                EnumDescriptor enumDescriptor = (EnumDescriptor) typeDescriptor;
673                if (enumDescriptor.getFields().get(constant) == null) {
674                    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);
675                }
676    
677                String type = qualified(javaFactoryType(typeDescriptor), getterCN);
678    
679                p("public " + type + " type() {");
680                indent();
681                p("return " + type + "." + constant + ";");
682                unindent();
683                p("}");
684                p();
685            }
686        }
687    
688        private void generateMethodParseFrom(MessageDescriptor m, String bufferClassName, String beanClassName) {
689            p("public " + beanClassName + " parseUnframed(org.fusesource.hawtbuf.proto.CodedInputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
690            indent();
691            p("return new " + beanClassName + "().mergeUnframed(data);");
692            unindent();
693            p("}");
694            p();
695            p("public " + beanClassName + " parseUnframed(java.io.InputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
696            indent();
697            p("return parseUnframed(new org.fusesource.hawtbuf.proto.CodedInputStream(data));");
698            unindent();
699            p("}");
700            p();
701            p("public " + bufferClassName + " parseUnframed(org.fusesource.hawtbuf.Buffer data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
702            indent();
703            p("return new " + bufferClassName + "(data);");
704            unindent();
705            p("}");
706            p();
707            p("public " + bufferClassName + " parseUnframed(byte[] data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
708            indent();
709            p("return parseUnframed(new org.fusesource.hawtbuf.Buffer(data));");
710            unindent();
711            p("}");
712            p();
713            p("public " + bufferClassName + " parseFramed(org.fusesource.hawtbuf.proto.CodedInputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
714            indent();
715            p("int length = data.readRawVarint32();");
716            p("int oldLimit = data.pushLimit(length);");
717            p("" + bufferClassName + " rc = parseUnframed(data.readRawBytes(length));");
718            p("data.popLimit(oldLimit);");
719            p("return rc;");
720            unindent();
721            p("}");
722            p();
723            p("public " + bufferClassName + " parseFramed(org.fusesource.hawtbuf.Buffer data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
724            indent();
725            p("try {");
726            indent();
727            p("org.fusesource.hawtbuf.proto.CodedInputStream input = new org.fusesource.hawtbuf.proto.CodedInputStream(data);");
728            p("" + bufferClassName + " rc = parseFramed(input);");
729            p("input.checkLastTagWas(0);");
730            p("return rc;");
731            unindent();
732            p("} catch (org.fusesource.hawtbuf.proto.InvalidProtocolBufferException e) {");
733            indent();
734            p("throw e;");
735            unindent();
736            p("} catch (java.io.IOException e) {");
737            indent();
738            p("throw new RuntimeException(\"An IOException was thrown (should never happen in this method).\", e);");
739            unindent();
740            p("}");
741            unindent();
742            p("}");
743            p();
744            p("public " + bufferClassName + " parseFramed(byte[] data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException {");
745            indent();
746            p("return parseFramed(new org.fusesource.hawtbuf.Buffer(data));");
747            unindent();
748            p("}");
749            p();
750            p("public " + bufferClassName + " parseFramed(java.io.InputStream data) throws org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException {");
751            indent();
752            p("return parseUnframed(org.fusesource.hawtbuf.proto.MessageBufferSupport.readFrame(data));");
753            unindent();
754            p("}");
755            p();
756    
757        }
758    
759        private void generateBeanEquals(MessageDescriptor m, String className) {
760            p("public boolean equals(Object obj) {");
761            indent();
762            p("if( obj==this )");
763            p("   return true;");
764            p("");
765            p("if( obj==null || obj.getClass()!=" + className + ".class )");
766            p("   return false;");
767            p("");
768            p("return equals((" + className + ")obj);");
769            unindent();
770            p("}");
771            p("");
772    
773            p("public boolean equals(" + className + " obj) {");
774            indent();
775            for (FieldDescriptor field : m.getFields().values()) {
776                String uname = uCamel(field.getName());
777                String getterMethod = "get" + uname + "()";
778                String hasMethod = "has" + uname + "()";
779    
780                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
781                    getterMethod = "get" + uname + "List()";
782                }
783    
784                p("if (" + hasMethod + " ^ obj." + hasMethod + " ) ");
785                p("   return false;");
786    
787    
788                if (field.getRule() != FieldDescriptor.REPEATED_RULE && (field.isNumberType() || field.getType() == FieldDescriptor.BOOL_TYPE)) {
789                    p("if (" + hasMethod + " && ( " + getterMethod + "!=obj." + getterMethod + " ))");
790                } else {
791                    p("if (" + hasMethod + " && ( !" + getterMethod + ".equals(obj." + getterMethod + ") ))");
792                }
793                p("   return false;");
794            }
795            p("return true;");
796            unindent();
797            p("}");
798            p("");
799            p("public int hashCode() {");
800            indent();
801            int hc = className.hashCode();
802            p("int rc=" + hc + ";");
803            int counter = 0;
804            for (FieldDescriptor field : m.getFields().values()) {
805                counter++;
806    
807                String uname = uCamel(field.getName());
808                String getterMethod = "get" + uname + "()";
809                String hasMethod = "has" + uname + "()";
810    
811                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
812                    getterMethod = "get" + uname + "List()";
813                }
814    
815                p("if (" + hasMethod + ") {");
816                indent();
817    
818                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
819                    p("rc ^= ( " + uname.hashCode() + "^" + getterMethod + ".hashCode() );");
820                } else if (field.isInteger32Type()) {
821                    p("rc ^= ( " + uname.hashCode() + "^" + getterMethod + " );");
822                } else if (field.isInteger64Type()) {
823                    p("rc ^= ( " + uname.hashCode() + "^(new Long(" + getterMethod + ")).hashCode() );");
824                } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
825                    p("rc ^= ( " + uname.hashCode() + "^(new Double(" + getterMethod + ")).hashCode() );");
826                } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
827                    p("rc ^= ( " + uname.hashCode() + "^(new Double(" + getterMethod + ")).hashCode() );");
828                } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
829                    p("rc ^= ( " + uname.hashCode() + "^ (" + getterMethod + "? " + counter + ":-" + counter + ") );");
830                } else {
831                    p("rc ^= ( " + uname.hashCode() + "^" + getterMethod + ".hashCode() );");
832                }
833                unindent();
834                p("}");
835            }
836            p("return rc;");
837            unindent();
838            p("}");
839            p("");
840        }
841    
842        private void generateBufferEquals(MessageDescriptor m, String className) {
843            p("public boolean equals(Object obj) {");
844            indent();
845            p("if( obj==this )");
846            p("   return true;");
847            p("");
848            p("if( obj==null || obj.getClass()!=" + className + ".class )");
849            p("   return false;");
850            p("");
851            p("return equals((" + className + ")obj);");
852            unindent();
853            p("}");
854            p("");
855    
856            p("public boolean equals(" + className + " obj) {");
857            indent();
858            p("return toUnframedBuffer().equals(obj.toUnframedBuffer());");
859            unindent();
860            p("}");
861            p("");
862            p("public int hashCode() {");
863            indent();
864            int hc = className.hashCode();
865            p("if( hashCode==0 ) {");
866            p("hashCode=" + hc + " ^ toUnframedBuffer().hashCode();");
867            p("}");
868            p("return hashCode;");
869            unindent();
870            p("}");
871            p("");
872        }
873    
874        /**
875         * @param m
876         */
877        private void generateMethodSerializedSize(MessageDescriptor m) {
878    
879            p("public int serializedSizeFramed() {");
880            indent();
881            p("int t = serializedSizeUnframed();");
882            p("return org.fusesource.hawtbuf.proto.CodedOutputStream.computeRawVarint32Size(t) + t;");
883            unindent();
884            p("}");
885            p();
886            p("public int serializedSizeUnframed() {");
887            indent();
888            p("if (buffer != null) {");
889            indent();
890            p("return buffer.length;");
891            unindent();
892            p("}");
893            p("if (size != -1)");
894            p("   return size;");
895            p();
896            p("size = 0;");
897            for (FieldDescriptor field : m.getFields().values()) {
898    
899                String uname = uCamel(field.getName());
900                String getter = "get" + uname + "()";
901                String type = javaType(field);
902    
903                if (!field.isRequired()) {
904                    p("if (has" + uname + "()) {");
905                    indent();
906                }
907    
908                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
909                    p("for (" + type + " i : get" + uname + "List()) {");
910                    indent();
911                    getter = "i";
912                }
913    
914                if (field.getType() == FieldDescriptor.STRING_TYPE) {
915                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeStringSize(" + field.getTag() + ", " + getter + ");");
916                } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
917                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeBytesSize(" + field.getTag() + ", " + getter + ");");
918                } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
919                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeBoolSize(" + field.getTag() + ", " + getter + ");");
920                } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
921                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeDoubleSize(" + field.getTag() + ", " + getter + ");");
922                } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
923                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFloatSize(" + field.getTag() + ", " + getter + ");");
924                } else if (field.getType() == FieldDescriptor.INT32_TYPE) {
925                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeInt32Size(" + field.getTag() + ", " + getter + ");");
926                } else if (field.getType() == FieldDescriptor.INT64_TYPE) {
927                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeInt64Size(" + field.getTag() + ", " + getter + ");");
928                } else if (field.getType() == FieldDescriptor.SINT32_TYPE) {
929                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSInt32Size(" + field.getTag() + ", " + getter + ");");
930                } else if (field.getType() == FieldDescriptor.SINT64_TYPE) {
931                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSInt64Size(" + field.getTag() + ", " + getter + ");");
932                } else if (field.getType() == FieldDescriptor.UINT32_TYPE) {
933                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeUInt32Size(" + field.getTag() + ", " + getter + ");");
934                } else if (field.getType() == FieldDescriptor.UINT64_TYPE) {
935                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeUInt64Size(" + field.getTag() + ", " + getter + ");");
936                } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) {
937                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFixed32Size(" + field.getTag() + ", " + getter + ");");
938                } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) {
939                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeFixed64Size(" + field.getTag() + ", " + getter + ");");
940                } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) {
941                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSFixed32Size(" + field.getTag() + ", " + getter + ");");
942                } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) {
943                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeSFixed64Size(" + field.getTag() + ", " + getter + ");");
944                } else if (field.getTypeDescriptor().isEnum()) {
945                    p("size += org.fusesource.hawtbuf.proto.CodedOutputStream.computeEnumSize(" + field.getTag() + ", " + getter + ".getNumber());");
946                } else if (field.getGroup() != null) {
947                    errors.add("This code generator does not support group fields.");
948    //                p("size += org.fusesource.hawtbuf.proto.MessageBufferSupport.computeGroupSize("+field.getTag()+", ("+type+"Buffer)"+getter+");");
949                } else {
950                    p("size += org.fusesource.hawtbuf.proto.MessageBufferSupport.computeMessageSize(" + field.getTag() + ", " + getter + ".freeze());");
951                }
952                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
953                    unindent();
954                    p("}");
955                }
956    
957                if (!field.isRequired()) {
958                    unindent();
959                    p("}");
960                }
961    
962            }
963            // TODO: handle unknown fields
964            // size += getUnknownFields().getSerializedSize();");
965            p("return size;");
966            unindent();
967            p("}");
968            p();
969        }
970    
971        /**
972         * @param m
973         */
974        private void generateMethodWrite(MessageDescriptor m) {
975    
976            p("public org.fusesource.hawtbuf.Buffer toUnframedBuffer() {");
977            indent();
978            p("if( buffer !=null ) {");
979            indent();
980            p("return buffer;");
981            unindent();
982            p("}");
983            p("return org.fusesource.hawtbuf.proto.MessageBufferSupport.toUnframedBuffer(this);");
984            unindent();
985            p("}");
986            p();
987            p("public org.fusesource.hawtbuf.Buffer toFramedBuffer() {");
988            indent();
989            p("return org.fusesource.hawtbuf.proto.MessageBufferSupport.toFramedBuffer(this);");
990            unindent();
991            p("}");
992            p();
993            p("public byte[] toUnframedByteArray() {");
994            indent();
995            p("return toUnframedBuffer().toByteArray();");
996            unindent();
997            p("}");
998            p();
999            p("public byte[] toFramedByteArray() {");
1000            indent();
1001            p("return toFramedBuffer().toByteArray();");
1002            unindent();
1003            p("}");
1004            p();
1005            p("public void writeFramed(org.fusesource.hawtbuf.proto.CodedOutputStream output) throws java.io.IOException {");
1006            indent();
1007            p("output.writeRawVarint32(serializedSizeUnframed());");
1008            p("writeUnframed(output);");
1009            unindent();
1010            p("}");
1011            p();
1012            p("public void writeFramed(java.io.OutputStream output) throws java.io.IOException {");
1013            indent();
1014            p("org.fusesource.hawtbuf.proto.CodedOutputStream codedOutput = new org.fusesource.hawtbuf.proto.CodedOutputStream(output);");
1015            p("writeFramed(codedOutput);");
1016            p("codedOutput.flush();");
1017            unindent();
1018            p("}");
1019            p();
1020    
1021            p("public void writeUnframed(java.io.OutputStream output) throws java.io.IOException {");
1022            indent();
1023            p("org.fusesource.hawtbuf.proto.CodedOutputStream codedOutput = new org.fusesource.hawtbuf.proto.CodedOutputStream(output);");
1024            p("writeUnframed(codedOutput);");
1025            p("codedOutput.flush();");
1026            unindent();
1027            p("}");
1028            p();
1029    
1030            p("public void writeUnframed(org.fusesource.hawtbuf.proto.CodedOutputStream output) throws java.io.IOException {");
1031            indent();
1032    
1033            p("if (buffer == null) {");
1034            indent();
1035            p("int size = serializedSizeUnframed();");
1036            p("buffer = output.getNextBuffer(size);");
1037            p("org.fusesource.hawtbuf.proto.CodedOutputStream original=null;");
1038            p("if( buffer == null ) {");
1039            indent();
1040            p("buffer = new org.fusesource.hawtbuf.Buffer(new byte[size]);");
1041            p("original = output;");
1042            p("output = new org.fusesource.hawtbuf.proto.CodedOutputStream(buffer);");
1043            unindent();
1044            p("}");
1045    
1046            for (FieldDescriptor field : m.getFields().values()) {
1047                String uname = uCamel(field.getName());
1048                String getter = "bean.get" + uname + "()";
1049                String type = javaType(field);
1050    
1051                if (!field.isRequired()) {
1052                    p("if (bean.has" + uname + "()) {");
1053                    indent();
1054                }
1055    
1056                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
1057                    p("for (" + type + " i : bean.get" + uname + "List()) {");
1058                    indent();
1059                    getter = "i";
1060                }
1061    
1062                if (field.getType() == FieldDescriptor.STRING_TYPE) {
1063                    p("output.writeString(" + field.getTag() + ", " + getter + ");");
1064                } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1065                    p("output.writeBytes(" + field.getTag() + ", " + getter + ");");
1066                } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1067                    p("output.writeBool(" + field.getTag() + ", " + getter + ");");
1068                } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1069                    p("output.writeDouble(" + field.getTag() + ", " + getter + ");");
1070                } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1071                    p("output.writeFloat(" + field.getTag() + ", " + getter + ");");
1072                } else if (field.getType() == FieldDescriptor.INT32_TYPE) {
1073                    p("output.writeInt32(" + field.getTag() + ", " + getter + ");");
1074                } else if (field.getType() == FieldDescriptor.INT64_TYPE) {
1075                    p("output.writeInt64(" + field.getTag() + ", " + getter + ");");
1076                } else if (field.getType() == FieldDescriptor.SINT32_TYPE) {
1077                    p("output.writeSInt32(" + field.getTag() + ", " + getter + ");");
1078                } else if (field.getType() == FieldDescriptor.SINT64_TYPE) {
1079                    p("output.writeSInt64(" + field.getTag() + ", " + getter + ");");
1080                } else if (field.getType() == FieldDescriptor.UINT32_TYPE) {
1081                    p("output.writeUInt32(" + field.getTag() + ", " + getter + ");");
1082                } else if (field.getType() == FieldDescriptor.UINT64_TYPE) {
1083                    p("output.writeUInt64(" + field.getTag() + ", " + getter + ");");
1084                } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) {
1085                    p("output.writeFixed32(" + field.getTag() + ", " + getter + ");");
1086                } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) {
1087                    p("output.writeFixed64(" + field.getTag() + ", " + getter + ");");
1088                } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) {
1089                    p("output.writeSFixed32(" + field.getTag() + ", " + getter + ");");
1090                } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) {
1091                    p("output.writeSFixed64(" + field.getTag() + ", " + getter + ");");
1092                } else if (field.getTypeDescriptor().isEnum()) {
1093                    p("output.writeEnum(" + field.getTag() + ", " + getter + ".getNumber());");
1094                } else if (field.getGroup() != null) {
1095                    errors.add("This code generator does not support group fields.");
1096    //                p("writeGroup(output, "+field.getTag()+", "+getter+");");
1097                } else {
1098                    p("org.fusesource.hawtbuf.proto.MessageBufferSupport.writeMessage(output, " + field.getTag() + ", " + getter + ".freeze());");
1099                }
1100    
1101                if (field.getRule() == FieldDescriptor.REPEATED_RULE) {
1102                    unindent();
1103                    p("}");
1104                }
1105    
1106                if (!field.isRequired()) {
1107                    unindent();
1108                    p("}");
1109                }
1110            }
1111    
1112            p("if( original !=null ) {");
1113            indent();
1114            p("output.checkNoSpaceLeft();");
1115            p("output = original;");
1116            p("output.writeRawBytes(buffer);");
1117            unindent();
1118            p("}");
1119            unindent();
1120            p("} else {");
1121            indent();
1122            p("output.writeRawBytes(buffer);");
1123            unindent();
1124            p("}");
1125    
1126            unindent();
1127            p("}");
1128            p();
1129        }
1130    
1131        /**
1132         * @param m
1133         * @param className
1134         */
1135        private void generateMethodMergeFromStream(MessageDescriptor m, String className) {
1136            p("public " + className + " mergeUnframed(java.io.InputStream input) throws java.io.IOException {");
1137            indent();
1138            p("return mergeUnframed(new org.fusesource.hawtbuf.proto.CodedInputStream(input));");
1139            unindent();
1140            p("}");
1141            p();
1142            p("public " + className + " mergeUnframed(org.fusesource.hawtbuf.proto.CodedInputStream input) throws java.io.IOException {");
1143            indent();
1144            {
1145                p("copyCheck();");
1146                p("while (true) {");
1147                indent();
1148                {
1149                    p("int tag = input.readTag();");
1150                    p("if ((tag & 0x07) == 4) {");
1151                    p("   return this;");
1152                    p("}");
1153    
1154                    p("switch (tag) {");
1155                    p("case 0:");
1156                    p("   return this;");
1157                    p("default: {");
1158    
1159                    p("   break;");
1160                    p("}");
1161    
1162                    for (FieldDescriptor field : m.getFields().values()) {
1163                        String uname = uCamel(field.getName());
1164                        String setter = "set" + uname;
1165                        boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1166                        if (repeated) {
1167                            setter = "create" + uname + "List().add";
1168                        }
1169                        if (field.getType() == FieldDescriptor.STRING_TYPE) {
1170                            p("case "
1171                                    + makeTag(field.getTag(),
1172                                    WIRETYPE_LENGTH_DELIMITED) + ":");
1173                            indent();
1174                            p(setter + "(input.readString());");
1175                        } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1176                            p("case " + makeTag(field.getTag(), WIRETYPE_LENGTH_DELIMITED) + ":");
1177                            indent();
1178                            String override = getOption(field.getOptions(), "java_override_type", null);
1179                            if ("AsciiBuffer".equals(override)) {
1180                                p(setter + "(new org.fusesource.hawtbuf.AsciiBuffer(input.readBytes()));");
1181                            } else if ("UTF8Buffer".equals(override)) {
1182                                p(setter + "(new org.fusesource.hawtbuf.UTF8Buffer(input.readBytes()));");
1183                            } else {
1184                                p(setter + "(input.readBytes());");
1185                            }
1186                        } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1187                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + ":");
1188                            indent();
1189                            p(setter + "(input.readBool());");
1190                        } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1191                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
1192                                    + ":");
1193                            indent();
1194                            p(setter + "(input.readDouble());");
1195                        } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1196                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
1197                                    + ":");
1198                            indent();
1199                            p(setter + "(input.readFloat());");
1200                        } else if (field.getType() == FieldDescriptor.INT32_TYPE) {
1201                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1202                                    + ":");
1203                            indent();
1204                            p(setter + "(input.readInt32());");
1205                        } else if (field.getType() == FieldDescriptor.INT64_TYPE) {
1206                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1207                                    + ":");
1208                            indent();
1209                            p(setter + "(input.readInt64());");
1210                        } else if (field.getType() == FieldDescriptor.SINT32_TYPE) {
1211                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1212                                    + ":");
1213                            indent();
1214                            p(setter + "(input.readSInt32());");
1215                        } else if (field.getType() == FieldDescriptor.SINT64_TYPE) {
1216                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1217                                    + ":");
1218                            indent();
1219                            p(setter + "(input.readSInt64());");
1220                        } else if (field.getType() == FieldDescriptor.UINT32_TYPE) {
1221                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1222                                    + ":");
1223                            indent();
1224                            p(setter + "(input.readUInt32());");
1225                        } else if (field.getType() == FieldDescriptor.UINT64_TYPE) {
1226                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1227                                    + ":");
1228                            indent();
1229                            p(setter + "(input.readUInt64());");
1230                        } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) {
1231                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
1232                                    + ":");
1233                            indent();
1234                            p(setter + "(input.readFixed32());");
1235                        } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) {
1236                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
1237                                    + ":");
1238                            indent();
1239                            p(setter + "(input.readFixed64());");
1240                        } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) {
1241                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32)
1242                                    + ":");
1243                            indent();
1244                            p(setter + "(input.readSFixed32());");
1245                        } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) {
1246                            p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64)
1247                                    + ":");
1248                            indent();
1249                            p(setter + "(input.readSFixed64());");
1250                        } else if (field.getTypeDescriptor().isEnum()) {
1251                            p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)
1252                                    + ":");
1253                            indent();
1254                            String type = javaType(field);
1255                            p("{");
1256                            indent();
1257                            p("int t = input.readEnum();");
1258                            p("" + type + " value = " + type + ".valueOf(t);");
1259                            p("if( value !=null ) {");
1260                            indent();
1261                            p(setter + "(value);");
1262                            unindent();
1263                            p("}");
1264                            // TODO: else store it as an known
1265    
1266                            unindent();
1267                            p("}");
1268    
1269                        } else if (field.getGroup() != null) {
1270                            errors.add("This code generator does not support group fields.");
1271    //                                              p("case "+ makeTag(field.getTag(), WIRETYPE_START_GROUP)+ ":");
1272    //                                              indent();
1273    //                                              String type = javaType(field);
1274    //                                              if (repeated) {
1275    //                                                      p(setter + "(readGroup(input, " + field.getTag()+ ", new " + type + "()));");
1276    //                                              } else {
1277    //                                                      p("if (has" + uname + "()) {");
1278    //                                                      indent();
1279    //                                                      p("readGroup(input, " + field.getTag() + ", get"
1280    //                                                                      + uname + "());");
1281    //                                                      unindent();
1282    //                                                      p("} else {");
1283    //                                                      indent();
1284    //                                                      p(setter + "(readGroup(input, " + field.getTag()
1285    //                                                                      + ",new " + type + "()));");
1286    //                                                      unindent();
1287    //                                                      p("}");
1288    //                                              }
1289    //                                              p("");
1290                        } else {
1291                            p("case " + makeTag(field.getTag(), WIRETYPE_LENGTH_DELIMITED) + ":");
1292                            indent();
1293                            String type = javaFactoryType(field.getTypeDescriptor());
1294                            if (repeated) {
1295                                p(setter + "(" + qualified(type, factoryFeild) + ".parseFramed(input));");
1296                            } else {
1297                                p("if (has" + uname + "()) {");
1298                                indent();
1299                                p("set" + uname + "(get" + uname + "().copy().mergeFrom(" + qualified(type, factoryFeild) + ".parseFramed(input)));");
1300                                unindent();
1301                                p("} else {");
1302                                indent();
1303                                p(setter + "(" + qualified(type, factoryFeild) + ".parseFramed(input));");
1304                                unindent();
1305                                p("}");
1306                            }
1307                        }
1308                        p("break;");
1309                        unindent();
1310                    }
1311                    p("}");
1312                }
1313                unindent();
1314                p("}");
1315            }
1316            unindent();
1317            p("}");
1318        }
1319    
1320        /**
1321         * @param m
1322         * @param getterClassName
1323         */
1324        private void generateMethodMergeFromBean(MessageDescriptor m, String getterClassName, String beanClassName) {
1325            p("public " + beanClassName + " mergeFrom(" + getterClassName + " other) {");
1326            indent();
1327            p("copyCheck();");
1328            for (FieldDescriptor field : m.getFields().values()) {
1329                String uname = uCamel(field.getName());
1330                p("if (other.has" + uname + "()) {");
1331                indent();
1332    
1333                if (field.isScalarType() || field.getTypeDescriptor().isEnum()) {
1334                    if (field.isRepeated()) {
1335                        p("get" + uname + "List().addAll(other.get" + uname + "List());");
1336                    } else {
1337                        p("set" + uname + "(other.get" + uname + "());");
1338                    }
1339                } else {
1340    
1341                    String type = qualified(javaFactoryType(field.getTypeDescriptor()), getterCN);
1342    
1343                    // It's complex type...
1344                    if (field.isRepeated()) {
1345                        p("for(" + type + " element: other.get" + uname + "List() ) {");
1346                        indent();
1347                        p("get" + uname + "List().add(element.copy());");
1348                        unindent();
1349                        p("}");
1350                    } else {
1351                        p("if (has" + uname + "()) {");
1352                        indent();
1353                        p("set" + uname + "(get" + uname + "().copy().mergeFrom(other.get" + uname + "()));");
1354                        unindent();
1355                        p("} else {");
1356                        indent();
1357                        p("set" + uname + "(other.get" + uname + "().copy());");
1358                        unindent();
1359                        p("}");
1360                    }
1361                }
1362                unindent();
1363                p("}");
1364            }
1365            p("return this;");
1366            unindent();
1367            p("}");
1368            p();
1369        }
1370    
1371        /**
1372         * @param m
1373         */
1374        private void generateMethodClear(MessageDescriptor m) {
1375            p("public void clear() {");
1376            indent();
1377            for (FieldDescriptor field : m.getFields().values()) {
1378                String uname = uCamel(field.getName());
1379                p("clear" + uname + "();");
1380            }
1381            unindent();
1382            p("}");
1383            p();
1384        }
1385    
1386        private void generateReadWriteExternal(MessageDescriptor m) {
1387    
1388            p("public void readExternal(java.io.DataInput in) throws java.io.IOException {");
1389            indent();
1390            p("assert frozen==null : org.fusesource.hawtbuf.proto.MessageBufferSupport.FORZEN_ERROR_MESSAGE;");
1391            p("bean = this;");
1392            p("frozen = null;");
1393    
1394            for (FieldDescriptor field : m.getFields().values()) {
1395                String lname = lCamel(field.getName());
1396                String type = javaType(field);
1397                boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1398    
1399                // Create the fields..
1400                if (repeated) {
1401                    p("{");
1402                    indent();
1403                    p("int size = in.readShort();");
1404                    p("if( size>=0 ) {");
1405                    indent();
1406                    p("f_" + lname + " = new java.util.ArrayList<" + javaCollectionType(field) + ">(size);");
1407                    p("for(int i=0; i<size; i++) {");
1408                    indent();
1409                    if (field.isInteger32Type()) {
1410                        p("f_" + lname + ".add(in.readInt());");
1411                    } else if (field.isInteger64Type()) {
1412                        p("f_" + lname + ".add(in.readLong());");
1413                    } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1414                        p("f_" + lname + ".add(in.readDouble());");
1415                    } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1416                        p("f_" + lname + ".add(in.readFloat());");
1417                    } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1418                        p("f_" + lname + ".add(in.readBoolean());");
1419                    } else if (field.getType() == FieldDescriptor.STRING_TYPE) {
1420                        p("f_" + lname + ".add(in.readUTF());");
1421                    } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1422                        p("byte b[] = new byte[in.readInt()];");
1423                        p("in.readFully(b);");
1424                        p("f_" + lname + ".add(new " + type + "(b));");
1425                    } else if (field.getTypeDescriptor().isEnum()) {
1426                        p("f_" + lname + ".add(" + type + ".valueOf(in.readShort()));");
1427                    } else {
1428                        p("" + qualified(type, beanCN) + " o = new " + qualified(type, beanCN) + "();");
1429                        p("o.readExternal(in);");
1430                        p("f_" + lname + ".add(o);");
1431                    }
1432                    unindent();
1433                    p("}");
1434                    unindent();
1435                    p("} else {");
1436                    indent();
1437                    p("f_" + lname + " = null;");
1438                    unindent();
1439                    p("}");
1440                    unindent();
1441                    p("}");
1442    
1443                } else {
1444                    if (field.isInteger32Type()) {
1445                        p("f_" + lname + " = in.readInt();");
1446                        p("b_" + lname + " = true;");
1447                    } else if (field.isInteger64Type()) {
1448                        p("f_" + lname + " = in.readLong();");
1449                        p("b_" + lname + " = true;");
1450                    } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1451                        p("f_" + lname + " = in.readDouble();");
1452                        p("b_" + lname + " = true;");
1453                    } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1454                        p("f_" + lname + " = in.readFloat();");
1455                        p("b_" + lname + " = true;");
1456                    } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1457                        p("f_" + lname + " = in.readBoolean();");
1458                        p("b_" + lname + " = true;");
1459                    } else if (field.getType() == FieldDescriptor.STRING_TYPE) {
1460                        p("if( in.readBoolean() ) {");
1461                        indent();
1462                        p("f_" + lname + " = in.readUTF();");
1463                        unindent();
1464                        p("} else {");
1465                        indent();
1466                        p("f_" + lname + " = null;");
1467                        unindent();
1468                        p("}");
1469                    } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1470                        p("{");
1471                        indent();
1472                        p("int size = in.readInt();");
1473                        p("if( size>=0 ) {");
1474                        indent();
1475                        p("byte b[] = new byte[size];");
1476                        p("in.readFully(b);");
1477                        p("f_" + lname + " = new " + type + "(b);");
1478                        unindent();
1479                        p("} else {");
1480                        indent();
1481                        p("f_" + lname + " = null;");
1482                        unindent();
1483                        p("}");
1484                        unindent();
1485                        p("}");
1486                    } else if (field.getTypeDescriptor().isEnum()) {
1487                        p("if( in.readBoolean() ) {");
1488                        indent();
1489                        p("f_" + lname + " = " + type + ".valueOf(in.readShort());");
1490                        unindent();
1491                        p("} else {");
1492                        indent();
1493                        p("f_" + lname + " = null;");
1494                        unindent();
1495                        p("}");
1496                    } else {
1497                        p("if( in.readBoolean() ) {");
1498                        indent();
1499                        String factoryType = javaFactoryType(field.getTypeDescriptor());
1500                        p("" + qualified(factoryType, beanCN) + " o = new " + qualified(factoryType, beanCN) + "();");
1501                        p("o.readExternal(in);");
1502                        p("f_" + lname + " = o;");
1503                        unindent();
1504                        p("} else {");
1505                        indent();
1506                        p("f_" + lname + " = null;");
1507                        unindent();
1508                        p("}");
1509                    }
1510                }
1511            }
1512    
1513            unindent();
1514            p("}");
1515            p();
1516            p("public void writeExternal(java.io.DataOutput out) throws java.io.IOException {");
1517            indent();
1518            for (FieldDescriptor field : m.getFields().values()) {
1519                String lname = lCamel(field.getName());
1520                boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1521    
1522                // Create the fields..
1523                if (repeated) {
1524                    p("if( bean.f_" + lname + "!=null ) {");
1525                    indent();
1526                    p("out.writeShort(bean.f_" + lname + ".size());");
1527                    p("for(" + javaCollectionType(field) + " o : bean.f_" + lname + ") {");
1528                    indent();
1529    
1530                    if (field.isInteger32Type()) {
1531                        p("out.writeInt(o);");
1532                    } else if (field.isInteger64Type()) {
1533                        p("out.writeLong(o);");
1534                    } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1535                        p("out.writeDouble(o);");
1536                    } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1537                        p("out.writeFloat(o);");
1538                    } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1539                        p("out.writeBoolean(o);");
1540                    } else if (field.getType() == FieldDescriptor.STRING_TYPE) {
1541                        p("out.writeUTF(o);");
1542                    } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1543                        p("out.writeInt(o.getLength());");
1544                        p("out.write(o.getData(), o.getOffset(), o.getLength());");
1545                    } else if (field.getTypeDescriptor().isEnum()) {
1546                        p("out.writeShort(o.getNumber());");
1547                    } else {
1548                        p("o.copy().writeExternal(out);");
1549                    }
1550                    unindent();
1551                    p("}");
1552                    unindent();
1553                    p("} else {");
1554                    indent();
1555                    p("out.writeShort(-1);");
1556                    unindent();
1557                    p("}");
1558    
1559                } else {
1560                    if (field.isInteger32Type()) {
1561                        p("out.writeInt(bean.f_" + lname + ");");
1562                    } else if (field.isInteger64Type()) {
1563                        p("out.writeLong(bean.f_" + lname + ");");
1564                    } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
1565                        p("out.writeDouble(bean.f_" + lname + ");");
1566                    } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
1567                        p("out.writeFloat(bean.f_" + lname + ");");
1568                    } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
1569                        p("out.writeBoolean(bean.f_" + lname + ");");
1570                    } else if (field.getType() == FieldDescriptor.STRING_TYPE) {
1571                        p("if( bean.f_" + lname + "!=null ) {");
1572                        indent();
1573                        p("out.writeBoolean(true);");
1574                        p("out.writeUTF(bean.f_" + lname + ");");
1575                        unindent();
1576                        p("} else {");
1577                        indent();
1578                        p("out.writeBoolean(false);");
1579                        unindent();
1580                        p("}");
1581                    } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1582                        p("if( bean.f_" + lname + "!=null ) {");
1583                        indent();
1584                        p("out.writeInt(bean.f_" + lname + ".getLength());");
1585                        p("out.write(bean.f_" + lname + ".getData(), bean.f_" + lname + ".getOffset(), bean.f_" + lname + ".getLength());");
1586                        unindent();
1587                        p("} else {");
1588                        indent();
1589                        p("out.writeInt(-1);");
1590                        unindent();
1591                        p("}");
1592                    } else if (field.getTypeDescriptor().isEnum()) {
1593                        p("if( bean.f_" + lname + "!=null ) {");
1594                        indent();
1595                        p("out.writeBoolean(true);");
1596                        p("out.writeShort(bean.f_" + lname + ".getNumber());");
1597                        unindent();
1598                        p("} else {");
1599                        indent();
1600                        p("out.writeBoolean(false);");
1601                        unindent();
1602                        p("}");
1603                    } else {
1604                        p("if( bean.f_" + lname + "!=null ) {");
1605                        indent();
1606                        p("out.writeBoolean(true);");
1607                        p("bean.f_" + lname + ".copy().writeExternal(out);");
1608                        unindent();
1609                        p("} else {");
1610                        indent();
1611                        p("out.writeBoolean(false);");
1612                        unindent();
1613                        p("}");
1614                    }
1615                }
1616            }
1617    
1618            unindent();
1619            p("}");
1620            p();
1621    
1622        }
1623    
1624    
1625    //    private void generateMethodAssertInitialized(MessageDescriptor m, String className) {
1626    //        
1627    //        p("public java.util.ArrayList<String> missingFields() {");
1628    //        indent();
1629    //        p("java.util.ArrayList<String> missingFields = super.missingFields();");
1630    //        
1631    //        for (FieldDescriptor field : m.getFields().values()) {
1632    //            String uname = uCamel(field.getName());
1633    //            if( field.isRequired() ) {
1634    //                p("if(  !has" + uname + "() ) {");
1635    //                indent();
1636    //                p("missingFields.add(\""+field.getName()+"\");");
1637    //                unindent();
1638    //                p("}");
1639    //            }
1640    //        }
1641    //        
1642    //        if( !deferredDecode ) {
1643    //              for (FieldDescriptor field : m.getFields().values()) {
1644    //                  if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) {
1645    //                      String uname = uCamel(field.getName());
1646    //                      p("if( has" + uname + "() ) {");
1647    //                      indent();
1648    //                      if( !field.isRepeated() ) {
1649    //                          p("try {");
1650    //                          indent();
1651    //                          p("get" + uname + "().assertInitialized();");
1652    //                          unindent();
1653    //                          p("} catch (org.fusesource.hawtbuf.proto.UninitializedMessageException e){");
1654    //                          indent();
1655    //                          p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+".\"));");
1656    //                          unindent();
1657    //                          p("}");
1658    //                      } else {
1659    //                          String type = javaCollectionType(field);
1660    //                          p("java.util.List<"+type+"> l = get" + uname + "List();");
1661    //                          p("for( int i=0; i < l.size(); i++ ) {");
1662    //                          indent();
1663    //                          p("try {");
1664    //                          indent();
1665    //                          p("l.get(i).assertInitialized();");
1666    //                          unindent();
1667    //                          p("} catch (org.fusesource.hawtbuf.proto.UninitializedMessageException e){");
1668    //                          indent();
1669    //                          p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+"[\"+i+\"]\"));");
1670    //                          unindent();
1671    //                          p("}");
1672    //                          unindent();
1673    //                          p("}");
1674    //                      }
1675    //                      unindent();
1676    //                      p("}");
1677    //                  }
1678    //              }
1679    //        }
1680    //        p("return missingFields;");
1681    //        unindent();
1682    //        p("}");
1683    //        p();
1684    //    }
1685    
1686        private void generateMethodToString(MessageDescriptor m) {
1687    
1688            p("public String toString() {");
1689            indent();
1690            p("return toString(new java.lang.StringBuilder(), \"\").toString();");
1691            unindent();
1692            p("}");
1693            p();
1694    
1695            p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {");
1696            indent();
1697            for (FieldDescriptor field : m.getFields().values()) {
1698                String uname = uCamel(field.getName());
1699                p("if(  has" + uname + "() ) {");
1700                indent();
1701                if (field.isRepeated()) {
1702                    String type = javaCollectionType(field);
1703                    p("java.util.List<" + type + "> l = get" + uname + "List();");
1704                    p("for( int i=0; i < l.size(); i++ ) {");
1705                    indent();
1706                    if (field.getTypeDescriptor() != null && !field.getTypeDescriptor().isEnum()) {
1707                        p("sb.append(prefix+\"" + field.getName() + "[\"+i+\"] {\\n\");");
1708                        p("l.get(i).toString(sb, prefix+\"  \");");
1709                        p("sb.append(prefix+\"}\\n\");");
1710                    } else {
1711                        p("sb.append(prefix+\"" + field.getName() + "[\"+i+\"]: \");");
1712                        p("sb.append(l.get(i));");
1713                        p("sb.append(\"\\n\");");
1714                    }
1715                    unindent();
1716                    p("}");
1717                } else {
1718                    if (field.getTypeDescriptor() != null && !field.getTypeDescriptor().isEnum()) {
1719                        p("sb.append(prefix+\"" + field.getName() + " {\\n\");");
1720                        p("get" + uname + "().toString(sb, prefix+\"  \");");
1721                        p("sb.append(prefix+\"}\\n\");");
1722                    } else {
1723                        p("sb.append(prefix+\"" + field.getName() + ": \");");
1724                        p("sb.append(get" + uname + "());");
1725                        p("sb.append(\"\\n\");");
1726                    }
1727                }
1728                unindent();
1729                p("}");
1730            }
1731            p("return sb;");
1732            unindent();
1733            p("}");
1734            p();
1735    
1736        }
1737    
1738        /**
1739         * @param field
1740         */
1741        private void generateBufferGetters(FieldDescriptor field) {
1742            String uname = uCamel(field.getName());
1743            String type = field.getRule() == FieldDescriptor.REPEATED_RULE ? javaCollectionType(field) : javaType(field);
1744            boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1745    
1746            // Create the fields..
1747            p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";");
1748            if (repeated) {
1749                // Create the field accessors
1750                p("public boolean has" + uname + "() {");
1751                indent();
1752                p("return bean().has" + uname + "();");
1753                unindent();
1754                p("}");
1755                p();
1756                p("public java.util.List<" + type + "> get" + uname + "List() {");
1757                indent();
1758                p("return bean().get" + uname + "List();");
1759                unindent();
1760                p("}");
1761                p();
1762                p("public int get" + uname + "Count() {");
1763                indent();
1764                p("return bean().get" + uname + "Count();");
1765                unindent();
1766                p("}");
1767                p();
1768                p("public " + type + " get" + uname + "(int index) {");
1769                indent();
1770                p("return bean().get" + uname + "(index);");
1771                unindent();
1772                p("}");
1773                p();
1774            } else {
1775                // Create the field accessors
1776                p("public boolean has" + uname + "() {");
1777                indent();
1778                p("return bean().has" + uname + "();");
1779                unindent();
1780                p("}");
1781                p();
1782                p("public " + type + " get" + uname + "() {");
1783                indent();
1784                p("return bean().get" + uname + "();");
1785                unindent();
1786                p("}");
1787                p();
1788            }
1789        }
1790    
1791        /**
1792         * @param field
1793         */
1794        private void generateFieldGetterSignatures(FieldDescriptor field) {
1795            String uname = uCamel(field.getName());
1796            String type = field.getRule() == FieldDescriptor.REPEATED_RULE ? javaCollectionType(field) : javaType(field);
1797            boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1798    
1799            // Create the fields..
1800            p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";");
1801            if (repeated) {
1802                // Create the field accessors
1803                p("public boolean has" + uname + "();");
1804                p("public java.util.List<" + type + "> get" + uname + "List();");
1805                p("public int get" + uname + "Count();");
1806                p("public " + type + " get" + uname + "(int index);");
1807            } else {
1808                // Create the field accessors
1809                p("public boolean has" + uname + "();");
1810                p("public " + type + " get" + uname + "();");
1811            }
1812        }
1813    
1814    
1815        /**
1816         * @param field
1817         */
1818        private void generateFieldAccessor(String beanClassName, FieldDescriptor field) {
1819    
1820            String lname = lCamel(field.getName());
1821            String uname = uCamel(field.getName());
1822            String type = field.getRule() == FieldDescriptor.REPEATED_RULE ? javaCollectionType(field) : javaType(field);
1823            String typeDefault = javaTypeDefault(field);
1824            boolean primitive = isPrimitive(type);
1825            boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE;
1826    
1827            // Create the fields..
1828            p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";");
1829    
1830            if (repeated) {
1831                p("private java.util.List<" + type + "> f_" + lname + ";");
1832                p();
1833    
1834                // Create the field accessors
1835                p("public boolean has" + uname + "() {");
1836                indent();
1837                p("return bean.f_" + lname + "!=null && !bean.f_" + lname + ".isEmpty();");
1838                unindent();
1839                p("}");
1840                p();
1841    
1842                p("public java.util.List<" + type + "> get" + uname + "List() {");
1843                indent();
1844                p("return bean.f_" + lname + ";");
1845                unindent();
1846                p("}");
1847                p();
1848    
1849                p("public java.util.List<" + type + "> create" + uname + "List() {");
1850                indent();
1851                p("copyCheck();");
1852                p("if( this.f_" + lname + " == null ) {");
1853                indent();
1854                p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">();");
1855                unindent();
1856                p("}");
1857                p("return bean.f_" + lname + ";");
1858                unindent();
1859                p("}");
1860                p();
1861    
1862                p("public " + beanClassName + " set" + uname + "List(java.util.List<" + type + "> " + lname + ") {");
1863                indent();
1864                p("copyCheck();");
1865                p("this.f_" + lname + " = " + lname + ";");
1866                p("return this;");
1867                unindent();
1868                p("}");
1869                p();
1870    
1871                p("public int get" + uname + "Count() {");
1872                indent();
1873                p("if( bean.f_" + lname + " == null ) {");
1874                indent();
1875                p("return 0;");
1876                unindent();
1877                p("}");
1878                p("return bean.f_" + lname + ".size();");
1879                unindent();
1880                p("}");
1881                p();
1882    
1883                p("public " + type + " get" + uname + "(int index) {");
1884                indent();
1885                p("if( bean.f_" + lname + " == null ) {");
1886                indent();
1887                p("return null;");
1888                unindent();
1889                p("}");
1890                p("return bean.f_" + lname + ".get(index);");
1891                unindent();
1892                p("}");
1893                p();
1894    
1895                p("public " + beanClassName + " set" + uname + "(int index, " + type + " value) {");
1896                indent();
1897                p("this.create" + uname + "List().set(index, value);");
1898                p("return this;");
1899                unindent();
1900                p("}");
1901                p();
1902    
1903                p("public " + beanClassName + " add" + uname + "(" + type + " value) {");
1904                indent();
1905                p("this.create" + uname + "List().add(value);");
1906                p("return this;");
1907                unindent();
1908                p("}");
1909                p();
1910    
1911                p("public " + beanClassName + " addAll" + uname + "(java.lang.Iterable<? extends " + type + "> collection) {");
1912                indent();
1913                p("org.fusesource.hawtbuf.proto.MessageBufferSupport.addAll(collection, this.create" + uname + "List());");
1914                p("return this;");
1915                unindent();
1916                p("}");
1917                p();
1918    
1919                p("public void clear" + uname + "() {");
1920                indent();
1921                p("copyCheck();");
1922                p("this.f_" + lname + " = null;");
1923                unindent();
1924                p("}");
1925                p();
1926    
1927            } else {
1928    
1929                p("private " + type + " f_" + lname + " = " + typeDefault + ";");
1930                if (primitive) {
1931                    p("private boolean b_" + lname + ";");
1932                }
1933                p();
1934    
1935                // Create the field accessors
1936                p("public boolean has" + uname + "() {");
1937                indent();
1938                if (primitive) {
1939                    p("return bean.b_" + lname + ";");
1940                } else {
1941                    p("return bean.f_" + lname + "!=null;");
1942                }
1943                unindent();
1944                p("}");
1945                p();
1946    
1947                p("public " + type + " get" + uname + "() {");
1948                indent();
1949                p("return bean.f_" + lname + ";");
1950                unindent();
1951                p("}");
1952                p();
1953    
1954                p("public " + beanClassName + " set" + uname + "(" + type + " " + lname + ") {");
1955                indent();
1956                p("copyCheck();");
1957                if (primitive) {
1958                    if (auto_clear_optional_fields && field.isOptional()) {
1959                        if (field.isStringType() && !"null".equals(typeDefault)) {
1960                            p("this.b_" + lname + " = (" + lname + " != " + typeDefault + ");");
1961                        } else {
1962                            p("this.b_" + lname + " = (" + lname + " != " + typeDefault + ");");
1963                        }
1964                    } else {
1965                        p("this.b_" + lname + " = true;");
1966                    }
1967                }
1968                p("this.f_" + lname + " = " + lname + ";");
1969                p("return this;");
1970                unindent();
1971                p("}");
1972                p();
1973    
1974                p("public void clear" + uname + "() {");
1975                indent();
1976                p("copyCheck();");
1977                if (primitive) {
1978                    p("this.b_" + lname + " = false;");
1979                }
1980                p("this.f_" + lname + " = " + typeDefault + ";");
1981                unindent();
1982                p("}");
1983                p();
1984            }
1985    
1986        }
1987    
1988        private String javaTypeDefault(FieldDescriptor field) {
1989            OptionDescriptor defaultOption = field.getOptions().get("default");
1990            if (defaultOption != null) {
1991                if (field.isStringType()) {
1992                    return asJavaString(defaultOption.getValue());
1993                } else if (field.getType() == FieldDescriptor.BYTES_TYPE) {
1994                    return "new " + javaType(field) + "(org.fusesource.hawtbuf.UTF8Buffer.encode(" + asJavaString(defaultOption.getValue()) + "))";
1995                } else if (field.isInteger32Type()) {
1996                    int v;
1997                    if (field.getType() == FieldDescriptor.UINT32_TYPE) {
1998                        v = TextFormat.parseUInt32(defaultOption.getValue());
1999                    } else {
2000                        v = TextFormat.parseInt32(defaultOption.getValue());
2001                    }
2002                    return "" + v;
2003                } else if (field.isInteger64Type()) {
2004                    long v;
2005                    if (field.getType() == FieldDescriptor.UINT64_TYPE) {
2006                        v = TextFormat.parseUInt64(defaultOption.getValue());
2007                    } else {
2008                        v = TextFormat.parseInt64(defaultOption.getValue());
2009                    }
2010                    return "" + v + "l";
2011                } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
2012                    double v = Double.valueOf(defaultOption.getValue());
2013                    return "" + v + "d";
2014                } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
2015                    float v = Float.valueOf(defaultOption.getValue());
2016                    return "" + v + "f";
2017                } else if (field.getType() == FieldDescriptor.BOOL_TYPE) {
2018                    boolean v = Boolean.valueOf(defaultOption.getValue());
2019                    return "" + v;
2020                } else if (field.getTypeDescriptor() != null && field.getTypeDescriptor().isEnum()) {
2021                    return javaType(field) + "." + defaultOption.getValue();
2022                }
2023                return defaultOption.getValue();
2024            } else {
2025                if (field.isNumberType()) {
2026                    return "0";
2027                }
2028                if (field.getType() == FieldDescriptor.BOOL_TYPE) {
2029                    return "false";
2030                }
2031                return "null";
2032            }
2033        }
2034    
2035        static final char HEX_TABLE[] = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
2036    
2037        private String asJavaString(String value) {
2038            StringBuilder sb = new StringBuilder(value.length() + 2);
2039            sb.append("\"");
2040            for (int i = 0; i < value.length(); i++) {
2041    
2042                char b = value.charAt(i);
2043                switch (b) {
2044                    // Java does not recognize \a or \v, apparently.
2045                    case '\b':
2046                        sb.append("\\b");
2047                        break;
2048                    case '\f':
2049                        sb.append("\\f");
2050                        break;
2051                    case '\n':
2052                        sb.append("\\n");
2053                        break;
2054                    case '\r':
2055                        sb.append("\\r");
2056                        break;
2057                    case '\t':
2058                        sb.append("\\t");
2059                        break;
2060                    case '\\':
2061                        sb.append("\\\\");
2062                        break;
2063                    case '\'':
2064                        sb.append("\\\'");
2065                        break;
2066                    case '"':
2067                        sb.append("\\\"");
2068                        break;
2069                    default:
2070                        if (b >= 0x20 && b < 'Z') {
2071                            sb.append((char) b);
2072                        } else {
2073                            sb.append("\\u");
2074                            sb.append(HEX_TABLE[(b >>> 12) & 0x0F]);
2075                            sb.append(HEX_TABLE[(b >>> 8) & 0x0F]);
2076                            sb.append(HEX_TABLE[(b >>> 4) & 0x0F]);
2077                            sb.append(HEX_TABLE[b & 0x0F]);
2078                        }
2079                        break;
2080                }
2081    
2082            }
2083            sb.append("\"");
2084            return sb.toString();
2085        }
2086    
2087        private void generateEnum(EnumDescriptor ed) {
2088            String uname = uCamel(ed.getName());
2089    
2090            String staticOption = "static ";
2091            if (multipleFiles && ed.getParent() == null) {
2092                staticOption = "";
2093            }
2094    
2095            // TODO Auto-generated method stub
2096            p();
2097            p("public " + staticOption + "enum " + uname + " {");
2098            indent();
2099    
2100    
2101            p();
2102            int counter = 0;
2103            for (EnumFieldDescriptor field : ed.getFields().values()) {
2104                boolean last = counter + 1 == ed.getFields().size();
2105                p(field.getName() + "(\"" + field.getName() + "\", " + field.getValue() + ")" + (last ? ";" : ","));
2106                counter++;
2107            }
2108            p();
2109            p("private final String name;");
2110            p("private final int value;");
2111            p();
2112            p("private " + uname + "(String name, int value) {");
2113            p("   this.name = name;");
2114            p("   this.value = value;");
2115            p("}");
2116            p();
2117            p("public final int getNumber() {");
2118            p("   return value;");
2119            p("}");
2120            p();
2121            p("public final String toString() {");
2122            p("   return name;");
2123            p("}");
2124            p();
2125            p("public static " + uname + " valueOf(int value) {");
2126            p("   switch (value) {");
2127    
2128            // It's possible to define multiple ENUM fields with the same value.. 
2129            //   we only want to put the first one into the switch statement.
2130            HashSet<Integer> values = new HashSet<Integer>();
2131            for (EnumFieldDescriptor field : ed.getFields().values()) {
2132                if (!values.contains(field.getValue())) {
2133                    p("   case " + field.getValue() + ":");
2134                    p("      return " + field.getName() + ";");
2135                    values.add(field.getValue());
2136                }
2137    
2138            }
2139            p("   default:");
2140            p("      return null;");
2141            p("   }");
2142            p("}");
2143            p();
2144    
2145    
2146            String createMessage = getOption(ed.getOptions(), "java_create_message", null);
2147            if ("true".equals(createMessage)) {
2148    
2149                p("public interface " + uname + "Creatable {");
2150                indent();
2151                p("" + uname + " to" + uname + "();");
2152                unindent();
2153                p("}");
2154                p();
2155    
2156                p("public " + uname + "Creatable createBean() {");
2157                indent();
2158                p("switch (this) {");
2159                indent();
2160                for (EnumFieldDescriptor field : ed.getFields().values()) {
2161                    p("case " + field.getName() + ":");
2162                    String type = field.getAssociatedType().getName();
2163                    p("   return new " + qualified(type, beanCN) + "();");
2164                }
2165                p("default:");
2166                p("   return null;");
2167                unindent();
2168                p("}");
2169                unindent();
2170                p("}");
2171                p();
2172    
2173                generateParseDelegate(ed, "parseUnframed", "org.fusesource.hawtbuf.Buffer", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException");
2174                generateParseDelegate(ed, "parseFramed", "org.fusesource.hawtbuf.Buffer", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException");
2175                generateParseDelegate(ed, "parseUnframed", "byte[]", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException");
2176                generateParseDelegate(ed, "parseFramed", "byte[]", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException");
2177                generateParseDelegate(ed, "parseFramed", "org.fusesource.hawtbuf.proto.CodedInputStream", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException");
2178                generateParseDelegate(ed, "parseFramed", "java.io.InputStream", "org.fusesource.hawtbuf.proto.InvalidProtocolBufferException, java.io.IOException");
2179            }
2180    
2181            unindent();
2182            p("}");
2183            p();
2184        }
2185    
2186        private void generateParseDelegate(EnumDescriptor descriptor, String methodName, String inputType, String exceptions) {
2187            p("public org.fusesource.hawtbuf.proto.MessageBuffer " + methodName + "(" + inputType + " data) throws " + exceptions + " {");
2188            indent();
2189            p("switch (this) {");
2190            indent();
2191            for (EnumFieldDescriptor field : descriptor.getFields().values()) {
2192                p("case " + field.getName() + ":");
2193                String type = constantToUCamelCase(field.getName());
2194                p("   return " + qualified(type, factoryFeild) + "." + methodName + "(data);");
2195            }
2196            p("default:");
2197            p("   return null;");
2198            unindent();
2199            p("}");
2200            unindent();
2201            p("}");
2202            p();
2203        }
2204    
2205    
2206        private String javaCollectionType(FieldDescriptor field) {
2207            if (field.isInteger32Type()) {
2208                return "java.lang.Integer";
2209            }
2210            if (field.isInteger64Type()) {
2211                return "java.lang.Long";
2212            }
2213            if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
2214                return "java.lang.Double";
2215            }
2216            if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
2217                return "java.lang.Float";
2218            }
2219            if (field.getType() == FieldDescriptor.STRING_TYPE) {
2220                // TODO: support handling string fields as buffers.
2221    //            String override = getOption(field.getOptions(), "java_override_type", null);
2222    //            if( "AsciiBuffer".equals(override) ) {
2223    //                return "org.fusesource.hawtbuf.AsciiBuffer";
2224    //            } else if( "UTF8Buffer".equals(override) ) {
2225    //                return "org.fusesource.hawtbuf.UTF8Buffer";
2226    //            } else if( "Buffer".equals(override) ) {
2227    //                return "org.fusesource.hawtbuf.Buffer";
2228    //            } else {
2229                return "java.lang.String";
2230    //            }
2231            }
2232            if (field.getType() == FieldDescriptor.BYTES_TYPE) {
2233                String override = getOption(field.getOptions(), "java_override_type", null);
2234                if ("AsciiBuffer".equals(override)) {
2235                    return "org.fusesource.hawtbuf.AsciiBuffer";
2236                } else if ("UTF8Buffer".equals(override)) {
2237                    return "org.fusesource.hawtbuf.UTF8Buffer";
2238                } else {
2239                    return "org.fusesource.hawtbuf.Buffer";
2240                }
2241            }
2242            if (field.getType() == FieldDescriptor.BOOL_TYPE) {
2243                return "java.lang.Boolean";
2244            }
2245    
2246            TypeDescriptor descriptor = field.getTypeDescriptor();
2247            return qualified(javaFactoryType(descriptor), getterCN);
2248        }
2249    
2250        private String javaType(FieldDescriptor field) {
2251            if (field.isInteger32Type()) {
2252                return "int";
2253            }
2254            if (field.isInteger64Type()) {
2255                return "long";
2256            }
2257            if (field.getType() == FieldDescriptor.DOUBLE_TYPE) {
2258                return "double";
2259            }
2260            if (field.getType() == FieldDescriptor.FLOAT_TYPE) {
2261                return "float";
2262            }
2263            if (field.getType() == FieldDescriptor.STRING_TYPE) {
2264                // TODO: support handling string fields as buffers.
2265    //            String override = getOption(field.getOptions(), "java_override_type", null);
2266    //            if( "AsciiBuffer".equals(override) ) {
2267    //                return "org.fusesource.hawtbuf.AsciiBuffer";
2268    //            } else if( "UTF8Buffer".equals(override) ) {
2269    //                return "org.fusesource.hawtbuf.UTF8Buffer";
2270    //            } else if( "Buffer".equals(override) ) {
2271    //                return "org.fusesource.hawtbuf.Buffer";
2272    //            } else {
2273                return "java.lang.String";
2274    //            }
2275            }
2276            if (field.getType() == FieldDescriptor.BYTES_TYPE) {
2277                String override = getOption(field.getOptions(), "java_override_type", null);
2278                if ("AsciiBuffer".equals(override)) {
2279                    return "org.fusesource.hawtbuf.AsciiBuffer";
2280                } else if ("UTF8Buffer".equals(override)) {
2281                    return "org.fusesource.hawtbuf.UTF8Buffer";
2282                } else {
2283                    return "org.fusesource.hawtbuf.Buffer";
2284                }
2285            }
2286            if (field.getType() == FieldDescriptor.BOOL_TYPE) {
2287                return "boolean";
2288            }
2289    
2290            TypeDescriptor descriptor = field.getTypeDescriptor();
2291            return qualified(javaFactoryType(descriptor), getterCN);
2292        }
2293    
2294        private String javaFactoryType(TypeDescriptor descriptor) {
2295            ProtoDescriptor p = descriptor.getProtoDescriptor();
2296            if (p != proto) {
2297                // Try to keep it short..
2298                String othePackage = javaPackage(p);
2299                if (equals(othePackage, javaPackage(proto))) {
2300                    return javaClassName(p) + "." + descriptor.getQName();
2301                }
2302                // Use the fully qualified class name.
2303                return othePackage + "." + javaClassName(p) + "." + descriptor.getQName();
2304            }
2305            return descriptor.getQName();
2306        }
2307    
2308        private String qualified(String type, String suffix) {
2309    //        int ix = type.lastIndexOf(".");
2310    //        if (ix == -1) {
2311    //            // type = Foo, result = Foo.FooBean
2312    //            return type+"."+type+suffix;
2313    //        }
2314    //        // type = Foo.Bar, result = Foo.Bar.BarBean
2315    //        return type+"."+type.substring(ix+1)+suffix;
2316            return type + "." + suffix;
2317        }
2318    
2319        private boolean equals(String o1, String o2) {
2320            if (o1 == o2)
2321                return true;
2322            if (o1 == null || o2 == null)
2323                return false;
2324            return o1.equals(o2);
2325        }
2326    
2327        private String javaClassName(ProtoDescriptor proto) {
2328            return getOption(proto.getOptions(), "java_outer_classname", uCamel(removeFileExtension(proto.getName())));
2329        }
2330    
2331        private boolean isMultipleFilesEnabled(ProtoDescriptor proto) {
2332            return "true".equals(getOption(proto.getOptions(), "java_multiple_files", "false"));
2333        }
2334    
2335    
2336        private String javaPackage(ProtoDescriptor proto) {
2337            String name = proto.getPackageName();
2338            if (name != null) {
2339                name = name.replace('-', '.');
2340                name = name.replace('/', '.');
2341            }
2342            return getOption(proto.getOptions(), "java_package", name);
2343        }
2344    
2345    
2346        // ----------------------------------------------------------------
2347        // Internal Helper methods
2348        // ----------------------------------------------------------------
2349    
2350        private void indent() {
2351            indent++;
2352        }
2353    
2354        private void unindent() {
2355            indent--;
2356        }
2357    
2358        private void p(String line) {
2359            // Indent...
2360            for (int i = 0; i < indent; i++) {
2361                w.print("   ");
2362            }
2363            // Then print.
2364            w.println(line);
2365        }
2366    
2367        private void p() {
2368            w.println();
2369        }
2370    
2371        private String getOption(Map<String, OptionDescriptor> options, String optionName, String defaultValue) {
2372            OptionDescriptor optionDescriptor = options.get(optionName);
2373            if (optionDescriptor == null) {
2374                return defaultValue;
2375            }
2376            return optionDescriptor.getValue();
2377        }
2378    
2379        static private String removeFileExtension(String name) {
2380            return name.replaceAll("\\..*", "");
2381        }
2382    
2383        static private String uCamel(String name) {
2384            boolean upNext = true;
2385            StringBuilder sb = new StringBuilder();
2386            for (int i = 0; i < name.length(); i++) {
2387                char c = name.charAt(i);
2388                if (Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) {
2389                    if (upNext) {
2390                        c = Character.toUpperCase(c);
2391                        upNext = false;
2392                    }
2393                    sb.append(c);
2394                } else {
2395                    upNext = true;
2396                }
2397            }
2398            return sb.toString();
2399        }
2400    
2401        static private String lCamel(String name) {
2402            if (name == null || name.length() < 1)
2403                return name;
2404            String uCamel = uCamel(name);
2405            return uCamel.substring(0, 1).toLowerCase() + uCamel.substring(1);
2406        }
2407    
2408    
2409        private String constantToUCamelCase(String name) {
2410            boolean upNext = true;
2411            StringBuilder sb = new StringBuilder();
2412            for (int i = 0; i < name.length(); i++) {
2413                char c = name.charAt(i);
2414                if (Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) {
2415                    if (upNext) {
2416                        c = Character.toUpperCase(c);
2417                        upNext = false;
2418                    } else {
2419                        c = Character.toLowerCase(c);
2420                    }
2421                    sb.append(c);
2422                } else {
2423                    upNext = true;
2424                }
2425            }
2426            return sb.toString();
2427        }
2428    
2429    
2430        private String constantCase(String name) {
2431            StringBuilder sb = new StringBuilder();
2432            for (int i = 0; i < name.length(); i++) {
2433                char c = name.charAt(i);
2434                if (i != 0 && Character.isUpperCase(c)) {
2435                    sb.append("_");
2436                }
2437                sb.append(Character.toUpperCase(c));
2438            }
2439            return sb.toString();
2440        }
2441    
2442        public File getOut() {
2443            return out;
2444        }
2445    
2446        public void setOut(File outputDirectory) {
2447            this.out = outputDirectory;
2448        }
2449    
2450        public File[] getPath() {
2451            return path;
2452        }
2453    
2454        public void setPath(File[] path) {
2455            this.path = path;
2456        }
2457    
2458    }