• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.google.clearsilver.jsilver.compiler;
18 
19 import java.io.Closeable;
20 import java.io.Flushable;
21 import java.io.PrintWriter;
22 import java.io.Writer;
23 import java.lang.reflect.Method;
24 import 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  */
46 public class JavaSourceWriter implements Closeable, Flushable {
47 
48   private final PrintWriter out;
49   private int indent;
50 
JavaSourceWriter(Writer out)51   public JavaSourceWriter(Writer out) {
52     this.out = new PrintWriter(out);
53   }
54 
writePackage(String packageName)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 
writeImports(Class... javaClasses)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 
writeComment(String comment)76   public void writeComment(String comment) {
77     // TODO: Handle line breaks in comments.
78     startLine();
79     out.append("// ").append(comment);
80     endLine();
81   }
82 
startClass(String className, String baseClassName, String... interfaceNames)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 
startAnonymousClass(String baseClass, JavaExpression... constructorArgs)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 
endAnonymousClass()128   public void endAnonymousClass() {
129     endBlock();
130   }
131 
132   /**
133    * Start a method. The signature is based on that of an existing method.
134    */
startMethod(Method method, String... paramNames)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 
startIfBlock(JavaExpression expression)198   public void startIfBlock(JavaExpression expression) {
199     startLine();
200     out.append("if (");
201     writeExpression(expression);
202     out.append(") ");
203     startBlock();
204   }
205 
endIfStartElseBlock()206   public void endIfStartElseBlock() {
207     endBlock();
208     out.append(" else ");
209     startBlock();
210   }
211 
endIfBlock()212   public void endIfBlock() {
213     endBlock();
214     endLine();
215   }
216 
startScopedBlock()217   public void startScopedBlock() {
218     startLine();
219     startBlock();
220   }
221 
endScopedBlock()222   public void endScopedBlock() {
223     endBlock();
224     endLine();
225   }
226 
startIterableForLoop(String type, String name, JavaExpression expression)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 
startForLoop(JavaExpression start, JavaExpression end, JavaExpression increment)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 
endLoop()251   public void endLoop() {
252     endBlock();
253     endLine();
254   }
255 
writeStatement(JavaExpression expression)256   public void writeStatement(JavaExpression expression) {
257     startLine();
258     writeExpression(expression);
259     out.append(';');
260     endLine();
261   }
262 
writeExpression(JavaExpression expression)263   public void writeExpression(JavaExpression expression) {
264     expression.write(out);
265   }
266 
endMethod()267   public void endMethod() {
268     endBlock();
269     endLine();
270     emptyLine();
271   }
272 
endClass()273   public void endClass() {
274     endBlock();
275     endLine();
276     emptyLine();
277   }
278 
279   @Override
flush()280   public void flush() {
281     out.flush();
282   }
283 
284   @Override
close()285   public void close() {
286     out.close();
287   }
288 
startBlock()289   private void startBlock() {
290     out.append('{');
291     endLine();
292     indent++;
293   }
294 
endBlock()295   private void endBlock() {
296     indent--;
297     startLine();
298     out.append('}');
299   }
300 
startLine()301   private void startLine() {
302     for (int i = 0; i < indent; i++) {
303       out.append("  ");
304     }
305   }
306 
endLine()307   private void endLine() {
308     out.append('\n');
309   }
310 
emptyLine()311   private void emptyLine() {
312     out.append('\n');
313   }
314 
writeJavaSymbol(PrintWriter out, String symbol)315   public static void writeJavaSymbol(PrintWriter out, String symbol) {
316     out.append(symbol); // TODO Make safe and validate.
317   }
318 
startField(String type, JavaExpression name)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 
endField()328   public void endField() {
329     out.append(';');
330     endLine();
331     emptyLine();
332   }
333 
334 }
335