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