/* * This code is distributed under The GNU Lesser General Public License (LGPLv3) * Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html * * Copyright Denis Pavlov 2009 * Web: http://www.genericdtoassembler.org * SVN: https://svn.code.sf.net/p/geda-genericdto/code/trunk/ * SVN (mirror): http://geda-genericdto.googlecode.com/svn/trunk/ */ package com.inspiresoftware.lib.dto.geda.assembler.extension.impl; import com.inspiresoftware.lib.dto.geda.assembler.extension.DataReader; import com.inspiresoftware.lib.dto.geda.assembler.extension.DataWriter; import com.inspiresoftware.lib.dto.geda.assembler.extension.MethodSynthesizer; import com.inspiresoftware.lib.dto.geda.exception.GeDAException; import com.inspiresoftware.lib.dto.geda.exception.UnableToCreateInstanceException; import com.sun.tools.javac.Main; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.lang.reflect.Type; /** * Sun Java tools implementation of method synthesizer. * * @author denispavlov * @since 1.1.2 * */ public class SunJavaToolsMethodSynthesizer extends AbstractPlainTextMethodSynthesizer implements MethodSynthesizer, BaseDirectoryProvider { private static final Logger LOG = LoggerFactory.getLogger(SunJavaToolsMethodSynthesizer.class); private static final String JAVA_SOURCE_VERSION = "1.5"; private String baseDir = ""; /** * Sun Java Tools method synthesizer constructor. * Initializes a class loader able to define classes from auto generated * compiled temporary files. * * @param classLoader class loader */ public SunJavaToolsMethodSynthesizer(final ClassLoader classLoader) { super(classLoader); } /** * Manual constructor with baseDir specified. * * @param baseDir base dir for creating files * @throws GeDAException any exceptions during configuration */ public SunJavaToolsMethodSynthesizer(final ClassLoader classLoader, final String baseDir) throws GeDAException { this(classLoader); this.configure("baseDir", baseDir); } /** {@inheritDoc} */ protected String getSynthesizerId() { return "suntools"; } /** {@inheritDoc} */ protected SoftReference<ClassLoader> initialiseClassLoaderWeakReference(final ClassLoader classLoader) { return new SoftReference<ClassLoader>(new FileClassLoader(classLoader, this)); } /** {@inheritDoc} */ public String getBaseDir(final String name) { return baseDir; } /** * @param configuration configuration name * baseDir - allows to set the directory where newly generated temp files for classes * will reside until the system exits. * readerCleanUpCycle - allows to set clean up cycle for soft cache of readers * writerCleanUpCycle - allows to set clean up cycle for soft cache of writers * @param value value to set * @return true if configuration was set, false if not set or invalid * @throws GeDAException any exceptions during configuration */ @Override public boolean configure(final String configuration, final Object value) throws GeDAException { if ("baseDir".equals(configuration) && value instanceof String) { final String dir = (String) value; if (dir.endsWith("/")) { this.baseDir = dir; } else { this.baseDir = dir + "/"; } LOG.info("Setting class loader base dir to: {}", this.baseDir); return true; } return super.configure(configuration, value); } /** {@inheritDoc} */ protected DataReader makeReaderClass( final ClassLoader loader, final Method readMethod, final String readerClassName, final String sourceClassNameFull, final String sourceClassGetterMethodName, final Type sourceClassGetterMethodReturnType, final MakeContext ctx) throws UnableToCreateInstanceException { try { final StringBuilder readMethodCode = new StringBuilder(); final StringBuilder getReturnTypeMethodCode = new StringBuilder(); generateReaderMethods(readMethodCode, getReturnTypeMethodCode, readerClassName, sourceClassNameFull, sourceClassGetterMethodName, sourceClassGetterMethodReturnType); final String source = generateReaderSource(readerClassName, readMethodCode, getReturnTypeMethodCode); final File clazz = createSourceFile(readerClassName, source); if (compile(clazz) == 0) { final Class< ? > readerClass = getClassLoader().loadClass(readerClassName); return (DataReader) readerClass.newInstance(); } else { ctx.next(null, source); } } catch (Exception ite) { throw new UnableToCreateInstanceException(readerClassName, "Unable to instantiate class: " + readerClassName, ite); } return null; } private String generateReaderSource(final String fullClassName, final StringBuilder readMethodCode, final StringBuilder getReturnTypeMethodCode) { final int namePos = fullClassName.lastIndexOf('.'); final String packageName = fullClassName.substring(0, namePos); final String className = fullClassName.substring(namePos + 1); final StringBuilder source = new StringBuilder(); source .append("package ").append(packageName).append(";\n") .append("import com.inspiresoftware.lib.dto.geda.assembler.extension.DataReader;\n") .append("public class ").append(className).append(" implements DataReader {\n") .append(getReturnTypeMethodCode) .append(readMethodCode) .append("}\n"); return source.toString(); } /** {@inheritDoc} */ protected DataWriter makeWriterClass( final ClassLoader loader, final Method writeMethod, final String writerClassName, final String sourceClassNameFull, final String sourceClassSetterMethodName, final Class< ? > sourceClassSetterMethodArgumentClass, final MakeContext ctx) throws UnableToCreateInstanceException { try { final StringBuilder writeMethodCode = new StringBuilder(); final StringBuilder getParameterTypeMethodCode = new StringBuilder(); generateWriterMethods(writeMethodCode, getParameterTypeMethodCode, writerClassName, sourceClassNameFull, sourceClassSetterMethodName, sourceClassSetterMethodArgumentClass); final String source = generateWriterSource(writerClassName, writeMethodCode, getParameterTypeMethodCode); final File clazz = createSourceFile(writerClassName, source); if (compile(clazz) == 0) { final Class< ? > writerClass = getClassLoader().loadClass(writerClassName); return (DataWriter) writerClass.newInstance(); } else { ctx.next(null, source); } } catch (Exception ite) { throw new UnableToCreateInstanceException(writerClassName, "Unable to instantiate class: " + writerClassName, ite); } return null; } private String generateWriterSource(final String fullClassName, final StringBuilder writeMethodCode, final StringBuilder getParameterTypeMethodCode) { final int namePos = fullClassName.lastIndexOf('.'); final String packageName = fullClassName.substring(0, namePos); final String className = fullClassName.substring(namePos + 1); final StringBuilder source = new StringBuilder(); source .append("package ").append(packageName).append(";\n") .append("import com.inspiresoftware.lib.dto.geda.assembler.extension.DataWriter;\n") .append("public class ").append(className).append(" implements DataWriter {\n") .append(getParameterTypeMethodCode) .append(writeMethodCode) .append("}\n"); return source.toString(); } private File createSourceFile(final String className, final String source) throws UnableToCreateInstanceException { final String readerSimpleName = className.substring(className.lastIndexOf('.') + 1); final File clazz = new File(this.baseDir + readerSimpleName + ".java"); try { if (LOG.isDebugEnabled()) { LOG.debug("Attempt to create source file: {}", clazz.getAbsolutePath()); } if (!clazz.exists()) { clazz.deleteOnExit(); clazz.createNewFile(); } LOG.debug("Source: \n{}\n", source); FileOutputStream fos = null; try { fos = new FileOutputStream(clazz); fos.write(source.getBytes()); } finally { if (fos != null) { fos.close(); } } if (LOG.isDebugEnabled()) { LOG.debug("Successfully created source file: {}", clazz.getAbsolutePath()); } return clazz; } catch (IOException ioe) { throw new UnableToCreateInstanceException("DataReader", "Unable to create temporary file for reader source: " + clazz.getAbsolutePath(), ioe); } } private static int compile(final File source) { return Main.compile(new String[] { "-source", JAVA_SOURCE_VERSION, "-target", JAVA_SOURCE_VERSION, "-verbose", source.getAbsolutePath() }); } }