/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
package org.ebayopensource.turmeric.tools.library.builders;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import org.ebayopensource.turmeric.runtime.common.impl.utils.CallTrackingLogger;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.tools.codegen.util.ContextClassLoaderUtil;
import org.ebayopensource.turmeric.tools.library.TypeLibraryConstants;
import org.ebayopensource.turmeric.tools.library.utils.TypeLibraryUtilities;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import com.sun.codemodel.CodeWriter;
import com.sun.codemodel.JCodeModel;
import com.sun.istack.NotNull;
import com.sun.tools.xjc.AbortException;
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.ErrorReceiver;
import com.sun.tools.xjc.Language;
import com.sun.tools.xjc.ModelLoader;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.XJCListener;
import com.sun.tools.xjc.model.Model;
import com.sun.tools.xjc.outline.Outline;
import com.sun.tools.xjc.util.ErrorReceiverFilter;
public class ToolsXJCWrappper {
private static ToolsXJCWrappper toolsXJCWrappper;
private ToolsXJCWrappper(){}
private static HashMap<String, List<Exception>> s_refToxsdsWithError;
private static String s_currProcessedType;
public static synchronized ToolsXJCWrappper getInstance(){
if(toolsXJCWrappper == null)
toolsXJCWrappper = new ToolsXJCWrappper();
return toolsXJCWrappper;
}
public static boolean runXJC(String[] xjcArguments, HashMap<String, List<Exception>> dsWithError, String xsdTypeName) throws Exception{
s_refToxsdsWithError = dsWithError;
s_currProcessedType = xsdTypeName;
run(xjcArguments,System.err,System.out,dsWithError,xsdTypeName);
return true;
}
static class SOACatalogResolver implements EntityResolver{
private static CallTrackingLogger logger = LogManager.getInstance(SOACatalogResolver.class);
private CallTrackingLogger getLogger(){
return logger;
}
public InputSource resolveEntity(String publicId, String systemId) {
getLogger().log(Level.INFO, "systemId passed : " +systemId);
InputSource inputSource = null;
if (systemId != null && systemId.startsWith(TypeLibraryConstants.TYPE_LIB_REF_PROTOCOL)){
if(inputSource == null)
inputSource = new InputSource();
String libraryName =null;
String importedXsdFileName = null;
String[] values =deriveLibraryAndTypeDetailFromSystemID(systemId);
libraryName = values[0];
importedXsdFileName = values[1];
String importedXSDFilePath = null;
ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
InputStream inStream = null;
if(!TypeLibraryUtilities.isEmptyString(libraryName)){
importedXSDFilePath = TypeLibraryConstants.TYPES_FOLDER + "/" + libraryName + "/" + importedXsdFileName;
inStream = findResource(myClassLoader, "1. Looking for XSD as resource path: ", importedXSDFilePath);
}
if(inStream == null){
importedXSDFilePath = TypeLibraryConstants.TYPES_FOLDER + "/" + importedXsdFileName;
inStream = findResource(myClassLoader, "2. Looking for XSD as resource path: ", importedXSDFilePath);
}
if(inStream == null){
getLogger().log(Level.SEVERE, "Resolver could not locate the XSD file : " + importedXSDFilePath );
String[] pathsLookedup = new String[]{ TypeLibraryConstants.TYPES_FOLDER + "/" + libraryName + "/" + importedXsdFileName, importedXSDFilePath };
String errMsg = "The Type " + s_currProcessedType + " refers to the file " + importedXsdFileName + ". But the referred file could not be found.";
errMsg = errMsg + " The paths looked up are " + Arrays.toString( pathsLookedup );
FileNotFoundException fileNotFoundException = new FileNotFoundException(errMsg);
CodeGenTypeLibraryGenerator.addExceptionsToXSDErrorList(s_refToxsdsWithError, fileNotFoundException, s_currProcessedType);
return null;
}else{
getLogger().log(Level.INFO, "Found XSD in the path : " + importedXSDFilePath);
}
inputSource.setSystemId(importedXSDFilePath);
inputSource.setByteStream(inStream);
}
return inputSource;
}
/**
* Make the lookup of schemas a bit more reliable, regardless of
* behavior of classloaders and whatnot.
* <p>
* This will issues a logger {@link Level#WARNING} if the requested schema
* exists in multiple places within the classloader.
* <p>
* This will also prefer local over jar if given a choice between
* multiple schemas.
*
* @param cl the classloader to lookup the resources within.
* @param msg the message to prefix all logs with.
* @param path the path to look up.
* @return the found resource as an {@link InputStream} or null if not found.
*/
private InputStream findResource(ClassLoader cl, String msg, String path) {
getLogger().log(Level.INFO, msg + path);
try {
return ContextClassLoaderUtil.getResourceAsStream(path);
}
catch (IOException e) {
getLogger().log(Level.INFO, msg + path, e);
}
return null;
}
private String[] deriveLibraryAndTypeDetailFromSystemID(String systemId) {
String derivedSystemId = systemId.replaceAll("//", "/");
getLogger().log(Level.INFO, "systemId derived : " +derivedSystemId);
String[] response = new String[2];
String libraryName = null;
String typeName = null;
int firstIndex = derivedSystemId.indexOf('/'); // this indexOf would never return -1
int secondIndex = derivedSystemId.lastIndexOf('/');
if( secondIndex > firstIndex){
libraryName = derivedSystemId.substring(firstIndex + 1, secondIndex);
typeName = derivedSystemId.substring(secondIndex + 1);
}
else if(firstIndex == secondIndex){
typeName = derivedSystemId.substring(firstIndex+1);
}
response[0] = libraryName;
response[1] = typeName;
return response;
}
}
public static int run(String[] args, final PrintStream status,
final PrintStream out)throws Exception {
String xsdTypeName = "";
HashMap<String, List<Exception>> xsdsWithError = new HashMap<String, List<Exception>>(); //populate to avoid NPE
return run(args, status, out, xsdsWithError, xsdTypeName);
}
public static int run(String[] args, final PrintStream status,
final PrintStream out, final HashMap<String, List<Exception>> XSDsWithError, final String xsdTypeName) throws Exception {
class Listener extends XJCListener {
private CallTrackingLogger logger = LogManager.getInstance(ToolsXJCWrappper.class);
private CallTrackingLogger getLogger(){
return logger;
}
/*
* commenting out , as this is a findbug because of no charset specification.
*
*/
/*
ConsoleErrorReporter cer = new ConsoleErrorReporter(
out == null ? new PrintStream(new NullStream()) : out);
*/
public void generatedFile(String fileName, int count, int total) {
message(fileName);
}
public void message(String msg) {
if (status != null)
status.println(msg);
}
public void error(SAXParseException exception) {
//cer.error(exception);
CodeGenTypeLibraryGenerator.addExceptionsToXSDErrorList(XSDsWithError, exception, xsdTypeName);
getLogger().log(Level.SEVERE,"XJCListener Exception" , exception);
}
public void fatalError(SAXParseException exception) {
//cer.fatalError(exception);
CodeGenTypeLibraryGenerator.addExceptionsToXSDErrorList(XSDsWithError, exception, xsdTypeName);
getLogger().log(Level.SEVERE,"XJCListener Exception" , exception);
}
public void warning(SAXParseException exception) {
//cer.warning(exception);
getLogger().log(Level.WARNING,"XJCListener Exception" , exception);
}
public void info(SAXParseException exception) {
//cer.info(exception);
//getLogger().log(Level.INFO,"XJCListener Exception" , exception);
}
}
return run(args, new Listener());
}
public static int run(String[] args, @NotNull
final XJCListener listener) throws BadCommandLineException {
final OptionsEx opt = new OptionsEx();
opt.entityResolver = new SOACatalogResolver();
opt.setSchemaLanguage(Language.XMLSCHEMA); // disable auto-guessing
try {
opt.parseArguments(args);
} catch (BadCommandLineException e) {
e.initOptions(opt);
throw e;
}
final ClassLoader contextClassLoader = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(
opt.getUserClassLoader(contextClassLoader));
/*
ClassLoader codegenClassLoader = ToolsXJCWrappper.class.getClassLoader();
Thread.currentThread().setContextClassLoader(
opt.getUserClassLoader(codegenClassLoader));
*/
try {
final boolean[] hadWarning = new boolean[1];
ErrorReceiver receiver = new ErrorReceiverFilter(listener) {
public void info(SAXParseException exception) {
if (opt.verbose)
super.info(exception);
}
public void warning(SAXParseException exception) {
hadWarning[0] = true;
if (!opt.quiet)
super.warning(exception);
}
@Override
public void pollAbort() throws AbortException {
if (listener.isCanceled())
throw new AbortException();
}
};
Model model = ModelLoader.load(opt, new JCodeModel(), receiver);
if (model == null) {
return -1;
}
// generate actual code
receiver.debug("generating code");
{// don't want to hold outline in memory for too long.
Outline outline = model.generateCode(opt, receiver);
if (outline == null) {
return -1;
}
listener.compiled(outline);
}
// then print them out
try {
CodeWriter cw;
cw = opt.createCodeWriter();
model.codeModel.build(cw);
} catch (IOException e) {
receiver.error(e);
return -1;
}
return 0;
} catch (StackOverflowError e) {
if (opt.verbose)
throw e;
else {
return -1;
}
}
}
/**
* Operation mode.
*/
private static enum Mode {
// normal mode. compile the code
CODE,
// dump the signature of the generated code
SIGNATURE,
// dump DOMForest
FOREST,
// same as CODE but don't produce any Java source code
DRYRUN,
// same as CODE but pack all the outputs into a zip and dumps to stdout
ZIP,
// testing a new binding mode
GBIND
}
static class OptionsEx extends Options {
/** Operation mode. */
protected Mode mode = Mode.CODE;
/** A switch that determines the behavior in the BGM mode. */
public boolean noNS = false;
/** Parse XJC-specific options. */
public int parseArgument(String[] args, int i)
throws BadCommandLineException {
if (args[i].equals("-noNS")) {
noNS = true;
return 1;
}
if (args[i].equals("-mode")) {
i++;
String mstr = args[i].toLowerCase();
for (Mode m : Mode.values()) {
if (m.name().toLowerCase().startsWith(mstr)
&& mstr.length() > 2) {
mode = m;
return 2;
}
}
}
return super.parseArgument(args, i);
}
}
}