comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java @ 16759:23415229349b

Truffle-DSL: new package structure.
author Christian Humer <christian.humer@gmail.com>
date Mon, 11 Aug 2014 15:57:14 +0200
parents graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/AbstractCodeWriter.java@0d5923064a88
children 62cfffca9be2
comparison
equal deleted inserted replaced
16758:c5f8eeb3cbc8 16759:23415229349b
1 /*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.truffle.dsl.processor.java.transform;
24
25 import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
26
27 import java.io.*;
28 import java.util.*;
29
30 import javax.lang.model.element.*;
31 import javax.lang.model.type.*;
32 import javax.lang.model.util.*;
33
34 import com.oracle.truffle.dsl.processor.java.*;
35 import com.oracle.truffle.dsl.processor.java.model.*;
36
37 public abstract class AbstractCodeWriter extends CodeElementScanner<Void, Void> {
38
39 private static final int MAX_LINE_LENGTH = 200;
40 private static final int LINE_WRAP_INDENTS = 3;
41 private static final String IDENT_STRING = " ";
42 private static final String LN = "\n"; /* unix style */
43
44 protected Writer writer;
45 private int indent;
46 private boolean newLine;
47 private int lineLength;
48 private boolean lineWrapping = false;
49
50 private OrganizedImports imports;
51
52 protected abstract Writer createWriter(CodeTypeElement clazz) throws IOException;
53
54 @Override
55 public Void visitType(CodeTypeElement e, Void p) {
56 if (e.isTopLevelClass()) {
57 Writer w = null;
58 try {
59 imports = OrganizedImports.organize(e);
60 w = new TrimTrailingSpaceWriter(createWriter(e));
61 writer = w;
62 writeRootClass(e);
63 } catch (IOException ex) {
64 throw new RuntimeException(ex);
65 } finally {
66 if (w != null) {
67 try {
68 w.close();
69 } catch (Throwable e1) {
70 // see eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=361378
71 // TODO temporary suppress errors on close.
72 }
73 }
74 writer = null;
75 }
76 } else {
77 writeClassImpl(e);
78 }
79 return null;
80 }
81
82 private void writeRootClass(CodeTypeElement e) {
83 writeHeader();
84 write("package ").write(e.getPackageName()).write(";").writeLn();
85 writeEmptyLn();
86
87 Set<CodeImport> generateImports = imports.generateImports();
88 List<CodeImport> typeImports = new ArrayList<>();
89 List<CodeImport> staticImports = new ArrayList<>();
90
91 for (CodeImport codeImport : generateImports) {
92 if (codeImport.isStaticImport()) {
93 staticImports.add(codeImport);
94 } else {
95 typeImports.add(codeImport);
96 }
97 }
98 Collections.sort(typeImports);
99 Collections.sort(staticImports);
100
101 for (CodeImport imp : staticImports) {
102 imp.accept(this, null);
103 writeLn();
104 }
105 if (!staticImports.isEmpty()) {
106 writeEmptyLn();
107 }
108
109 for (CodeImport imp : typeImports) {
110 imp.accept(this, null);
111 writeLn();
112 }
113 if (!typeImports.isEmpty()) {
114 writeEmptyLn();
115 }
116
117 writeClassImpl(e);
118 }
119
120 private String useImport(Element enclosedType, TypeMirror type) {
121 if (imports != null) {
122 return imports.createTypeReference(enclosedType, type);
123 } else {
124 return ElementUtils.getSimpleName(type);
125 }
126 }
127
128 private void writeClassImpl(CodeTypeElement e) {
129 for (AnnotationMirror annotation : e.getAnnotationMirrors()) {
130 visitAnnotation(e, annotation);
131 writeLn();
132 }
133
134 writeModifiers(e.getModifiers());
135 if (e.getKind() == ElementKind.ENUM) {
136 write("enum ");
137 } else {
138 write("class ");
139 }
140 write(e.getSimpleName());
141 if (e.getSuperclass() != null && !getQualifiedName(e.getSuperclass()).equals("java.lang.Object")) {
142 write(" extends ").write(useImport(e, e.getSuperclass()));
143 }
144 if (e.getImplements().size() > 0) {
145 write(" implements ");
146 for (int i = 0; i < e.getImplements().size(); i++) {
147 write(useImport(e, e.getImplements().get(i)));
148 if (i < e.getImplements().size() - 1) {
149 write(", ");
150 }
151 }
152 }
153
154 write(" {").writeLn();
155 writeEmptyLn();
156 indent(1);
157
158 List<VariableElement> staticFields = getStaticFields(e);
159 List<VariableElement> instanceFields = getInstanceFields(e);
160
161 for (int i = 0; i < staticFields.size(); i++) {
162 VariableElement field = staticFields.get(i);
163 field.accept(this, null);
164 if (e.getKind() == ElementKind.ENUM && i < staticFields.size() - 1) {
165 write(",");
166 writeLn();
167 } else {
168 write(";");
169 writeLn();
170 }
171 }
172
173 if (staticFields.size() > 0) {
174 writeEmptyLn();
175 }
176
177 for (VariableElement field : instanceFields) {
178 field.accept(this, null);
179 write(";");
180 writeLn();
181 }
182 if (instanceFields.size() > 0) {
183 writeEmptyLn();
184 }
185
186 for (ExecutableElement method : ElementFilter.constructorsIn(e.getEnclosedElements())) {
187 method.accept(this, null);
188 }
189
190 for (ExecutableElement method : getInstanceMethods(e)) {
191 method.accept(this, null);
192 }
193
194 for (ExecutableElement method : getStaticMethods(e)) {
195 method.accept(this, null);
196 }
197
198 for (TypeElement clazz : e.getInnerClasses()) {
199 clazz.accept(this, null);
200 }
201
202 dedent(1);
203 write("}");
204 writeEmptyLn();
205 }
206
207 private static List<VariableElement> getStaticFields(CodeTypeElement clazz) {
208 List<VariableElement> staticFields = new ArrayList<>();
209 for (VariableElement field : clazz.getFields()) {
210 if (field.getModifiers().contains(Modifier.STATIC)) {
211 staticFields.add(field);
212 }
213 }
214 return staticFields;
215 }
216
217 private static List<VariableElement> getInstanceFields(CodeTypeElement clazz) {
218 List<VariableElement> instanceFields = new ArrayList<>();
219 for (VariableElement field : clazz.getFields()) {
220 if (!field.getModifiers().contains(Modifier.STATIC)) {
221 instanceFields.add(field);
222 }
223 }
224 return instanceFields;
225 }
226
227 private static List<ExecutableElement> getStaticMethods(CodeTypeElement clazz) {
228 List<ExecutableElement> staticMethods = new ArrayList<>();
229 for (ExecutableElement method : clazz.getMethods()) {
230 if (method.getModifiers().contains(Modifier.STATIC)) {
231 staticMethods.add(method);
232 }
233 }
234 return staticMethods;
235 }
236
237 private static List<ExecutableElement> getInstanceMethods(CodeTypeElement clazz) {
238 List<ExecutableElement> instanceMethods = new ArrayList<>();
239 for (ExecutableElement method : clazz.getMethods()) {
240 if (!method.getModifiers().contains(Modifier.STATIC)) {
241 instanceMethods.add(method);
242 }
243 }
244 return instanceMethods;
245 }
246
247 @Override
248 public Void visitVariable(VariableElement f, Void p) {
249 Element parent = f.getEnclosingElement();
250
251 for (AnnotationMirror annotation : f.getAnnotationMirrors()) {
252 visitAnnotation(f, annotation);
253 write(" ");
254 }
255
256 CodeTree init = null;
257 if (f instanceof CodeVariableElement) {
258 init = ((CodeVariableElement) f).getInit();
259 }
260
261 if (parent.getKind() == ElementKind.ENUM && f.getModifiers().contains(Modifier.STATIC)) {
262 write(f.getSimpleName());
263 if (init != null) {
264 write("(");
265 init.acceptCodeElementScanner(this, p);
266 write(")");
267 }
268 } else {
269 Element enclosing = f.getEnclosingElement();
270 writeModifiers(f.getModifiers());
271
272 boolean varArgs = false;
273 if (enclosing.getKind() == ElementKind.METHOD) {
274 ExecutableElement method = (ExecutableElement) enclosing;
275 if (method.isVarArgs() && method.getParameters().indexOf(f) == method.getParameters().size() - 1) {
276 varArgs = true;
277 }
278 }
279
280 TypeMirror varType = f.asType();
281 if (varArgs) {
282 if (varType.getKind() == TypeKind.ARRAY) {
283 varType = ((ArrayType) varType).getComponentType();
284 }
285 write(useImport(f, varType));
286 write("...");
287 } else {
288 write(useImport(f, varType));
289 }
290
291 write(" ");
292 write(f.getSimpleName());
293 if (init != null) {
294 write(" = ");
295 init.acceptCodeElementScanner(this, p);
296 }
297 }
298 return null;
299 }
300
301 private void visitAnnotation(Element enclosedElement, AnnotationMirror e) {
302 write("@").write(useImport(enclosedElement, e.getAnnotationType()));
303
304 if (!e.getElementValues().isEmpty()) {
305 write("(");
306 final ExecutableElement defaultElement = findExecutableElement(e.getAnnotationType(), "value");
307
308 Map<? extends ExecutableElement, ? extends AnnotationValue> values = e.getElementValues();
309 if (defaultElement != null && values.size() == 1 && values.get(defaultElement) != null) {
310 visitAnnotationValue(enclosedElement, values.get(defaultElement));
311 } else {
312 Set<? extends ExecutableElement> methodsSet = values.keySet();
313 List<ExecutableElement> methodsList = new ArrayList<>();
314 for (ExecutableElement method : methodsSet) {
315 if (values.get(method) == null) {
316 continue;
317 }
318 methodsList.add(method);
319 }
320
321 Collections.sort(methodsList, new Comparator<ExecutableElement>() {
322
323 @Override
324 public int compare(ExecutableElement o1, ExecutableElement o2) {
325 return o1.getSimpleName().toString().compareTo(o2.getSimpleName().toString());
326 }
327 });
328
329 for (int i = 0; i < methodsList.size(); i++) {
330 ExecutableElement method = methodsList.get(i);
331 AnnotationValue value = values.get(method);
332 write(method.getSimpleName().toString());
333 write(" = ");
334 visitAnnotationValue(enclosedElement, value);
335
336 if (i < methodsList.size() - 1) {
337 write(", ");
338 }
339 }
340 }
341
342 write(")");
343 }
344 }
345
346 private void visitAnnotationValue(Element enclosedElement, AnnotationValue e) {
347 e.accept(new AnnotationValueWriterVisitor(enclosedElement), null);
348 }
349
350 private class AnnotationValueWriterVisitor extends AbstractAnnotationValueVisitor7<Void, Void> {
351
352 private final Element enclosedElement;
353
354 public AnnotationValueWriterVisitor(Element enclosedElement) {
355 this.enclosedElement = enclosedElement;
356 }
357
358 @Override
359 public Void visitBoolean(boolean b, Void p) {
360 write(Boolean.toString(b));
361 return null;
362 }
363
364 @Override
365 public Void visitByte(byte b, Void p) {
366 write(Byte.toString(b));
367 return null;
368 }
369
370 @Override
371 public Void visitChar(char c, Void p) {
372 write(Character.toString(c));
373 return null;
374 }
375
376 @Override
377 public Void visitDouble(double d, Void p) {
378 write(Double.toString(d));
379 return null;
380 }
381
382 @Override
383 public Void visitFloat(float f, Void p) {
384 write(Float.toString(f));
385 return null;
386 }
387
388 @Override
389 public Void visitInt(int i, Void p) {
390 write(Integer.toString(i));
391 return null;
392 }
393
394 @Override
395 public Void visitLong(long i, Void p) {
396 write(Long.toString(i));
397 return null;
398 }
399
400 @Override
401 public Void visitShort(short s, Void p) {
402 write(Short.toString(s));
403 return null;
404 }
405
406 @Override
407 public Void visitString(String s, Void p) {
408 write("\"");
409 write(s);
410 write("\"");
411 return null;
412 }
413
414 @Override
415 public Void visitType(TypeMirror t, Void p) {
416 write(useImport(enclosedElement, t));
417 write(".class");
418 return null;
419 }
420
421 @Override
422 public Void visitEnumConstant(VariableElement c, Void p) {
423 write(useImport(enclosedElement, c.asType()));
424 write(".");
425 write(c.getSimpleName().toString());
426 return null;
427 }
428
429 @Override
430 public Void visitAnnotation(AnnotationMirror a, Void p) {
431 AbstractCodeWriter.this.visitAnnotation(enclosedElement, a);
432 return null;
433 }
434
435 @Override
436 public Void visitArray(List<? extends AnnotationValue> vals, Void p) {
437 write("{");
438 for (int i = 0; i < vals.size(); i++) {
439 AnnotationValue value = vals.get(i);
440 AbstractCodeWriter.this.visitAnnotationValue(enclosedElement, value);
441 if (i < vals.size() - 1) {
442 write(", ");
443 }
444 }
445 write("}");
446 return null;
447 }
448 }
449
450 private static ExecutableElement findExecutableElement(DeclaredType type, String name) {
451 List<? extends ExecutableElement> elements = ElementFilter.methodsIn(type.asElement().getEnclosedElements());
452 for (ExecutableElement executableElement : elements) {
453 if (executableElement.getSimpleName().toString().equals(name)) {
454 return executableElement;
455 }
456 }
457 return null;
458 }
459
460 @Override
461 public void visitImport(CodeImport e, Void p) {
462 if (e.isStaticImport()) {
463 write("import static ").write(e.getImportString()).write(";");
464 } else {
465 write("import ").write(e.getImportString()).write(";");
466 }
467 }
468
469 @Override
470 public Void visitExecutable(CodeExecutableElement e, Void p) {
471 for (AnnotationMirror annotation : e.getAnnotationMirrors()) {
472 visitAnnotation(e, annotation);
473 writeLn();
474 }
475
476 writeModifiers(e.getModifiers());
477
478 if (e.getReturnType() != null) {
479 write(useImport(e, e.getReturnType()));
480 write(" ");
481 }
482 write(e.getSimpleName());
483 write("(");
484
485 for (int i = 0; i < e.getParameters().size(); i++) {
486 VariableElement param = e.getParameters().get(i);
487 param.accept(this, p);
488 if (i < e.getParameters().size() - 1) {
489 write(", ");
490 }
491 }
492 write(")");
493
494 List<TypeMirror> throwables = e.getThrownTypes();
495 if (throwables.size() > 0) {
496 write(" throws ");
497 for (int i = 0; i < throwables.size(); i++) {
498 write(useImport(e, throwables.get(i)));
499 if (i < throwables.size() - 1) {
500 write(", ");
501 }
502 }
503 }
504
505 if (e.getModifiers().contains(Modifier.ABSTRACT)) {
506 writeLn(";");
507 } else if (e.getBodyTree() != null) {
508 writeLn(" {");
509 indent(1);
510 e.getBodyTree().acceptCodeElementScanner(this, p);
511 dedent(1);
512 writeLn("}");
513 } else if (e.getBody() != null) {
514 write(" {");
515 write(e.getBody());
516 writeLn("}");
517 } else {
518 writeLn("{ }");
519 }
520 writeEmptyLn();
521 return null;
522 }
523
524 @Override
525 public void visitTree(CodeTree e, Void p) {
526 CodeTreeKind kind = e.getCodeKind();
527
528 switch (kind) {
529 case COMMA_GROUP:
530 List<CodeTree> children = e.getEnclosedElements();
531 for (int i = 0; i < children.size(); i++) {
532 children.get(i).acceptCodeElementScanner(this, p);
533 if (i < e.getEnclosedElements().size() - 1) {
534 write(", ");
535 }
536 }
537 break;
538 case GROUP:
539 for (CodeTree tree : e.getEnclosedElements()) {
540 tree.acceptCodeElementScanner(this, p);
541 }
542 break;
543 case INDENT:
544 indent(1);
545 for (CodeTree tree : e.getEnclosedElements()) {
546 tree.acceptCodeElementScanner(this, p);
547 }
548 dedent(1);
549 break;
550 case NEW_LINE:
551 writeLn();
552 break;
553 case STRING:
554 if (e.getString() != null) {
555 write(e.getString());
556 } else {
557 write("null");
558 }
559 break;
560 case STATIC_FIELD_REFERENCE:
561 if (e.getString() != null) {
562 write(imports.createStaticFieldReference(e, e.getType(), e.getString()));
563 } else {
564 write("null");
565 }
566 break;
567 case STATIC_METHOD_REFERENCE:
568 if (e.getString() != null) {
569 write(imports.createStaticMethodReference(e, e.getType(), e.getString()));
570 } else {
571 write("null");
572 }
573 break;
574 case TYPE:
575 write(useImport(e, e.getType()));
576 break;
577 default:
578 assert false;
579 return;
580 }
581 }
582
583 protected void writeHeader() {
584 // default implementation does nothing
585 }
586
587 private void writeModifiers(Set<Modifier> modifiers) {
588 if (modifiers != null) {
589 for (Modifier modifier : modifiers) {
590 write(modifier.toString());
591 write(" ");
592 }
593 }
594 }
595
596 private void indent(int count) {
597 indent += count;
598 }
599
600 private void dedent(int count) {
601 indent -= count;
602 }
603
604 private void writeLn() {
605 writeLn("");
606 }
607
608 protected void writeLn(String text) {
609 write(text);
610 write(LN);
611 lineLength = 0;
612 newLine = true;
613 if (lineWrapping) {
614 dedent(LINE_WRAP_INDENTS);
615 lineWrapping = false;
616 }
617 lineWrapping = false;
618 }
619
620 private void writeEmptyLn() {
621 writeLn();
622 }
623
624 private AbstractCodeWriter write(Name name) {
625 return write(name.toString());
626 }
627
628 private AbstractCodeWriter write(String m) {
629 if (m.isEmpty()) {
630 return this;
631 }
632 try {
633 String s = m;
634 lineLength += s.length();
635 if (newLine && s != LN) {
636 writeIndent();
637 newLine = false;
638 }
639 if (lineLength > MAX_LINE_LENGTH) {
640 s = wrapLine(s);
641 }
642 writer.write(s);
643 } catch (IOException e) {
644 throw new RuntimeException(e);
645 }
646 return this;
647 }
648
649 private String wrapLine(String m) throws IOException {
650 assert !m.isEmpty();
651
652 char firstCharacter = m.charAt(0);
653 char lastCharacter = m.charAt(m.length() - 1);
654 if (firstCharacter == '\"' && lastCharacter == '\"') {
655 // string line wrapping
656 String string = m.substring(1, m.length() - 1);
657 if (string.isEmpty()) {
658 return m;
659 }
660
661 // restore original line length
662 lineLength = lineLength - m.length();
663 int size = 0;
664 for (int i = 0; i < string.length(); i += size) {
665 if (i != 0) {
666 write("+ ");
667 }
668
669 int nextSize = MAX_LINE_LENGTH - lineLength - 2;
670 if (nextSize <= 0) {
671 writeLn();
672 nextSize = MAX_LINE_LENGTH - lineLength - 2;
673 }
674
675 int end = Math.min(i + nextSize, string.length());
676
677 // TODO(CH): fails in normal usage - output ok though
678 // assert lineLength + (end - i) + 2 < MAX_LINE_LENGTH;
679
680 write("\"" + string.substring(i, end) + "\"");
681 size = nextSize;
682 }
683
684 return "";
685 } else if (!Character.isAlphabetic(firstCharacter) && firstCharacter != '+') {
686 return m;
687 }
688
689 if (!lineWrapping) {
690 indent(LINE_WRAP_INDENTS);
691 }
692 lineWrapping = true;
693 lineLength = 0;
694 write(LN);
695 writeIndent();
696 return m;
697 }
698
699 private void writeIndent() throws IOException {
700 lineLength += indentSize();
701 for (int i = 0; i < indent; i++) {
702 writer.write(IDENT_STRING);
703 }
704 }
705
706 private int indentSize() {
707 return IDENT_STRING.length() * indent;
708 }
709
710 private static class TrimTrailingSpaceWriter extends Writer {
711
712 private final Writer delegate;
713 private final StringBuilder buffer = new StringBuilder();
714
715 public TrimTrailingSpaceWriter(Writer delegate) {
716 this.delegate = delegate;
717 }
718
719 @Override
720 public void close() throws IOException {
721 this.delegate.close();
722 }
723
724 @Override
725 public void flush() throws IOException {
726 this.delegate.flush();
727 }
728
729 @Override
730 public void write(char[] cbuf, int off, int len) throws IOException {
731 buffer.append(cbuf, off, len);
732 int newLinePoint = buffer.indexOf(LN);
733
734 if (newLinePoint != -1) {
735 String lhs = trimTrailing(buffer.substring(0, newLinePoint));
736 delegate.write(lhs);
737 delegate.write(LN);
738 buffer.delete(0, newLinePoint + 1);
739 }
740 }
741
742 private static String trimTrailing(String s) {
743 int cut = 0;
744 for (int i = s.length() - 1; i >= 0; i--) {
745 if (Character.isWhitespace(s.charAt(i))) {
746 cut++;
747 } else {
748 break;
749 }
750 }
751 if (cut > 0) {
752 return s.substring(0, s.length() - cut);
753 }
754 return s;
755 }
756 }
757
758 }