View Javadoc
1   /*
2    * Copyright 2006 Ralf Jaochim
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.castor.xmlctf.compiler;
15  
16  import java.io.File;
17  import java.lang.reflect.Method;
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.List;
21  
22  import org.castor.xmlctf.XMLTestCase;
23  import org.castor.xmlctf.util.FileServices;
24  
25  /**
26   * Compiles a directory tree, recursively. This class is built to use the Sun Javac compiler
27   * contained in tools.jar. A IllegalStateException will be thrown if tools.jar is not on the
28   * classpath at construction of the class and execution of the compileDirectory() method.
29   *
30   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
31   * @version $Revision: 5951 $ $Date: 2006-04-25 16:09:10 -0600 (Tue, 25 Apr 2006) $
32   * @since 1.0.5
33   */
34  public class SunJavaCompiler implements Compiler {
35  
36    private static final int COMPILATION_SUCCESS = 0;
37    private static final int COMPILATION_ERROR = 1;
38    private static final int COMPILATION_CMDERR = 2;
39    private static final int COMPILATION_SYSERR = 3;
40    private static final int COMPILATION_ABNORMAL = 4;
41  
42    private static final String COMPILE_CLASSNAME = "sun.tools.javac.Main";
43    private static final String COMPILE_METHODNAME = "compile";
44    private static final Class[] COMPILE_PARAMTYPE = new Class[] {String[].class};
45  
46    private static final HashSet IGNORE_DIRS = new HashSet();
47  
48    private static Method _compileMethod = null;
49    private static boolean _initialized = false;
50    /** Java version of the JVM we are running in. */
51    private static final float JAVA_VERSION =
52        Float.parseFloat(System.getProperty("java.specification.version"));
53  
54    private String _javaVersion = null;
55  
56    private final File _baseDirectory;
57    private final File _outputDirectory;
58  
59    /**
60     * Creates a compiler for a given directory.
61     * 
62     * @param baseDirectory The directory that holds the files to be compiled.
63     */
64    public SunJavaCompiler(final File baseDirectory) {
65      if (baseDirectory == null) {
66        throw new IllegalArgumentException("'baseDirectory' must not be null.");
67      }
68      _baseDirectory = baseDirectory;
69      _outputDirectory = baseDirectory;
70  
71      if (!_initialized) {
72        initialize();
73      }
74    }
75  
76    /**
77     * Sets the Java source version the current test will be using.
78     * 
79     * @param javaSourceVersion The Java Source version to be used.
80     */
81    public void setJavaSourceVersion(final float javaSourceVersion) {
82      float srcVersion = javaSourceVersion;
83      if (javaSourceVersion >= 5F && javaSourceVersion < 10F) {
84        srcVersion = 1.0F + (javaSourceVersion / 10F);
85      }
86      _javaVersion = "" + srcVersion;
87    }
88  
89    /**
90     * Initialize.
91     */
92    private void initialize() {
93      IGNORE_DIRS.add(FileServices.CVS);
94      IGNORE_DIRS.add(FileServices.SVN);
95  
96      try {
97        ClassLoader loader = this.getClass().getClassLoader();
98        Class cls = loader.loadClass(COMPILE_CLASSNAME);
99        _compileMethod = cls.getMethod(COMPILE_METHODNAME, COMPILE_PARAMTYPE);
100     } catch (Exception ex) {
101       IllegalStateException ise = new IllegalStateException("Failed to find compile method.");
102       ise.initCause(ex);
103       throw ise;
104     }
105 
106     _initialized = true;
107   }
108 
109   /**
110    * Compiles the content of a directory. Throws a <code>CompilationException</code> if the build
111    * fails.
112    */
113   public void compileDirectory() {
114     List filesList = findSourceFiles(_baseDirectory);
115     if (filesList.size() > 0) {
116       filesList.addAll(0, getCompileArguments(_baseDirectory, _outputDirectory));
117 
118       String[] args = new String[filesList.size()];
119       args = (String[]) filesList.toArray(args);
120 
121       int status;
122       try {
123         Object result = _compileMethod.invoke(null, new Object[] {args});
124         status = ((Integer) result).intValue();
125       } catch (Exception ex) {
126         throw new IllegalStateException("Failed to call compile method.");
127       }
128 
129       switch (status) {
130         case COMPILATION_SUCCESS:
131           break;
132         case COMPILATION_ERROR:
133           throw new CompilationException("Compile status: ERROR");
134         case COMPILATION_CMDERR:
135           throw new CompilationException("Compile status: CMDERR");
136         case COMPILATION_SYSERR:
137           throw new CompilationException("Compile status: SYSERR");
138         case COMPILATION_ABNORMAL:
139           throw new CompilationException("Compile status: ABNORMAL");
140         default:
141           throw new CompilationException("Compile status: Unknown");
142       }
143     } else {
144       throw new CompilationException("No files to compile: " + _baseDirectory);
145     }
146   }
147 
148   /**
149    * Returns a list of arguments for the compiler.
150    * 
151    * @param srcDir The source directory for compilation
152    * @param destDir The destination directory for compilation
153    * @return a list of arguments for the compiler.
154    */
155   private List getCompileArguments(final File srcDir, final File destDir) {
156     List args = new ArrayList();
157 
158     args.add("-g");
159     if (JAVA_VERSION == 1.5F) {
160       args.add("-Xlint:unchecked");
161     }
162     if (XMLTestCase._verbose) {
163       args.add("-verbose");
164     } else {
165       args.add("-nowarn");
166       args.add("-Xmaxwarns");
167       args.add("0");
168       args.add("-Xmaxerrs");
169       args.add("5");
170     }
171     if (_javaVersion != null) {
172       args.add("-source");
173       args.add(_javaVersion);
174     }
175     args.add("-classpath");
176     String classPathOverriden = System.getProperty("xmlctf.classpath.override");
177     if (classPathOverriden != null) {
178       args.add(classPathOverriden + ";" + destDir.getAbsolutePath());
179     } else {
180       args.add(System.getProperty("java.class.path") + ";" + destDir.getAbsolutePath());
181     }
182     args.add("-d");
183     args.add(destDir.getAbsolutePath());
184     args.add("-sourcepath");
185     args.add(srcDir.getAbsolutePath());
186     return args;
187   }
188 
189   /**
190    * Recursively searches the provided directory, returning a list of all Java files found.
191    * 
192    * @param srcDir A directory to search for Java files.
193    * @return a List of all Java files found in the provided directory
194    */
195   private List findSourceFiles(final File srcDir) {
196     List files = new ArrayList();
197 
198     File[] entries = srcDir.listFiles();
199     for (int i = 0; i < entries.length; i++) {
200       File entry = entries[i];
201       if (entry.getName().endsWith(".java")) {
202         files.add(entry.getAbsolutePath());
203       } else if (entry.isDirectory() && !IGNORE_DIRS.contains(entry.getName())) {
204         files.addAll(findSourceFiles(entry));
205       }
206     }
207 
208     return files;
209   }
210 
211 }