Java tutorial
/* * Orika - simpler, better and faster Java bean mapping * * Copyright (C) 2011 Orika authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ma.glasnost.orika.impl.generator; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import ma.glasnost.orika.impl.generator.eclipsejdt.CompilationUnit; import ma.glasnost.orika.impl.generator.eclipsejdt.CompilerRequestor; import ma.glasnost.orika.impl.generator.eclipsejdt.NameEnvironment; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.text.edits.TextEdit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * EclipseJdtCompiler leverages the eclipse jdt core to compile source code * provided in String format.<br> * It can also make use of the source formatter tool to format source. * * @author matt.deboer@gmail.com */ public class EclipseJdtCompiler { private final static Logger LOG = LoggerFactory.getLogger(EclipseJdtCompiler.class); private static final String JAVA_COMPILER_SOURCE_VERSION = "1.5"; private static final String JAVA_COMPILER_COMPLIANCE_VERSION = "1.5"; private static final String JAVA_COMPILER_CODEGEN_TARGET_PLATFORM_VERSION = "1.5"; private static final String JAVA_SOURCE_ENCODING = "UTF-8"; private final ByteCodeClassLoader byteCodeClassLoader; private final CodeFormatter formatter; private final NameEnvironment compilerNameEnvironment; private final CompilerRequestor compilerRequester; private final Compiler compiler; public EclipseJdtCompiler() { this(Thread.currentThread().getContextClassLoader()); } public EclipseJdtCompiler(ClassLoader parentLoader) { this.byteCodeClassLoader = new ByteCodeClassLoader(parentLoader); this.formatter = ToolFactory.createCodeFormatter(getFormattingOptions()); this.compilerNameEnvironment = new NameEnvironment(this.byteCodeClassLoader); this.compilerRequester = new CompilerRequestor(); this.compiler = new Compiler(compilerNameEnvironment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), getCompilerOptions(), compilerRequester, new DefaultProblemFactory(Locale.getDefault())); } /** * Return the options to be passed when creating {@link CodeFormatter} * instance. * * @return */ private Map<Object, Object> getFormattingOptions() { @SuppressWarnings("unchecked") Map<Object, Object> options = DefaultCodeFormatterConstants.getEclipseDefaultSettings(); options.put(JavaCore.COMPILER_SOURCE, JAVA_COMPILER_SOURCE_VERSION); options.put(JavaCore.COMPILER_COMPLIANCE, JAVA_COMPILER_COMPLIANCE_VERSION); options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JAVA_COMPILER_CODEGEN_TARGET_PLATFORM_VERSION); return options; } private CompilerOptions getCompilerOptions() { Map<Object, Object> options = new HashMap<Object, Object>(); options.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE); options.put(CompilerOptions.OPTION_LineNumberAttribute, CompilerOptions.GENERATE); options.put(CompilerOptions.OPTION_SourceFileAttribute, CompilerOptions.GENERATE); options.put(CompilerOptions.OPTION_SuppressWarnings, CompilerOptions.ENABLED); options.put(CompilerOptions.OPTION_Source, JAVA_COMPILER_SOURCE_VERSION); options.put(CompilerOptions.OPTION_TargetPlatform, JAVA_COMPILER_CODEGEN_TARGET_PLATFORM_VERSION); options.put(CompilerOptions.OPTION_Encoding, JAVA_SOURCE_ENCODING); options.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE); // Ignore unchecked types and raw types options.put(JavaCore.COMPILER_PB_UNCHECKED_TYPE_OPERATION, CompilerOptions.IGNORE); options.put(JavaCore.COMPILER_PB_RAW_TYPE_REFERENCE, CompilerOptions.IGNORE); options.put(JavaCore.COMPILER_PB_VARARGS_ARGUMENT_NEED_CAST, CompilerOptions.IGNORE); return new CompilerOptions(options); } /** * Format the source code using the Eclipse text formatter */ public String formatSource(String code) { String lineSeparator = "\n"; TextEdit te = formatter.format(CodeFormatter.K_COMPILATION_UNIT, code, 0, code.length(), 0, lineSeparator); if (te == null) { throw new IllegalArgumentException( "source code was unable to be formatted; \n" + "//--- BEGIN ---\n" + code + "\n//--- END ---"); } IDocument doc = new Document(code); try { te.apply(doc); } catch (Exception e) { throw new RuntimeException(e); } String formattedCode = doc.get(); return formattedCode; } public void assertTypeAccessible(Class<?> type) throws IllegalStateException { if (!type.isPrimitive() && type.getClassLoader() != null) { String className; if (type.isArray()) { className = type.getComponentType().getName(); } else { className = type.getName(); } try { byteCodeClassLoader.loadClass(className); } catch (ClassNotFoundException e) { throw new IllegalStateException(type + " is not accessible", e); } NameEnvironmentAnswer answer = compilerNameEnvironment.findType(className); if (answer == null) { throw new IllegalStateException(type + " is not accessible"); } } } /** * Compile and return the (generated) class. * * @param source * @param packageName * @param classSimpleName * * @return the (generated) compiled class * @throws ClassNotFoundException */ public Class<?> compileAndLoad(String source, String packageName, String classSimpleName) throws ClassNotFoundException { String className = packageName + "." + classSimpleName; return load(className, compile(source, packageName, classSimpleName)); } /** * Compile and return the raw bytes of the class file. * * @param source * @param packageName * @param classSimpleName * * @return the raw bytes of the class file */ public byte[] compile(String source, String packageName, String classSimpleName) { Map<String, byte[]> compiledClasses = compile(source, packageName, classSimpleName, Thread.currentThread().getContextClassLoader()); String className = packageName + "." + classSimpleName; byte[] data = compiledClasses.get(className); return data; } /** * Compiles a set of files contained in source directory directly to bytes in memory, * returning a ClassLoader which is able to access them. * * @param sourceDir the directory (base) from which to find the java source files * @param parent the parent ClassLoader to be set for the returned loader * @throws IOException */ public ClassLoader compile(File sourceDir, ClassLoader parent) throws IOException { Map<String, byte[]> compiledClasses = compileClasses(sourceDir); ByteCodeClassLoader loader = new ByteCodeClassLoader(parent); for (Entry<String, byte[]> compiledClass : compiledClasses.entrySet()) { loader.putClassData(compiledClass.getKey(), compiledClass.getValue()); } return loader; } private Map<String, byte[]> compileClasses(File sourceDir) throws IOException { Collection<File> javaSources = FilePathUtility.getJavaSourceFiles(sourceDir); if (javaSources == null || javaSources.isEmpty()) { LOG.warn("No sources detected at " + sourceDir); return Collections.emptyMap(); } else { List<ICompilationUnit> compilationUnits = new ArrayList<ICompilationUnit>(); for (File javaSource : javaSources) { compilationUnits.add(new CompilationUnit(FilePathUtility.readFileAsString(javaSource), FilePathUtility.getJavaPackage(javaSource, sourceDir), FilePathUtility.getJavaClassName(javaSource))); } Map<String, byte[]> compiledClasses = compile(compilationUnits.toArray(new ICompilationUnit[0])); return compiledClasses; } } /** * Compiles a set of files contained in source directory, writing the class * files to binDir * * @param sourceDir * @param binDir * @throws IOException */ public void compile(File sourceDir, File binDir) throws IOException { Map<String, byte[]> compiledClasses = compileClasses(sourceDir); for (Entry<String, byte[]> compiledClass : compiledClasses.entrySet()) { FilePathUtility.writeClassFile(compiledClass.getKey(), compiledClass.getValue(), binDir); } } public Class<?> load(String className, byte[] data) throws ClassNotFoundException { byteCodeClassLoader.putClassData(className, data); return byteCodeClassLoader.loadClass(className); } /** * Gets the raw bytes of the classFile that was defined for the given * className by this compiler. * * @param className * @return */ public byte[] getBytes(String className) { return byteCodeClassLoader.getBytes(className); } private Map<String, byte[]> compile(String source, String packageName, String className, ClassLoader classLoader) { CompilationUnit unit = new CompilationUnit(source, packageName, className); return compile(unit); } private Map<String, byte[]> compile(final ICompilationUnit... compilationUnits) { Map<String, byte[]> compiledClasses = null; synchronized (compiler) { compilerRequester.reset(); compiler.compile(compilationUnits); if (compilerRequester.getProblems() != null) { StringBuilder warningText = new StringBuilder(); StringBuilder errorText = new StringBuilder(); boolean hasErrors = false; for (IProblem p : compilerRequester.getProblems()) { if (p.isError()) { hasErrors = true; errorText.append("ERROR: " + p.toString() + "\n\n"); } else { warningText.append("WARNING: " + p.toString() + "\n\n"); } } if (hasErrors) { throw new RuntimeException("Compilation encountered errors:\n" + errorText.toString() + "\n\n" + warningText.toString()); } else { LOG.warn("Compiler warnings:" + warningText.toString()); } } compiledClasses = compilerRequester.getCompiledClassFiles(); } return compiledClasses; } }