blob: bfc44c25f4086c820b89cd097c514d7139817837 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.myfaces.extensions.scripting.core.engine.compiler;
import org.apache.myfaces.extensions.scripting.core.api.Configuration;
import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
import org.apache.myfaces.extensions.scripting.core.common.util.ClassLoaderUtils;
import org.apache.myfaces.extensions.scripting.core.common.util.FileUtils;
import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationMessage;
import org.apache.myfaces.extensions.scripting.core.engine.api.CompilationResult;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.apache.myfaces.extensions.scripting.core.api.ScriptingConst.ENGINE_TYPE_JSF_JAVA;
import static org.apache.myfaces.extensions.scripting.core.engine.api.CompilerConst.*;
/**
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
* <p/>
* a JSR 199 based compiler which implements
* our simplified compiler interface
*/
public class JSR199Compiler implements org.apache.myfaces.extensions.scripting.core.engine.api.Compiler
{
javax.tools.JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = null;
public JSR199Compiler()
{
super();
}
/**
* compile all files starting from a given root
* <p/>
* note, the java compiler interface does not allow per se
* wildcards due to its file object indirection
* we deal with that problem by determine all files manually and then
* push the list into the jsr compiler interface
*
*
* @param sourceRoot the root for all java sources to be compiled
* @param loader the classpath holder for the compilation
* @return the collected compilation results as bundle
*/
public CompilationResult compile(File sourceRoot, File destination, ClassLoader loader) {
WeavingContext context = WeavingContext.getInstance();
Configuration configuration = context.getConfiguration();
destination.mkdirs();
fileManager = javaCompiler.getStandardFileManager(new
DiagnosticCollector<JavaFileObject>(), Locale.getDefault(), null);
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
getLog().info("[EXT-SCRIPTING] Doing a full recompile");
List<File> sourceFiles = FileUtils.fetchSourceFiles(configuration.getWhitelistedSourceDirs
(ENGINE_TYPE_JSF_JAVA), JAVA_WILDCARD);
HashSet<String> alreadyProcessed = new HashSet<String>();
Iterator<File> sourceIt = sourceFiles.iterator();
while(sourceIt.hasNext()) {
File currentProcessed = sourceIt.next();
if(alreadyProcessed.contains(currentProcessed.getAbsolutePath())) {
sourceIt.remove();
} else {
alreadyProcessed.add(currentProcessed.getAbsolutePath());
}
}
for (File sourceFile : sourceFiles)
{
if (!sourceFile.exists())
{
getLog().log(Level.WARNING, "[EXT-SCRIPTING] Source file with path {0} does not exist it might cause an error in the compilation process", sourceFile.getAbsolutePath());
}
}
Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(sourceFiles.toArray(new File[sourceFiles.size()]));
String[] options = new String[]{JC_CLASSPATH, /*fileManager.getClassPath()*/
ClassLoaderUtils.buildClasspath(ClassLoaderUtils.getDefaultClassLoader())
, JC_TARGET_PATH,
destination.getAbsolutePath(), JC_SOURCEPATH,
sourceRoot.getAbsolutePath(), JC_DEBUG};
javaCompiler.getTask(null, fileManager, diagnosticCollector, Arrays.asList(options), null, fileObjects).call();
CompilationResult result = handleDiagnostics(diagnosticCollector);
return result;
}
/**
* internal diagnostics handler
* which just logs the errors
*
* @param diagnosticCollector the compilation results, the jsr 199 uses a DiagnosticsCollector object
* to keep the errors and warnings of the compiler
* @throws ClassNotFoundException in case of an error (this is enforced by the compiler interface
* and probably will be overhauled in the long run)
*/
private CompilationResult handleDiagnostics(DiagnosticCollector<JavaFileObject> diagnosticCollector)
{
if (diagnosticCollector.getDiagnostics().size() > 0)
{
Logger log = Logger.getLogger(this.getClass().getName());
StringBuilder errors = new StringBuilder();
CompilationResult result = new CompilationResult("");
boolean hasError = false;
for (Diagnostic diagnostic : diagnosticCollector.getDiagnostics())
{
String error = createErrorMessage(diagnostic);
log.log(Level.WARNING, "[EXT-SCRIPTING] Compiler: {0}", error);
if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR))
{
hasError = true;
result.getErrors().add(new CompilationMessage(diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault())));
} else
{
result.getWarnings().add(new CompilationMessage(diagnostic.getLineNumber(), diagnostic.getMessage(Locale.getDefault())));
}
errors.append(error);
}
return result;
} else
{
//WeavingContext.setCompilationResult(ENGINE_TYPE_JSF_JAVA, new CompilationResult(""));
return new CompilationResult("");
}
}
/**
* interruption of the compile flow should only
* happen if an error has occurred otherwise we will proceed
* as expected
*
* @param errors the errors messages found
* @param hasError marker if an error was found or not
* @throws ClassNotFoundException in case of a compile error
*/
private void assertErrorFound(StringBuilder errors, boolean hasError) throws ClassNotFoundException
{
if (hasError)
{
throw new ClassNotFoundException("Compile error of java file:" + errors.toString());
}
}
/**
* creates a standardized error message
*
* @param diagnostic the diagnostic of the compiler containing the error data
* @return a formatted string with the standardized error message which then later
* can be processed by the user
*/
private String createErrorMessage(Diagnostic diagnostic)
{
StringBuilder retVal = new StringBuilder(256);
if (diagnostic == null)
{
return retVal.toString();
}
if (diagnostic.getKind().equals(Diagnostic.Kind.ERROR))
{
retVal.append(STD_ERROR_HEAD);
} else if (diagnostic.getKind().equals(Diagnostic.Kind.NOTE))
{
retVal.append(STD_NOTE_HEAD);
} else if (diagnostic.getKind().equals(Diagnostic.Kind.WARNING))
{
retVal.append(STD_WARN_HEAD);
} else if (diagnostic.getKind().equals(Diagnostic.Kind.MANDATORY_WARNING))
{
retVal.append(STD_MANDATORY_WARN_HEAD);
} else if (diagnostic.getKind().equals(Diagnostic.Kind.OTHER))
{
retVal.append(STD_OTHER_HEAD);
}
String message = diagnostic.getMessage(Locale.getDefault());
message = (message == null) ? "" : message;
retVal.append(message);
retVal.append(diagnostic.getLineNumber());
retVal.append("\n\n");
String source = "No additional source info";
if (diagnostic.getSource() != null)
{
source = diagnostic.getSource().toString();
}
retVal.append(source);
return retVal.toString();
}
private Logger getLog()
{
return Logger.getLogger(this.getClass().getName());
}
}