/*******************************************************************************
* 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.codegen.builders;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.runtime.common.impl.internal.schema.BaseTypeDefsBuilder;
import org.ebayopensource.turmeric.runtime.common.impl.internal.schema.FlatSchemaComplexTypeImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.schema.FlatSchemaElementDeclImpl;
import org.ebayopensource.turmeric.runtime.common.impl.internal.utils.ServiceNameUtils;
import org.ebayopensource.turmeric.runtime.common.types.SOAFrameworkCommonTypeDefsBuilder;
import org.ebayopensource.turmeric.tools.codegen.CodeGenContext;
import org.ebayopensource.turmeric.tools.codegen.CodeGenInfoFinder;
import org.ebayopensource.turmeric.tools.codegen.InputOptions;
import org.ebayopensource.turmeric.tools.codegen.JTypeTable;
import org.ebayopensource.turmeric.tools.codegen.SourceGenerator;
import org.ebayopensource.turmeric.tools.codegen.InputOptions.InputType;
import org.ebayopensource.turmeric.tools.codegen.exception.BadInputValueException;
import org.ebayopensource.turmeric.tools.codegen.exception.CodeGenFailedException;
import org.ebayopensource.turmeric.tools.codegen.exception.UnsupportedSchemaException;
import org.ebayopensource.turmeric.tools.codegen.schema.FlatSchemaLoader;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenConstants;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
import org.ebayopensource.turmeric.tools.codegen.util.IntrospectUtil;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;
/**
* Generates TypeDefs.java based on the XSD or WSDL
*
* @author ichernyshev
*/
public class TypeDefsBuilderGenerator extends BaseCodeGenerator implements SourceGenerator {
private JCodeModel m_codeModel;
private JDefinedClass m_targetClass;
private Map<String,JFieldVar> m_namespaces = new HashMap<String,JFieldVar>();
public static TypeDefsBuilderGenerator getInstance() {
return new TypeDefsBuilderGenerator();
}
public boolean continueOnError() {
return true;
}
public void generate(CodeGenContext codeGenCtx) throws CodeGenFailedException {
JTypeTable jTypeTable = codeGenCtx.getJTypeTable();
InputOptions inputOptions = codeGenCtx.getInputOptions();
FlatSchemaLoader loader = null;
Set<Class<?>> allTypes = jTypeTable.getTypesReferred();
if (allTypes != null && !allTypes.isEmpty()) {
// If it is not a wsdl based service, try to find the schema file first, if
// schema file is not found, go find the wsdl file.
if (!inputOptions.isWSDLBasedService()) {
String schemaDir = XMLSchemaGenerator.getSchemaFileDir(codeGenCtx);
File schemaDirFile = new File(schemaDir);
schemaDirFile = schemaDirFile.getAbsoluteFile();
File[] files = schemaDirFile.listFiles();
if (files != null && files.length != 0) {
try {
loader = FlatSchemaLoader.createInstanceFromXsd(files);
} catch (UnsupportedSchemaException e) {
throw new CodeGenFailedException("Unable to parse XSD files in '" +
schemaDirFile.getPath() + "': " + e.toString(), e);
}
}
}
if (loader == null) {
String wsdlFilename = codeGenCtx.getWSDLURI();
try {
if (null == wsdlFilename) {
String metaSrcDestLoc = inputOptions.getMetaSrcDestLocation();
String destLocation = inputOptions.getDestLocation();
if (CodeGenUtil.isEmptyString(destLocation))
destLocation = "."; //defaulting
if (CodeGenUtil.isEmptyString(metaSrcDestLoc)) {
// set it to default
String metaSrc = inputOptions.getOriginalInputType().equals(InputType.WSDL) ? CodeGenConstants.META_SRC_FOLDER : CodeGenConstants.GEN_META_SRC_FOLDER;
metaSrcDestLoc = CodeGenUtil.genDestFolderPath(destLocation, metaSrc);
//defaulting to the current directory if values for options -mdest and -dest are null(or empty).
}
wsdlFilename = CodeGenUtil.toOSFilePath(metaSrcDestLoc);
wsdlFilename += CodeGenInfoFinder.getPathforNonModifiableArtifact(inputOptions.getServiceAdminName(), "WSDL");
}
loader = FlatSchemaLoader.createInstanceFromWsdl(wsdlFilename);
} catch (BadInputValueException e) {
// No-op. This is never thrown.
}
catch (UnsupportedSchemaException e) {
throw new CodeGenFailedException("Unable to parse WSDL '" + wsdlFilename +
"': " + e.toString(), e);
}
}
} else {
loader = FlatSchemaLoader.createEmptyInstance();
}
m_codeModel = new JCodeModel();
Class<?> serviceInterfaceClass = jTypeTable.getClazz();
String targetClassName = getTargetClassName(codeGenCtx, serviceInterfaceClass.getName());
m_targetClass = createNewClass(m_codeModel, targetClassName);
extend(m_targetClass, BaseTypeDefsBuilder.class);
JMethod allComplexTypesMethod = addAllComplexTypesMethod();
if(loader != null)
addAllComplexTypesLogic(allComplexTypesMethod, loader);
addJavaDocs(m_targetClass);
generateJavaFile(codeGenCtx, m_targetClass, CodeGenConstants.CLIENT_GEN_FOLDER);
if (codeGenCtx.getInputOptions().isNoCompile() == false) {
compileJavaFilesNoException(
codeGenCtx.getGeneratedJavaFiles(),
codeGenCtx.getBinLocation());
}
}
private String getTargetClassName(CodeGenContext codeGenCtx, String className) {
QName serviceQName = codeGenCtx.getServiceQName();
String dispatcherClassName = ServiceNameUtils.getServiceTypeDefsBuilderClassName(
serviceQName.getLocalPart(), className);
return dispatcherClassName;
}
private JMethod addAllComplexTypesMethod()
throws CodeGenFailedException
{
String methodName = "build";
Method baseMethod = IntrospectUtil.getMethodWithSignature(
BaseTypeDefsBuilder.class, methodName, null);
if (baseMethod == null) {
throw new CodeGenFailedException(
"No 'build' method defined in BaseTypeDefsBuilder");
}
Type[] paramTypes = new Type[0];
JMethod newMethod = addMethod(m_targetClass, methodName, paramTypes,
baseMethod.getGenericExceptionTypes(),
baseMethod.getGenericReturnType());
return newMethod;
}
private void addAllComplexTypesLogic(JMethod method, FlatSchemaLoader loader)
throws CodeGenFailedException
{
JBlock methodBody = method.body();
// create complexTypes list
JClass complexTypeClass = m_codeModel.ref(FlatSchemaComplexTypeImpl.class);
JClass complexTypesVarType = m_codeModel.ref(ArrayList.class).narrow(complexTypeClass);
JVar complexTypesVar = methodBody.decl(complexTypesVarType, "complexTypes", JExpr._new(complexTypesVarType));
// create all FlatSchemaComplexTypeImpl instances and add to the result list
List<FlatSchemaComplexTypeImpl> complexTypes = loader.getComplexTypes();
addComplexTypesPopulationLogic(methodBody, complexTypeClass, complexTypesVar, complexTypes);
methodBody.directStatement(" ");
// add all elements
if (!complexTypes.isEmpty()) {
addComplexTypeElementsLogic(methodBody, complexTypeClass, complexTypesVar, complexTypes);
methodBody.directStatement(" ");
}
// create rootElements list
JClass elementDeclClass = m_codeModel.ref(FlatSchemaElementDeclImpl.class);
JClass rootElementsVarType = m_codeModel.ref(HashMap.class).narrow(
new JClass[] {m_codeModel.ref(QName.class), elementDeclClass});
JVar rootElementsVar = methodBody.decl(rootElementsVarType, "rootElements", JExpr._new(rootElementsVarType));
// add all root elements
Map<QName,FlatSchemaElementDeclImpl> rootElements = loader.getRootElements();
addRootElementsPopulationLogic(methodBody, elementDeclClass, rootElementsVar,
complexTypesVar, rootElements, complexTypes);
methodBody.directStatement(" ");
//adding common types
//generates: SOAFrameworkCommonTypeDefsBuilder.includeTypeDefs(complexTypes, rootElements);
JClass commonTypeDefsBuilderClass = m_codeModel.ref(SOAFrameworkCommonTypeDefsBuilder.class);
JInvocation includeTypeDefsInvo = commonTypeDefsBuilderClass.staticInvoke("includeTypeDefs");
includeTypeDefsInvo.arg(complexTypesVar);
includeTypeDefsInvo.arg(rootElementsVar);
methodBody.add(includeTypeDefsInvo);
methodBody.directStatement(" ");
// copy variable pointers
methodBody.directStatement("m_complexTypes = complexTypes;");
methodBody.directStatement("m_rootElements = rootElements;");
}
private void addComplexTypesPopulationLogic(JBlock outerMethodBody,
JClass complexTypeClass, JVar outerComplexTypesVar,
List<FlatSchemaComplexTypeImpl> complexTypes)
{
JVar complexTypesVar = outerComplexTypesVar;
JBlock currentBlock = outerMethodBody;
for (int i=0; i<complexTypes.size(); i++) {
if ((i % 100) == 0) {
JMethod newMethod = m_targetClass.method(JMod.PRIVATE,
m_codeModel.VOID, "addComplexTypes" + (i/100));
complexTypesVar = newMethod.param(outerComplexTypesVar.type(), "complexTypes");
currentBlock = newMethod.body();
JInvocation newMethodCall = outerMethodBody.invoke(newMethod);
newMethodCall.arg(outerComplexTypesVar);
}
FlatSchemaComplexTypeImpl complexType = complexTypes.get(i);
QName typeName = complexType.getTypeName();
JInvocation newComplexTypeExpr;
if (typeName != null) {
// construct type QName
JExpression newQNameExpr = getQNameExpression(typeName);
// call FlatSchemaComplexTypeImpl constructor
newComplexTypeExpr = JExpr._new(complexTypeClass);
newComplexTypeExpr.arg(newQNameExpr);
} else {
// anonymous complex type
newComplexTypeExpr = JExpr._new(complexTypeClass);
}
// add to the result list
JInvocation addToResultExpr = complexTypesVar.invoke("add");
addToResultExpr.arg(newComplexTypeExpr);
addComplexTypeComment(currentBlock, i, complexType);
currentBlock.add(addToResultExpr);
}
}
private void addComplexTypeComment(JBlock currentBlock, int index,
FlatSchemaComplexTypeImpl complexType)
{
QName typeQName = complexType.getTypeName();
String typeName = (typeQName != null ? typeQName.getLocalPart() : "<Anonymous>");
currentBlock.directStatement("// Type #" + index + " (" + typeName + ")");
}
private void addComplexTypeElementsLogic(JBlock outerMethodBody,
JClass complexTypeClass, JVar outerComplexTypesVar,
List<FlatSchemaComplexTypeImpl> complexTypes) throws CodeGenFailedException
{
int nextSubmethodNo = 0;
int statementCount = 0;
JVar complexTypesVar = outerComplexTypesVar;
JBlock currentBlock = outerMethodBody;
JVar currTypeVar = null;
for (int i=0; i<complexTypes.size(); i++) {
if (currTypeVar == null || statementCount >= 200) {
JMethod newMethod = m_targetClass.method(JMod.PRIVATE,
m_codeModel.VOID, "addComplexTypeElements" + nextSubmethodNo);
complexTypesVar = newMethod.param(outerComplexTypesVar.type(), "complexTypes");
currentBlock = newMethod.body();
currTypeVar = currentBlock.decl(complexTypeClass, "currType");
nextSubmethodNo++;
statementCount = 0;
JInvocation newMethodCall = outerMethodBody.invoke(newMethod);
newMethodCall.arg(outerComplexTypesVar);
}
FlatSchemaComplexTypeImpl complexType = complexTypes.get(i);
currentBlock.directStatement(" ");
addComplexTypeComment(currentBlock, i, complexType);
List<FlatSchemaElementDeclImpl> elements = complexType.getElements();
if (elements.isEmpty()) {
currentBlock.directStatement("// type has no child elements");
continue;
}
// get current FlatSchemaComplexTypeImpl instance
JInvocation getFromResultExpr = complexTypesVar.invoke("get");
getFromResultExpr.arg(JExpr.lit(i));
currentBlock.assign(currTypeVar, getFromResultExpr);
for (int j=0; j<elements.size(); j++) {
FlatSchemaElementDeclImpl element = elements.get(j);
QName elementName=null;
//for anonymous complex Type .Need to have a check.
if(CodeGenUtil.isEmptyString(element.getName().getNamespaceURI()) && complexType.getTypeName()!=null)
elementName = new QName(complexType.getTypeName().getNamespaceURI(),element.getName().getLocalPart());
else
elementName = element.getName();
// construct element QName
JExpression newQNameExpr = getQNameExpression(elementName);
if (!element.isComplexType()) {
JInvocation addSimpleElementExpr;
if (element.isAttribute()) {
// add attribute to the current complex type
addSimpleElementExpr = currTypeVar.invoke("addAttribute");
addSimpleElementExpr.arg(newQNameExpr);
} else {
// add simple element to the current complex type
addSimpleElementExpr = currTypeVar.invoke("addSimpleElement");
addSimpleElementExpr.arg(newQNameExpr);
addSimpleElementExpr.arg(JExpr.lit(element.getMaxOccurs()));
}
currentBlock.add(addSimpleElementExpr);
statementCount++;
continue;
}
JInvocation getOtherFromResultExpr = addOtherComplexTypeGetInvocation(
element, complexTypesVar, complexTypes);
JInvocation addComplexElementExpr = currTypeVar.invoke("addComplexElement");
addComplexElementExpr.arg(newQNameExpr);
addComplexElementExpr.arg(getOtherFromResultExpr);
addComplexElementExpr.arg(JExpr.lit(element.getMaxOccurs()));
currentBlock.add(addComplexElementExpr);
statementCount++;
}
}
}
private JInvocation addOtherComplexTypeGetInvocation(FlatSchemaElementDeclImpl element,
JVar complexTypesVar, List<FlatSchemaComplexTypeImpl> complexTypes)
throws CodeGenFailedException
{
int otherComplexTypeIdx = getComplexTypeIndex(complexTypes,
element.getComplexType(), element.getName());
JInvocation getOtherFromResultExpr = complexTypesVar.invoke("get");
getOtherFromResultExpr.arg(JExpr.lit(otherComplexTypeIdx));
return getOtherFromResultExpr;
}
private void addRootElementsPopulationLogic(JBlock outerMethodBody,
JClass elementDeclClass, JVar outerRootElementsVar, JVar outerComplexTypesVar,
Map<QName,FlatSchemaElementDeclImpl> rootElements,
List<FlatSchemaComplexTypeImpl> complexTypes) throws CodeGenFailedException
{
int lineNo = 0;
JVar complexTypesVar = outerComplexTypesVar;
JVar rootElementsVar = outerRootElementsVar;
JBlock currentBlock = outerMethodBody;
for (Map.Entry<QName,FlatSchemaElementDeclImpl> e: rootElements.entrySet()) {
if ((lineNo % 100) == 0) {
JMethod newMethod = m_targetClass.method(JMod.PRIVATE,
m_codeModel.VOID, "addRootElements" + (lineNo/100));
complexTypesVar = newMethod.param(outerComplexTypesVar.type(), "complexTypes");
rootElementsVar = newMethod.param(outerRootElementsVar.type(), "rootElements");
currentBlock = newMethod.body();
JInvocation newMethodCall = outerMethodBody.invoke(newMethod);
newMethodCall.arg(outerComplexTypesVar);
newMethodCall.arg(outerRootElementsVar);
}
QName elementName = e.getKey();
FlatSchemaElementDeclImpl element = e.getValue();
// construct element QName
JExpression newQNameExpr = getQNameExpression(elementName);
JInvocation createElementExpr;
if (!element.isComplexType()) {
if (element.isAttribute()) {
throw new CodeGenFailedException("Internal error - attribute '" +
element.getName() + "' should not be at the root level");
} else {
// add attribute to the current complex type
createElementExpr = elementDeclClass.staticInvoke("createRootSimpleElement");
createElementExpr.arg(newQNameExpr);
}
} else {
JInvocation getOtherFromResultExpr = addOtherComplexTypeGetInvocation(
element, complexTypesVar, complexTypes);
createElementExpr = elementDeclClass.staticInvoke("createRootComplexElement");
createElementExpr.arg(newQNameExpr);
createElementExpr.arg(getOtherFromResultExpr);
}
JInvocation putRootElementExpr = rootElementsVar.invoke("put");
putRootElementExpr.arg(newQNameExpr);
putRootElementExpr.arg(createElementExpr);
currentBlock.add(putRootElementExpr);
lineNo++;
}
}
private JExpression getNamespaceExpression(String namespace) {
if (namespace == null || namespace.length() == 0) {
return JExpr._null();
}
JFieldVar var = m_namespaces.get(namespace);
if (var != null) {
return var;
}
// optimize namespace generation by caching instances in the statics
int FIELD_MODS = (JMod.PRIVATE | JMod.STATIC | JMod.FINAL);
var = m_targetClass.field(FIELD_MODS, m_codeModel.ref(String.class),
"NS" + (m_namespaces.size()+1), JExpr.lit(namespace));
m_namespaces.put(namespace, var);
return var;
}
private JExpression getQNameExpression(QName name) {
JInvocation qnameNew = JExpr._new(m_codeModel.ref(QName.class));
qnameNew.arg(getNamespaceExpression(name.getNamespaceURI()));
qnameNew.arg(JExpr.lit(name.getLocalPart()));
return qnameNew;
}
private int getComplexTypeIndex(List<FlatSchemaComplexTypeImpl> list,
FlatSchemaComplexTypeImpl elem, QName outerName) throws CodeGenFailedException
{
for (int i=0; i<list.size(); i++) {
if (list.get(i) == elem) {
return i;
}
}
throw new CodeGenFailedException("Internal error - unable to find " +
"complex type '" + elem.getTypeName() +
"' referred within element '" + outerName + "'");
}
private TypeDefsBuilderGenerator() {
}
public String getFilePath(String serviceAdminName, String interfaceName) {
// TODO Auto-generated method stub
return null;
}
}