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