/** * Copyright (C) 2006-2017 INRIA and contributors * Spoon - http://spoon.gforge.inria.fr/ * * This software is governed by the CeCILL-C License under French law and * abiding by the rules of distribution of free software. You can use, modify * and/or redistribute the software under the terms of the CeCILL-C license as * circulated by CEA, CNRS and INRIA at http://www.cecill.info. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ package spoon.support; import spoon.Launcher; import spoon.processing.AbstractProcessor; import spoon.processing.FileGenerator; import spoon.processing.TraversalStrategy; import spoon.reflect.cu.CompilationUnit; import spoon.reflect.declaration.CtNamedElement; import spoon.reflect.declaration.CtPackage; import spoon.reflect.declaration.CtType; import spoon.reflect.visitor.DefaultJavaPrettyPrinter; import spoon.reflect.visitor.PrettyPrinter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; //import spoon.reflect.cu.CompilationUnit; /** * A processor that generates compilable Java source files from the meta-model. */ public class JavaOutputProcessor extends AbstractProcessor<CtNamedElement> implements FileGenerator<CtNamedElement> { PrettyPrinter printer; File directory; List<File> printedFiles = new ArrayList<>(); /** * Creates a new processor for generating Java source files. * * @param outputDirectory the root output directory */ public JavaOutputProcessor(File outputDirectory, PrettyPrinter printer) { this.directory = outputDirectory; this.printer = printer; } /** * usedful for testing */ public JavaOutputProcessor() { } public PrettyPrinter getPrinter() { return printer; } public List<File> getCreatedFiles() { return printedFiles; } public File getOutputDirectory() { return directory; } @Override public void init() { // Skip loading properties // super.init(); // Check output directory if (directory == null) { throw new RuntimeException("You should set output directory before printing"); } // Create spooned dir if (directory.isFile()) { throw new RuntimeException("Output must be a directory"); } if (!directory.exists()) { if (!directory.mkdirs()) { throw new RuntimeException("Error creating output directory"); } } try { directory = directory.getCanonicalFile(); } catch (IOException e) { Launcher.LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } } Map<String, Map<Integer, Integer>> lineNumberMappings = new HashMap<>(); /** * Creates the Java file associated to the given element. Splits top-level * classes in different files (even if they are in the same file in the * original sources). */ public void createJavaFile(CtType<?> element) { getEnvironment().debugMessage("printing " + element.getQualifiedName() + " to " + directory); // we only create a file for top-level classes if (!element.isTopLevel()) { throw new IllegalArgumentException(); } CompilationUnit cu = null; if (element.getPosition() != null) { cu = element.getPosition().getCompilationUnit(); // this is a top level type (see check above) // if the compilation unit is not set, we use a default one if (cu == null) { cu = element.getFactory().CompilationUnit().create(element.getQualifiedName()); cu.setDeclaredPackage(element.getPackage()); } } List<CtType<?>> toBePrinted = new ArrayList<>(); toBePrinted.add(element); printer.calculate(cu, toBePrinted); CtPackage pack = element.getPackage(); PrintStream stream = null; // print type try { File file = new File(getPackageFile(pack).getAbsolutePath() + File.separatorChar + element.getSimpleName() + DefaultJavaPrettyPrinter.JAVA_FILE_EXTENSION); file.createNewFile(); if (!printedFiles.contains(file)) { printedFiles.add(file); } stream = new PrintStream(file); stream.print(printer.getResult()); for (CtType<?> t : toBePrinted) { lineNumberMappings.put(t.getQualifiedName(), printer.getLineNumberMapping()); } stream.close(); } catch (IOException e) { Launcher.LOGGER.error(e.getMessage(), e); } finally { if (stream != null) { stream.close(); } } } @Override public boolean isToBeProcessed(CtNamedElement candidate) { return candidate instanceof CtType<?> || candidate instanceof CtPackage && (candidate.getComments().size() > 0 || candidate.getAnnotations().size() > 0); } /** * Creates a source file for each processed top-level type and pretty prints * its contents. */ public void process(CtNamedElement nameElement) { if (nameElement instanceof CtType && ((CtType) nameElement).isTopLevel()) { createJavaFile((CtType<?>) nameElement); } else if (nameElement instanceof CtPackage) { createPackageFile((CtPackage) nameElement); } printer.reset(); } private void createPackageFile(CtPackage pack) { // Create package annotation file File packageAnnot = new File(getPackageFile(pack).getAbsolutePath() + File.separatorChar + DefaultJavaPrettyPrinter.JAVA_PACKAGE_DECLARATION); if (!printedFiles.contains(packageAnnot)) { printedFiles.add(packageAnnot); } PrintStream stream = null; try { stream = new PrintStream(packageAnnot); stream.println(printer.printPackageInfo(pack)); stream.close(); } catch (FileNotFoundException e) { Launcher.LOGGER.error(e.getMessage(), e); } finally { if (stream != null) { stream.close(); } } } private File getPackageFile(CtPackage pack) { File packageDir; if (pack.isUnnamedPackage()) { packageDir = new File(directory.getAbsolutePath()); } else { // Create current package dir packageDir = new File(directory.getAbsolutePath() + File.separatorChar + pack.getQualifiedName().replace('.', File.separatorChar)); } if (!packageDir.exists()) { if (!packageDir.mkdirs()) { throw new RuntimeException("Error creating output directory"); } } return packageDir; } public void setOutputDirectory(File directory) { this.directory = directory; } public Map<String, Map<Integer, Integer>> getLineNumberMappings() { return lineNumberMappings; } @Override public TraversalStrategy getTraversalStrategy() { return TraversalStrategy.PRE_ORDER; } }