JavaSourceWriter.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.clearsilver.jsilver.compiler;
18
19import java.io.Closeable;
20import java.io.Flushable;
21import java.io.PrintWriter;
22import java.io.Writer;
23import java.lang.reflect.Method;
24import java.lang.reflect.Modifier;
25
26/**
27 * Simple API for generating Java source code. Easier than lots of string manipulation.
28 *
29 * <h3>Example</h3>
30 *
31 * <pre>
32 * java = new JavaSourceWriter(out);
33 *
34 * java.writeComment("// Auto generated file");
35 * java.writePackage("com.something.mypackage");
36 * java.writeImports(SomeClassToImport.class, Another.class);
37 *
38 * java.startClass("SomeClass", "InterfaceA");
39 * java.startMethod(Object.class.getMethod("toString"));
40 * java.writeStatement(call("System.out.println", string("hello")));
41 * java.endClass();
42 * </pre>
43 *
44 * Note: For writing statements/expressions, staticly import the methods on {@link JavaExpression}.
45 */
46public class JavaSourceWriter implements Closeable, Flushable {
47
48  private final PrintWriter out;
49  private int indent;
50
51  public JavaSourceWriter(Writer out) {
52    this.out = new PrintWriter(out);
53  }
54
55  public void writePackage(String packageName) {
56    // TODO: Verify packageName is valid.
57    if (packageName != null) {
58      startLine();
59      out.append("package ").append(packageName).append(';');
60      endLine();
61      emptyLine();
62    }
63  }
64
65  public void writeImports(Class... javaClasses) {
66    for (Class javaClass : javaClasses) {
67      startLine();
68      out.append("import ").append(javaClass.getName()).append(';');
69      endLine();
70    }
71    if (javaClasses.length > 0) {
72      emptyLine();
73    }
74  }
75
76  public void writeComment(String comment) {
77    // TODO: Handle line breaks in comments.
78    startLine();
79    out.append("// ").append(comment);
80    endLine();
81  }
82
83  public void startClass(String className, String baseClassName, String... interfaceNames) {
84    startLine();
85    out.append("public class ");
86    writeJavaSymbol(out, className);
87
88    if (baseClassName != null) {
89      out.append(" extends ");
90      writeJavaSymbol(out, baseClassName);
91    }
92
93    boolean seenAnyInterfaces = false;
94    for (String interfaceName : interfaceNames) {
95      if (!seenAnyInterfaces) {
96        seenAnyInterfaces = true;
97        out.append(" implements ");
98      } else {
99        out.append(", ");
100      }
101      writeJavaSymbol(out, interfaceName);
102    }
103
104    out.append(' ');
105    startBlock();
106    emptyLine();
107  }
108
109  public void startAnonymousClass(String baseClass, JavaExpression... constructorArgs) {
110    out.append("new ");
111    writeJavaSymbol(out, baseClass);
112    out.append('(');
113
114    boolean seenAnyArgs = false;
115    for (JavaExpression constructorArg : constructorArgs) {
116      if (seenAnyArgs) {
117        out.append(", ");
118      }
119      seenAnyArgs = true;
120      constructorArg.write(out);
121    }
122
123    out.append(") ");
124    startBlock();
125    emptyLine();
126  }
127
128  public void endAnonymousClass() {
129    endBlock();
130  }
131
132  /**
133   * Start a method. The signature is based on that of an existing method.
134   */
135  public void startMethod(Method method, String... paramNames) {
136    // This currently does not support generics, varargs or arrays.
137    // If you need it - add the support. Don't want to overcomplicate it
138    // until necessary.
139
140    if (paramNames.length != method.getParameterTypes().length) {
141      throw new IllegalArgumentException("Did not specifiy correct "
142          + "number of parameter names for method signature " + method);
143    }
144
145    startLine();
146
147    // @Override abstract methods.
148    int modifiers = method.getModifiers();
149    if (Modifier.isAbstract(modifiers)) {
150      out.append("@Override");
151      endLine();
152      startLine();
153    }
154
155    // Modifiers: (public, protected, static)
156    if (modifiers != 0) {
157      // Modifiers we care about. Ditch the rest. Specifically NOT ABSTRACT.
158      modifiers &= Modifier.PUBLIC | Modifier.PROTECTED | Modifier.STATIC;
159      out.append(Modifier.toString(modifiers)).append(' ');
160    }
161
162    // Return type and name: (e.g. "void doStuff(")
163    out.append(method.getReturnType().getSimpleName()).append(' ').append(method.getName()).append(
164        '(');
165
166    // Parameters.
167    int paramIndex = 0;
168    for (Class<?> paramType : method.getParameterTypes()) {
169      if (paramIndex > 0) {
170        out.append(", ");
171      }
172      writeJavaSymbol(out, paramType.getSimpleName());
173      out.append(' ');
174      writeJavaSymbol(out, paramNames[paramIndex]);
175      paramIndex++;
176    }
177
178    out.append(')');
179
180    // Exceptions thrown.
181    boolean seenAnyExceptions = false;
182    for (Class exception : method.getExceptionTypes()) {
183      if (!seenAnyExceptions) {
184        seenAnyExceptions = true;
185        endLine();
186        startLine();
187        out.append("    throws ");
188      } else {
189        out.append(", ");
190      }
191      writeJavaSymbol(out, exception.getSimpleName());
192    }
193
194    out.append(' ');
195    startBlock();
196  }
197
198  public void startIfBlock(JavaExpression expression) {
199    startLine();
200    out.append("if (");
201    writeExpression(expression);
202    out.append(") ");
203    startBlock();
204  }
205
206  public void endIfStartElseBlock() {
207    endBlock();
208    out.append(" else ");
209    startBlock();
210  }
211
212  public void endIfBlock() {
213    endBlock();
214    endLine();
215  }
216
217  public void startScopedBlock() {
218    startLine();
219    startBlock();
220  }
221
222  public void endScopedBlock() {
223    endBlock();
224    endLine();
225  }
226
227  public void startIterableForLoop(String type, String name, JavaExpression expression) {
228    startLine();
229    out.append("for (");
230    writeJavaSymbol(out, type);
231    out.append(' ');
232    writeJavaSymbol(out, name);
233    out.append(" : ");
234    writeExpression(expression);
235    out.append(") ");
236    startBlock();
237  }
238
239  public void startForLoop(JavaExpression start, JavaExpression end, JavaExpression increment) {
240    startLine();
241    out.append("for (");
242    writeExpression(start);
243    out.append("; ");
244    writeExpression(end);
245    out.append("; ");
246    writeExpression(increment);
247    out.append(") ");
248    startBlock();
249  }
250
251  public void endLoop() {
252    endBlock();
253    endLine();
254  }
255
256  public void writeStatement(JavaExpression expression) {
257    startLine();
258    writeExpression(expression);
259    out.append(';');
260    endLine();
261  }
262
263  public void writeExpression(JavaExpression expression) {
264    expression.write(out);
265  }
266
267  public void endMethod() {
268    endBlock();
269    endLine();
270    emptyLine();
271  }
272
273  public void endClass() {
274    endBlock();
275    endLine();
276    emptyLine();
277  }
278
279  @Override
280  public void flush() {
281    out.flush();
282  }
283
284  @Override
285  public void close() {
286    out.close();
287  }
288
289  private void startBlock() {
290    out.append('{');
291    endLine();
292    indent++;
293  }
294
295  private void endBlock() {
296    indent--;
297    startLine();
298    out.append('}');
299  }
300
301  private void startLine() {
302    for (int i = 0; i < indent; i++) {
303      out.append("  ");
304    }
305  }
306
307  private void endLine() {
308    out.append('\n');
309  }
310
311  private void emptyLine() {
312    out.append('\n');
313  }
314
315  public static void writeJavaSymbol(PrintWriter out, String symbol) {
316    out.append(symbol); // TODO Make safe and validate.
317  }
318
319  public void startField(String type, JavaExpression name) {
320    startLine();
321    out.append("private final ");
322    writeJavaSymbol(out, type);
323    out.append(' ');
324    name.write(out);
325    out.append(" = ");
326  }
327
328  public void endField() {
329    out.append(';');
330    endLine();
331    emptyLine();
332  }
333
334}
335