Home | History | Annotate | Download | only in compiler
      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 
     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