package knorxx.framework.generator; import com.google.common.base.Joiner; import com.google.common.base.Optional; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.HashSet; import java.util.List; import java.util.Set; import knorxx.framework.generator.dependency.ByteCodeDependencyCollector; import knorxx.framework.generator.dependency.DependencyCollector; import knorxx.framework.generator.dependency.JavaSourceDependencyCollector; import knorxx.framework.generator.library.LibraryDetector; import knorxx.framework.generator.order.InheritanceOrderSorter; import knorxx.framework.generator.order.OrderSorter; import knorxx.framework.generator.single.SingleFileGenerator; import knorxx.framework.generator.single.SingleFileGeneratorException; import knorxx.framework.generator.single.SingleResult; import static knorxx.framework.generator.util.JavaIdentifierUtils.javaClassNamesToPackages; import static knorxx.framework.generator.util.JavaIdentifierUtils.removeJavaCoreClassNames; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author sj */ public class JavaScriptGenerator { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * @param dependencyCollector If no dependency collector is supplied the ByteCodeDependencyCollector and the * JavaSourceDependencyCollector are used. * @param classLoader If no class loader is supplied Thread.currentThread().getContextClassLoader() is used. * @param orderSorter If no order sorter is supplied the InheritanceOrderSorter is used. */ public GenerationUnit generate(Class<?> javaClass, SingleFileGenerator singleFileGenerator, Optional<DependencyCollector> dependencyCollector, Optional<? extends ClassLoader> classLoader, Optional<OrderSorter> orderSorter, Optional<LibraryDetector> libraryDetector, GenerationRoots generationRoots) { return generate(javaClass, singleFileGenerator, getDependencyCollector(dependencyCollector), getClassLoader(classLoader), getOrderSorter(orderSorter), getLibraryDetector(libraryDetector), generationRoots, new GenerationUnit()); } /** * @param dependencyCollector If no dependency collector is supplied the ByteCodeDependencyCollector and the * JavaSourceDependencyCollector are used. * @param classLoader If no class loader is supplied Thread.currentThread().getContextClassLoader() is used. * @param orderSorter If no order sorter is supplied the InheritanceOrderSorter is used. */ public GenerationUnit generate(Class<?> javaClass, SingleFileGenerator singleFileGenerator, Optional<DependencyCollector> dependencyCollector, Optional<? extends ClassLoader> classLoader, Optional<OrderSorter> orderSorter, Optional<LibraryDetector> libraryDetector, GenerationRoots generationRoots, GenerationUnit unit) { return generate(javaClass, singleFileGenerator, getDependencyCollector(dependencyCollector).get(), getClassLoader(classLoader).get(), getOrderSorter(orderSorter).get(), getLibraryDetector(libraryDetector).get(), generationRoots, unit); } /** * @param dependencyCollector If no dependency collector is supplied the ByteCodeDependencyCollector and the * JavaSourceDependencyCollector are used. * @param classLoader If no class loader is supplied Thread.currentThread().getContextClassLoader() is used. * @param orderSorter If no order sorter is supplied the InheritanceOrderSorter is used. */ public GenerationUnit generateAll(Class<?> javaClass, SingleFileGenerator singleFileGenerator, Optional<DependencyCollector> dependencyCollector, Optional<? extends ClassLoader> classLoader, Optional<OrderSorter> orderSorter, Optional<LibraryDetector> libraryDetector, GenerationRoots generationRoots) { return generateAll(Lists.<Class<?>>newArrayList(javaClass), singleFileGenerator, dependencyCollector, classLoader, orderSorter, libraryDetector, generationRoots); } /** * @param dependencyCollector If no dependency collector is supplied the ByteCodeDependencyCollector and the * JavaSourceDependencyCollector are used. * @param classLoader If no class loader is supplied Thread.currentThread().getContextClassLoader() is used. * @param orderSorter If no order sorter is supplied the InheritanceOrderSorter is used. */ public GenerationUnit generateAll(List<Class<?>> javaClasses, SingleFileGenerator singleFileGenerator, Optional<DependencyCollector> dependencyCollector, Optional<? extends ClassLoader> classLoader, Optional<OrderSorter> orderSorter, Optional<LibraryDetector> libraryDetector, GenerationRoots generationRoots) { GenerationUnit unit = new GenerationUnit(); for(int i = 1; i < javaClasses.size(); i++) { unit.addMissingDependencies(Sets.newHashSet(javaClasses.get(i).getName())); } unit = generate(javaClasses.get(0), singleFileGenerator, getDependencyCollector(dependencyCollector), getClassLoader(classLoader), getOrderSorter(orderSorter), getLibraryDetector(libraryDetector), generationRoots, unit); while (!unit.isMissingDependenciesEmpty()) { Class nextJavaClass; try { nextJavaClass = classLoader.get().loadClass(unit.getNextDependency()); } catch (ClassNotFoundException ex) { throw new IllegalStateException("Can't resolve the Java class '" + unit.getNextDependency() + "'!", ex); } unit = generate(nextJavaClass, singleFileGenerator, getDependencyCollector(dependencyCollector), getClassLoader(classLoader), getOrderSorter(orderSorter), getLibraryDetector(libraryDetector), generationRoots, unit); } return unit; } private GenerationUnit generate(Class<?> javaClass, SingleFileGenerator singleFileGenerator, DependencyCollector dependencyCollector, ClassLoader classLoader, OrderSorter orderSorter, LibraryDetector libraryDetector, GenerationRoots generationRoots, GenerationUnit unit) { logger.info("Generating JavaScript for {}", javaClass.getName()); JavaFileWithSource javaFile = new JavaFileWithSource<>(javaClass, generationRoots); checkArgument(!javaFile.isInnerClass(), "It's not possible to generate JavaScript for inner classes!"); unit = new GenerationUnit(unit); unit.addVisitedClass(javaFile.getJavaClassName()); unit.removeMissingDependencies(javaFile.getJavaClassName()); Set<String> dependencies = getAllDependencies(javaFile, dependencyCollector, classLoader, singleFileGenerator, generationRoots); logger.debug("Dependencies: {}", Joiner.on(", ").join(dependencies)); unit.getLibraryUrls().addAll(libraryDetector.detect(dependencies)); SingleResult singleResult; try { singleResult = singleFileGenerator.generate(javaFile, classLoader, javaClassNamesToPackages(dependencies)); } catch (SingleFileGeneratorException ex) { throw new IllegalStateException("An error occured while generating the file '" + javaFile + "'!", ex); } dependencies = removeJavaCoreClassNames(dependencies); logger.debug("Dependencies after removal of Java core classes: {}", Joiner.on(", ").join(dependencies)); dependencies = singleFileGenerator.removeNotGeneratableJavaClasses(javaClass, dependencies, classLoader); logger.debug("Dependencies after removal of not generatable Java classes (by {}): {}", singleFileGenerator.getClass().getSimpleName(), Joiner.on(", ").join(dependencies)); unit.addGenerationResult(new GenerationResult(javaFile, new DateTime(), dependencies, singleResult), orderSorter); unit.addMissingDependencies(dependencies); return unit; } /** * Collects the dependencies of the javaFile and all of its super classes (if generatable). */ private Set<String> getAllDependencies(JavaFileWithSource<?> javaFile, DependencyCollector dependencyCollector, ClassLoader classLoader, SingleFileGenerator singleFileGenerator, GenerationRoots generationRoots) { Set<String> dependencies = new HashSet<>(); Class currentJavaClass = javaFile.getJavaClass(); while (currentJavaClass != null) { dependencies.addAll(dependencyCollector.collect(javaFile, classLoader)); currentJavaClass = currentJavaClass.getSuperclass(); if (currentJavaClass != null) { // NOTE The check if the super class is generatable feels a bit hacky... but it works. ;-) Set<String> superClassGeneratableTest = Sets.newHashSet(currentJavaClass.getName()); superClassGeneratableTest = removeJavaCoreClassNames(superClassGeneratableTest); superClassGeneratableTest = singleFileGenerator.removeNotGeneratableJavaClasses(currentJavaClass, superClassGeneratableTest, classLoader); if (superClassGeneratableTest.isEmpty()) { currentJavaClass = null; } else { javaFile = new JavaFileWithSource<>(currentJavaClass, generationRoots); } } } return dependencies; } private Optional<? extends ClassLoader> getClassLoader(Optional<? extends ClassLoader> classLoader) { if (classLoader.isPresent()) { return classLoader; } else { return Optional.of(Thread.currentThread().getContextClassLoader()); } } private Optional<DependencyCollector> getDependencyCollector(Optional<DependencyCollector> dependencyCollector) { if (dependencyCollector.isPresent()) { return dependencyCollector; } else { return Optional.<DependencyCollector>of(new ByteCodeDependencyCollector(new JavaSourceDependencyCollector())); } } private Optional<OrderSorter> getOrderSorter(Optional<OrderSorter> orderSorter) { if(orderSorter.isPresent()) { return orderSorter; } else { return Optional.<OrderSorter>of(new InheritanceOrderSorter()); } } private Optional<LibraryDetector> getLibraryDetector(Optional<LibraryDetector> libraryDetector) { if(libraryDetector.isPresent()) { return libraryDetector; } else { return Optional.<LibraryDetector>of(new LibraryDetector.None()); } } }