Home | History | Annotate | Download | only in preproc
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package sample.preproc;
     17 
     18 import java.io.IOException;
     19 import java.io.BufferedReader;
     20 import java.io.FileReader;
     21 import java.io.BufferedWriter;
     22 import java.io.FileWriter;
     23 import java.util.Vector;
     24 import javassist.CannotCompileException;
     25 import javassist.CtClass;
     26 import javassist.ClassPool;
     27 
     28 /**
     29  * This is a preprocessor for Java source programs using annotated
     30  * import declarations.
     31  *
     32  * <ul><pre>
     33  * import <i>class-name</i> by <i>assistant-name</i> [(<i>arg1, arg2, ...</i>)]
     34  * </pre></ul>
     35  *
     36  * <p>To process this annotation, run this class as follows:
     37  *
     38  * <ul><pre>
     39  * java sample.preproc.Compiler sample.j
     40  * </pre></ul>
     41  *
     42  * <p>This command produces <code>sample.java</code>, which only includes
     43  * regular import declarations.  Also, the Javassist program
     44  * specified by <i>assistant-name</i> is executed so that it produces
     45  * class files under the <code>./tmpjvst</code> directory.  The class
     46  * specified by <i>assistant-name</i> must implement
     47  * <code>sample.preproc.Assistant</code>.
     48  *
     49  * @see sample.preproc.Assistant
     50  */
     51 
     52 public class Compiler {
     53     protected BufferedReader input;
     54     protected BufferedWriter output;
     55     protected ClassPool classPool;
     56 
     57     /**
     58      * Constructs a <code>Compiler</code> with a source file.
     59      *
     60      * @param inputname         the name of the source file.
     61      */
     62     public Compiler(String inputname) throws CannotCompileException {
     63         try {
     64             input = new BufferedReader(new FileReader(inputname));
     65         }
     66         catch (IOException e) {
     67             throw new CannotCompileException("cannot open: " + inputname);
     68         }
     69 
     70         String outputname = getOutputFilename(inputname);
     71         if (outputname.equals(inputname))
     72             throw new CannotCompileException("invalid source name: "
     73                                              + inputname);
     74 
     75         try {
     76             output = new BufferedWriter(new FileWriter(outputname));
     77         }
     78         catch (IOException e) {
     79             throw new CannotCompileException("cannot open: " + outputname);
     80         }
     81 
     82         classPool = ClassPool.getDefault();
     83     }
     84 
     85     /**
     86      * Starts preprocessing.
     87      */
     88     public void process() throws IOException, CannotCompileException {
     89         int c;
     90         CommentSkipper reader = new CommentSkipper(input, output);
     91         while ((c = reader.read()) != -1) {
     92             output.write(c);
     93             if (c == 'p') {
     94                 if (skipPackage(reader))
     95                     break;
     96             }
     97             else if (c == 'i')
     98                 readImport(reader);
     99             else if (c != ' ' && c != '\t' && c != '\n' && c != '\r')
    100                 break;
    101         }
    102 
    103         while ((c = input.read()) != -1)
    104             output.write(c);
    105 
    106         input.close();
    107         output.close();
    108     }
    109 
    110     private boolean skipPackage(CommentSkipper reader) throws IOException {
    111         int c;
    112         c = reader.read();
    113         output.write(c);
    114         if (c != 'a')
    115             return true;
    116 
    117         while ((c = reader.read()) != -1) {
    118             output.write(c);
    119             if (c == ';')
    120                 break;
    121         }
    122 
    123         return false;
    124     }
    125 
    126     private void readImport(CommentSkipper reader)
    127                                 throws IOException, CannotCompileException
    128     {
    129         int word[] = new int[5];
    130         int c;
    131         for (int i = 0; i < 5; ++i) {
    132             word[i] = reader.read();
    133             output.write(word[i]);
    134         }
    135 
    136         if (word[0] != 'm' || word[1] != 'p' || word[2] != 'o'
    137             || word[3] != 'r' || word[4] != 't')
    138             return;     // syntax error?
    139 
    140         c = skipSpaces(reader, ' ');
    141         StringBuffer classbuf = new StringBuffer();
    142         while (c != ' ' && c != '\t' && c != '\n' && c != '\r'
    143                && c != ';' && c != -1) {
    144             classbuf.append((char)c);
    145             c = reader.read();
    146         }
    147 
    148         String importclass = classbuf.toString();
    149         c = skipSpaces(reader, c);
    150         if (c == ';') {
    151             output.write(importclass);
    152             output.write(';');
    153             return;
    154         }
    155         if (c != 'b')
    156             syntaxError(importclass);
    157 
    158         reader.read();  // skip 'y'
    159 
    160         StringBuffer assistant = new StringBuffer();
    161         Vector args = new Vector();
    162         c = readAssistant(reader, importclass, assistant, args);
    163         c = skipSpaces(reader, c);
    164         if (c != ';')
    165             syntaxError(importclass);
    166 
    167         runAssistant(importclass, assistant.toString(), args);
    168     }
    169 
    170     void syntaxError(String importclass) throws CannotCompileException {
    171         throw new CannotCompileException("Syntax error.  Cannot import "
    172                                          + importclass);
    173     }
    174 
    175     int readAssistant(CommentSkipper reader, String importclass,
    176                       StringBuffer assistant, Vector args)
    177         throws IOException, CannotCompileException
    178     {
    179         int c = readArgument(reader, assistant);
    180         c = skipSpaces(reader, c);
    181         if (c == '(') {
    182             do {
    183                 StringBuffer arg = new StringBuffer();
    184                 c = readArgument(reader, arg);
    185                 args.addElement(arg.toString());
    186                 c = skipSpaces(reader, c);
    187             } while (c == ',');
    188 
    189             if (c != ')')
    190                 syntaxError(importclass);
    191 
    192             return reader.read();
    193         }
    194 
    195         return c;
    196     }
    197 
    198     int readArgument(CommentSkipper reader, StringBuffer buf)
    199         throws IOException
    200     {
    201         int c = skipSpaces(reader, ' ');
    202         while ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
    203                || '0' <= c && c <= '9' || c == '.' || c == '_') {
    204             buf.append((char)c);
    205             c = reader.read();
    206         }
    207 
    208         return c;
    209     }
    210 
    211     int skipSpaces(CommentSkipper reader, int c) throws IOException {
    212         while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
    213             if (c == '\n' || c == '\r')
    214                 output.write(c);
    215 
    216             c = reader.read();
    217         }
    218 
    219         return c;
    220     }
    221 
    222     /**
    223      * Is invoked if this compiler encoutenrs:
    224      *
    225      * <ul><pre>
    226      * import <i>class name</i> by <i>assistant</i> (<i>args1</i>, <i>args2</i>, ...);
    227      * </pre></ul>
    228      *
    229      * @param   classname       class name
    230      * @param   assistantname   assistant
    231      * @param   argv            args1, args2, ...
    232      */
    233     private void runAssistant(String importname, String assistantname,
    234                               Vector argv)
    235         throws IOException, CannotCompileException
    236     {
    237         Class assistant;
    238         Assistant a;
    239         int s = argv.size();
    240         String[] args = new String[s];
    241         for (int i = 0; i < s; ++i)
    242             args[i] = (String)argv.elementAt(i);
    243 
    244         try {
    245             assistant = Class.forName(assistantname);
    246         }
    247         catch (ClassNotFoundException e) {
    248             throw new CannotCompileException("Cannot find " + assistantname);
    249         }
    250 
    251         try {
    252             a = (Assistant)assistant.newInstance();
    253         }
    254         catch (Exception e) {
    255             throw new CannotCompileException(e);
    256         }
    257 
    258         CtClass[] imports = a.assist(classPool, importname, args);
    259         s = imports.length;
    260         if (s < 1)
    261             output.write(" java.lang.Object;");
    262         else {
    263             output.write(' ');
    264             output.write(imports[0].getName());
    265             output.write(';');
    266             for (int i = 1; i < s; ++i) {
    267                 output.write(" import ");
    268                 output.write(imports[1].getName());
    269                 output.write(';');
    270             }
    271         }
    272     }
    273 
    274     private String getOutputFilename(String input) {
    275         int i = input.lastIndexOf('.');
    276         if (i < 0)
    277             i = input.length();
    278 
    279         return input.substring(0, i) + ".java";
    280     }
    281 
    282     public static void main(String[] args) {
    283         if (args.length > 0)
    284             try {
    285                 Compiler c = new Compiler(args[0]);
    286                 c.process();
    287             }
    288             catch (IOException e) {
    289                 System.err.println(e);
    290             }
    291             catch (CannotCompileException e) {
    292                 System.err.println(e);
    293             }
    294         else {
    295             System.err.println("Javassist version " + CtClass.version);
    296             System.err.println("No source file is specified.");
    297         }
    298     }
    299 }
    300 
    301 class CommentSkipper {
    302     private BufferedReader input;
    303     private BufferedWriter output;
    304 
    305     public CommentSkipper(BufferedReader reader, BufferedWriter writer) {
    306         input = reader;
    307         output = writer;
    308     }
    309 
    310     public int read() throws IOException {
    311         int c;
    312         while ((c = input.read()) != -1)
    313             if (c != '/')
    314                 return c;
    315             else {
    316                 c = input.read();
    317                 if (c == '/')
    318                     skipCxxComments();
    319                 else if (c == '*')
    320                     skipCComments();
    321                 else
    322                     output.write('/');
    323             }
    324 
    325         return c;
    326     }
    327 
    328     private void skipCxxComments() throws IOException {
    329         int c;
    330         output.write("//");
    331         while ((c = input.read()) != -1) {
    332             output.write(c);
    333             if (c == '\n' || c == '\r')
    334                 break;
    335         }
    336     }
    337 
    338     private void skipCComments() throws IOException {
    339         int c;
    340         boolean star = false;
    341         output.write("/*");
    342         while ((c = input.read()) != -1) {
    343             output.write(c);
    344             if (c == '*')
    345                 star = true;
    346             else if(star && c == '/')
    347                 break;
    348             else
    349                 star = false;
    350         }
    351     }
    352 }
    353