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