/** * Copyright © 2010-2014 Nokia * * 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 org.jsonschema2pojo.integration.util; import java.io.File; import java.io.IOException; import java.io.Writer; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.tools.Diagnostic; import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import static org.apache.commons.io.FileUtils.*; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static org.jsonschema2pojo.integration.util.Compiler.systemJavaCompiler; /** * A JUnit rule that executes JsonSchema2Pojo. * * @author Christian Trimble * */ public class Jsonschema2PojoRule implements TestRule { private File generateDir; private File compileDir; private boolean active = false; private boolean captureDiagnostics = false; private boolean sourceDirInitialized = false; private boolean classesDirInitialized = false; private ClassLoader classLoader; private List<Diagnostic<? extends JavaFileObject>> diagnostics; public Jsonschema2PojoRule captureDiagnostics() { this.captureDiagnostics = true; return this; } /** * Gets the target directory for generate calls. * * @return The target directory for generate calls. */ public File getGenerateDir() { checkActive(); sourceDirInitialized = ensureDirectoryInitialized(generateDir, sourceDirInitialized); return generateDir; } /** * Gets the target directory for compile calls. * * @return The target directory for compile calls. */ public File getCompileDir() { checkActive(); classesDirInitialized = ensureDirectoryInitialized(compileDir, classesDirInitialized); return compileDir; } /** * Returns the class loader for compiled classes. Only defined after calling * a compile method. * * @return The class loader for compiled classes. */ public ClassLoader getClassLoader() { checkActive(); return classLoader; } public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() { checkActive(); return diagnostics; } @Override public Statement apply(final Statement base, final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { active = true; diagnostics = new ArrayList<Diagnostic<? extends JavaFileObject>>(); boolean captureDiagnosticsStart = captureDiagnostics; try { File testRoot = methodNameDir(classNameDir(rootDirectory(), description.getClassName()), description.getMethodName()); generateDir = new File(testRoot, "generate"); compileDir = new File(testRoot, "compile"); base.evaluate(); } finally { generateDir = null; compileDir = null; classLoader = null; sourceDirInitialized = false; classesDirInitialized = false; captureDiagnostics = captureDiagnosticsStart; diagnostics = null; active = false; } } }; } public File generate(String schema, String targetPackage) { return generate(schema, targetPackage, emptyConfig()); } public File generate(URL schema, String targetPackage) { return generate(schema, targetPackage, emptyConfig()); } public File generate(String schema, String targetPackage, Map<String, Object> configValues) { return generate(schemaUrl(schema), targetPackage, configValues); } public File generate(final URL schema, final String targetPackage, final Map<String, Object> configValues) { CodeGenerationHelper.generate(schema, targetPackage, configValues, getGenerateDir()); return generateDir; } public ClassLoader compile() { return compile(emptyClasspath(), emptyConfig()); } public ClassLoader compile(List<File> classpath) { return compile(classpath, emptyConfig()); } public ClassLoader compile(List<File> classpath, Map<String, Object> config) { return compile(systemJavaCompiler(), null, classpath, config); } public ClassLoader compile(JavaCompiler compiler, Writer out, List<File> classpath, Map<String, Object> config) { if (classLoader != null) { throw new IllegalStateException("cannot recompile sources"); } DiagnosticListener<JavaFileObject> diagnosticListener = captureDiagnostics ? new CapturingDiagnosticListener() : null; classLoader = CodeGenerationHelper.compile(compiler, out, getGenerateDir(), getCompileDir(), classpath, config, diagnosticListener); return classLoader; } public ClassLoader generateAndCompile(String schema, String targetPackage, Map<String, Object> configValues) { generate(schema, targetPackage, configValues); return compile(emptyClasspath(), configValues); } public ClassLoader generateAndCompile(String schema, String targetPackage) { generate(schema, targetPackage); return compile(); } public ClassLoader generateAndCompile(URL schema, String targetPackage) { generate(schema, targetPackage); return compile(emptyClasspath(), emptyConfig()); } public ClassLoader generateAndCompile(URL schema, String targetPackage, Map<String, Object> configValues) { generate(schema, targetPackage, configValues); return compile(emptyClasspath(), configValues); } public File generated(String relativeSourcePath) { return new File(generateDir, relativeSourcePath); } private void checkActive() { if (active != true) { throw new IllegalStateException("cannot access Jsonschema2PojoRule state when inactive"); } } class CapturingDiagnosticListener implements DiagnosticListener<JavaFileObject> { @Override public void report(Diagnostic<? extends JavaFileObject> diagnostic) { diagnostics.add(diagnostic); } } private static List<File> emptyClasspath() { return new ArrayList<File>(); } private static Map<String, Object> emptyConfig() { return new HashMap<String, Object>(); } private static URL schemaUrl(String schema) { URL schemaUrl = Jsonschema2PojoRule.class.getResource(schema); assertThat("Unable to read schema resource from the classpath: " + schema, schemaUrl, is(notNullValue())); return schemaUrl; } static File rootDirectory() { return new File("target" + File.separator + "jsonschema2pojo"); } static File classNameDir(File baseDir, String className) throws IOException { return new File(baseDir, classNameToPath(className)); } static final Pattern methodNamePattern = compilePattern("\\A([^\\[]+)(?:\\[(.*)\\])?\\Z"); /** * Returns the compiled pattern, or null if the pattern could not compile. */ static Pattern compilePattern(String pattern) { try { return Pattern.compile(pattern); } catch (Exception e) { System.err.println("Could not compile pattern " + pattern); e.printStackTrace(System.err); return null; } } static File methodNameDir(File baseDir, String methodName) throws IOException { if (methodName == null) methodName = "class"; Matcher matcher = methodNamePattern.matcher(methodName); if (matcher.matches()) { if (matcher.group(2) != null) { baseDir = new File(baseDir, safeDirName(matcher.group(2))); } return new File(baseDir, safeDirName(matcher.group(1))); } else { throw new IOException("cannot transform methodName (" + methodName + ") into path"); } } static boolean ensureDirectoryInitialized(File dir, boolean isInitialized) { if (!isInitialized) { try { forceMkdir(dir); cleanDirectory(dir); } catch (IOException ioe) { throw new RuntimeException("could not clean directory", ioe); } } return true; } static String safeDirName(String label) { return label.replaceAll("[^a-zA-Z1-9]+", "_"); } static String classNameToPath(String className) { return className .replaceAll("\\A(?:.*\\.)?([^\\.]*)\\Z", "$1") .replaceAll("\\$", Pattern.quote(File.separator)); } }