comparison truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java @ 21951:9c8c0937da41

Moving all sources into truffle subdirectory
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 17 Jun 2015 10:58:08 +0200
parents graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java@810d466073f0
children dc83cc1f94f2
comparison
equal deleted inserted replaced
21950:2a5011c7e641 21951:9c8c0937da41
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 = Integer.MAX_VALUE; // line wrapping disabled
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(), true);
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 != null && parent.getKind() == ElementKind.ENUM && f.getModifiers().contains(Modifier.STATIC)) {
262 write(f.getSimpleName());
263 if (init != null) {
264 write("(");
265 visitTree(init, p, f);
266 write(")");
267 }
268 } else {
269 writeModifiers(f.getModifiers(), true);
270
271 boolean varArgs = false;
272 if (parent != null && parent.getKind() == ElementKind.METHOD) {
273 ExecutableElement method = (ExecutableElement) parent;
274 if (method.isVarArgs() && method.getParameters().indexOf(f) == method.getParameters().size() - 1) {
275 varArgs = true;
276 }
277 }
278
279 TypeMirror varType = f.asType();
280 if (varArgs) {
281 if (varType.getKind() == TypeKind.ARRAY) {
282 varType = ((ArrayType) varType).getComponentType();
283 }
284 write(useImport(f, varType));
285 write("...");
286 } else {
287 write(useImport(f, varType));
288 }
289
290 write(" ");
291 write(f.getSimpleName());
292 if (init != null) {
293 write(" = ");
294 visitTree(init, p, f);
295 }
296 }
297 return null;
298 }
299
300 private void visitAnnotation(Element enclosedElement, AnnotationMirror e) {
301 write("@").write(useImport(enclosedElement, e.getAnnotationType()));
302
303 if (!e.getElementValues().isEmpty()) {
304 write("(");
305 final ExecutableElement defaultElement = findExecutableElement(e.getAnnotationType(), "value");
306
307 Map<? extends ExecutableElement, ? extends AnnotationValue> values = e.getElementValues();
308 if (defaultElement != null && values.size() == 1 && values.get(defaultElement) != null) {
309 visitAnnotationValue(enclosedElement, values.get(defaultElement));
310 } else {
311 Set<? extends ExecutableElement> methodsSet = values.keySet();
312 List<ExecutableElement> methodsList = new ArrayList<>();
313 for (ExecutableElement method : methodsSet) {
314 if (values.get(method) == null) {
315 continue;
316 }
317 methodsList.add(method);
318 }
319
320 Collections.sort(methodsList, new Comparator<ExecutableElement>() {
321
322 @Override
323 public int compare(ExecutableElement o1, ExecutableElement o2) {
324 return o1.getSimpleName().toString().compareTo(o2.getSimpleName().toString());
325 }
326 });
327
328 for (int i = 0; i < methodsList.size(); i++) {
329 ExecutableElement method = methodsList.get(i);
330 AnnotationValue value = values.get(method);
331 write(method.getSimpleName().toString());
332 write(" = ");
333 visitAnnotationValue(enclosedElement, value);
334
335 if (i < methodsList.size() - 1) {
336 write(", ");
337 }
338 }
339 }
340
341 write(")");
342 }
343 }
344
345 private void visitAnnotationValue(Element enclosedElement, AnnotationValue e) {
346 e.accept(new AnnotationValueWriterVisitor(enclosedElement), null);
347 }
348
349 private class AnnotationValueWriterVisitor extends AbstractAnnotationValueVisitor7<Void, Void> {
350
351 private final Element enclosedElement;
352
353 public AnnotationValueWriterVisitor(Element enclosedElement) {
354 this.enclosedElement = enclosedElement;
355 }
356
357 @Override
358 public Void visitBoolean(boolean b, Void p) {
359 write(Boolean.toString(b));
360 return null;
361 }
362
363 @Override
364 public Void visitByte(byte b, Void p) {
365 write(Byte.toString(b));
366 return null;
367 }
368
369 @Override
370 public Void visitChar(char c, Void p) {
371 write(Character.toString(c));
372 return null;
373 }
374
375 @Override
376 public Void visitDouble(double d, Void p) {
377 write(Double.toString(d));
378 return null;
379 }
380
381 @Override
382 public Void visitFloat(float f, Void p) {
383 write(Float.toString(f));
384 return null;
385 }
386
387 @Override
388 public Void visitInt(int i, Void p) {
389 write(Integer.toString(i));
390 return null;
391 }
392
393 @Override
394 public Void visitLong(long i, Void p) {
395 write(Long.toString(i));
396 return null;
397 }
398
399 @Override
400 public Void visitShort(short s, Void p) {
401 write(Short.toString(s));
402 return null;
403 }
404
405 @Override
406 public Void visitString(String s, Void p) {
407 write("\"");
408 write(s);
409 write("\"");
410 return null;
411 }
412
413 @Override
414 public Void visitType(TypeMirror t, Void p) {
415 write(useImport(enclosedElement, t));
416 write(".class");
417 return null;
418 }
419
420 @Override
421 public Void visitEnumConstant(VariableElement c, Void p) {
422 write(useImport(enclosedElement, c.asType()));
423 write(".");
424 write(c.getSimpleName().toString());
425 return null;
426 }
427
428 @Override
429 public Void visitAnnotation(AnnotationMirror a, Void p) {
430 AbstractCodeWriter.this.visitAnnotation(enclosedElement, a);
431 return null;
432 }
433
434 @Override
435 public Void visitArray(List<? extends AnnotationValue> vals, Void p) {
436 write("{");
437 for (int i = 0; i < vals.size(); i++) {
438 AnnotationValue value = vals.get(i);
439 AbstractCodeWriter.this.visitAnnotationValue(enclosedElement, value);
440 if (i < vals.size() - 1) {
441 write(", ");
442 }
443 }
444 write("}");
445 return null;
446 }
447 }
448
449 private static ExecutableElement findExecutableElement(DeclaredType type, String name) {
450 List<? extends ExecutableElement> elements = ElementFilter.methodsIn(type.asElement().getEnclosedElements());
451 for (ExecutableElement executableElement : elements) {
452 if (executableElement.getSimpleName().toString().equals(name)) {
453 return executableElement;
454 }
455 }
456 return null;
457 }
458
459 @Override
460 public void visitImport(CodeImport e, Void p) {
461 write("import ");
462 if (e.isStaticImport()) {
463 write("static ");
464 }
465 write(e.getPackageName());
466 write(".");
467 write(e.getSymbolName());
468 write(";");
469 }
470
471 @Override
472 public Void visitExecutable(CodeExecutableElement e, Void p) {
473 for (AnnotationMirror annotation : e.getAnnotationMirrors()) {
474 visitAnnotation(e, annotation);
475 writeLn();
476 }
477
478 writeModifiers(e.getModifiers(), !e.getEnclosingClass().getModifiers().contains(Modifier.FINAL));
479
480 if (e.getReturnType() != null) {
481 write(useImport(e, e.getReturnType()));
482 write(" ");
483 }
484 write(e.getSimpleName());
485 write("(");
486
487 for (int i = 0; i < e.getParameters().size(); i++) {
488 VariableElement param = e.getParameters().get(i);
489 param.accept(this, p);
490 if (i < e.getParameters().size() - 1) {
491 write(", ");
492 }
493 }
494 write(")");
495
496 List<TypeMirror> throwables = e.getThrownTypes();
497 if (throwables.size() > 0) {
498 write(" throws ");
499 for (int i = 0; i < throwables.size(); i++) {
500 write(useImport(e, throwables.get(i)));
501 if (i < throwables.size() - 1) {
502 write(", ");
503 }
504 }
505 }
506
507 if (e.getModifiers().contains(Modifier.ABSTRACT)) {
508 writeLn(";");
509 } else if (e.getBodyTree() != null) {
510 writeLn(" {");
511 indent(1);
512 visitTree(e.getBodyTree(), p, e);
513 dedent(1);
514 writeLn("}");
515 } else if (e.getBody() != null) {
516 write(" {");
517 write(e.getBody());
518 writeLn("}");
519 } else {
520 writeLn(" {");
521 writeLn("}");
522 }
523 writeEmptyLn();
524 return null;
525 }
526
527 @Override
528 public void visitTree(CodeTree e, Void p, Element enclosingElement) {
529 CodeTreeKind kind = e.getCodeKind();
530
531 switch (kind) {
532 case COMMA_GROUP:
533 List<CodeTree> children = e.getEnclosedElements();
534 if (children != null) {
535 for (int i = 0; i < children.size(); i++) {
536 visitTree(children.get(i), p, enclosingElement);
537 if (i < e.getEnclosedElements().size() - 1) {
538 write(", ");
539 }
540 }
541 }
542 break;
543 case GROUP:
544 super.visitTree(e, p, enclosingElement);
545 break;
546 case INDENT:
547 indent(1);
548 super.visitTree(e, p, enclosingElement);
549 dedent(1);
550 break;
551 case NEW_LINE:
552 writeLn();
553 break;
554 case STRING:
555 if (e.getString() != null) {
556 write(e.getString());
557 } else {
558 write("null");
559 }
560 break;
561 case STATIC_FIELD_REFERENCE:
562 if (e.getString() != null) {
563 write(imports.createStaticFieldReference(enclosingElement, e.getType(), e.getString()));
564 } else {
565 write("null");
566 }
567 break;
568 case STATIC_METHOD_REFERENCE:
569 if (e.getString() != null) {
570 write(imports.createStaticMethodReference(enclosingElement, e.getType(), e.getString()));
571 } else {
572 write("null");
573 }
574 break;
575 case TYPE:
576 write(useImport(enclosingElement, e.getType()));
577 break;
578 default:
579 assert false;
580 return;
581 }
582 }
583
584 protected void writeHeader() {
585 // default implementation does nothing
586 }
587
588 private void writeModifiers(Set<Modifier> modifiers, boolean includeFinal) {
589 if (modifiers != null && !modifiers.isEmpty()) {
590 Modifier[] modArray = modifiers.toArray(new Modifier[modifiers.size()]);
591 Arrays.sort(modArray);
592 for (Modifier mod : modArray) {
593 if (mod == Modifier.FINAL && !includeFinal) {
594 continue;
595 }
596 write(mod.toString());
597 write(" ");
598 }
599 }
600 }
601
602 private void indent(int count) {
603 indent += count;
604 }
605
606 private void dedent(int count) {
607 indent -= count;
608 }
609
610 private void writeLn() {
611 writeLn("");
612 }
613
614 protected void writeLn(String text) {
615 write(text);
616 write(LN);
617 lineLength = 0;
618 newLine = true;
619 if (lineWrapping) {
620 dedent(LINE_WRAP_INDENTS);
621 lineWrapping = false;
622 }
623 lineWrapping = false;
624 }
625
626 private void writeEmptyLn() {
627 writeLn();
628 }
629
630 private AbstractCodeWriter write(Name name) {
631 return write(name.toString());
632 }
633
634 private AbstractCodeWriter write(String m) {
635 if (m.isEmpty()) {
636 return this;
637 }
638 try {
639 String s = m;
640 lineLength += s.length();
641 if (newLine && s != LN) {
642 writeIndent();
643 newLine = false;
644 }
645 if (lineLength > MAX_LINE_LENGTH) {
646 s = wrapLine(s);
647 }
648 writer.write(s);
649 } catch (IOException e) {
650 throw new RuntimeException(e);
651 }
652 return this;
653 }
654
655 private String wrapLine(String m) throws IOException {
656 assert !m.isEmpty();
657
658 char firstCharacter = m.charAt(0);
659 char lastCharacter = m.charAt(m.length() - 1);
660 if (firstCharacter == '\"' && lastCharacter == '\"') {
661 // string line wrapping
662 String string = m.substring(1, m.length() - 1);
663 if (string.isEmpty()) {
664 return m;
665 }
666
667 // restore original line length
668 lineLength = lineLength - m.length();
669 int size = 0;
670 for (int i = 0; i < string.length(); i += size) {
671 if (i != 0) {
672 write("+ ");
673 }
674
675 int nextSize = MAX_LINE_LENGTH - lineLength - 2;
676 if (nextSize <= 0) {
677 writeLn();
678 nextSize = MAX_LINE_LENGTH - lineLength - 2;
679 }
680
681 int end = Math.min(i + nextSize, string.length());
682
683 // TODO(CH): fails in normal usage - output ok though
684 // assert lineLength + (end - i) + 2 < MAX_LINE_LENGTH;
685 write("\"");
686 write(string.substring(i, end));
687 write("\"");
688 size = nextSize;
689 }
690
691 return "";
692 } else if (!Character.isAlphabetic(firstCharacter) && firstCharacter != '+') {
693 return m;
694 }
695
696 if (!lineWrapping) {
697 indent(LINE_WRAP_INDENTS);
698 }
699 lineWrapping = true;
700 lineLength = 0;
701 write(LN);
702 writeIndent();
703 return m;
704 }
705
706 private void writeIndent() throws IOException {
707 lineLength += indentSize();
708 for (int i = 0; i < indent; i++) {
709 writer.write(IDENT_STRING);
710 }
711 }
712
713 private int indentSize() {
714 return IDENT_STRING.length() * indent;
715 }
716
717 private static class TrimTrailingSpaceWriter extends Writer {
718
719 private final Writer delegate;
720 private final StringBuilder buffer = new StringBuilder();
721
722 public TrimTrailingSpaceWriter(Writer delegate) {
723 this.delegate = delegate;
724 }
725
726 @Override
727 public void close() throws IOException {
728 this.delegate.close();
729 }
730
731 @Override
732 public void flush() throws IOException {
733 this.delegate.flush();
734 }
735
736 @Override
737 public void write(char[] cbuf, int off, int len) throws IOException {
738 buffer.append(cbuf, off, len);
739 int newLinePoint = buffer.indexOf(LN);
740
741 if (newLinePoint != -1) {
742 String lhs = trimTrailing(buffer.substring(0, newLinePoint));
743 delegate.write(lhs);
744 delegate.write(LN);
745 buffer.delete(0, newLinePoint + 1);
746 }
747 }
748
749 private static String trimTrailing(String s) {
750 int cut = 0;
751 for (int i = s.length() - 1; i >= 0; i--) {
752 if (Character.isWhitespace(s.charAt(i))) {
753 cut++;
754 } else {
755 break;
756 }
757 }
758 if (cut > 0) {
759 return s.substring(0, s.length() - cut);
760 }
761 return s;
762 }
763 }
764
765 }