/*******************************************************************************
* 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.eclipse.template.wsdl.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.ebayopensource.turmeric.common.config.LibraryType;
import org.ebayopensource.turmeric.eclipse.core.model.IParameterElement;
import org.ebayopensource.turmeric.eclipse.core.resources.constants.SOAProjectConstants.TemplateBinding;
import org.ebayopensource.turmeric.eclipse.core.resources.constants.SOATypeLibraryConstants;
import org.ebayopensource.turmeric.eclipse.exception.core.CommandFailedException;
import org.ebayopensource.turmeric.eclipse.repositorysystem.core.GlobalRepositorySystem;
import org.ebayopensource.turmeric.eclipse.repositorysystem.core.SOAGlobalRegistryAdapter;
import org.ebayopensource.turmeric.eclipse.template.wsdl.resources.SOAMessages;
import org.ebayopensource.turmeric.eclipse.typelibrary.ui.TypeLibraryUIActivator;
import org.ebayopensource.turmeric.eclipse.typelibrary.ui.TypeLibraryUtil;
import org.ebayopensource.turmeric.eclipse.typelibrary.ui.wst.WTPTypeLibUtil;
import org.ebayopensource.turmeric.eclipse.utils.lang.StringUtil;
import org.eclipse.wst.wsdl.Binding;
import org.eclipse.wst.wsdl.Definition;
import org.eclipse.wst.wsdl.Input;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.MessageReference;
import org.eclipse.wst.wsdl.Operation;
import org.eclipse.wst.wsdl.Output;
import org.eclipse.wst.wsdl.Part;
import org.eclipse.wst.wsdl.PortType;
import org.eclipse.wst.wsdl.WSDLFactory;
import org.eclipse.wst.wsdl.internal.generator.BindingGenerator;
import org.eclipse.wst.wsdl.internal.generator.extension.ContentGeneratorExtensionFactoryRegistry;
import org.eclipse.wst.wsdl.ui.internal.commands.AddMessageCommand;
import org.eclipse.wst.wsdl.ui.internal.commands.AddPartCommand;
import org.eclipse.wst.wsdl.ui.internal.util.NameUtil;
import org.eclipse.wst.wsdl.ui.internal.util.XSDComponentHelper;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDCompositor;
import org.eclipse.xsd.XSDDerivationMethod;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFactory;
import org.eclipse.xsd.XSDImport;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDTypeDefinition;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* The Utility class to serve the Service template processors and other template
* classes. This class has methods to create the WSDL, manipulate it i.e add
* operation, elements etc. Basically this class manipulates the WSDL using the
* WTP EMF model and expose the manipulating functionalities SOA require. It
* also has getters for accessing the the WSDL properties like the port,
* operations, elements etc. Has the binding generation responsibility also.
*
* @author smathew
*
*/
public class ServiceTemplateUtil {
/**
* Gets the default port in a definition object. The default is the first
* port type available in the WSDL. If the definition does not have any port
* types then it creates a new one, add it to the definition and returns it.
*
* @param definition the definition
* @return the default port
*/
public static PortType getDefaultPort(Definition definition) {
PortType portType = null;
if (definition.getPortTypes() != null) {
Iterator<?> iterator = definition.getPortTypes().values().iterator();
while (iterator.hasNext()) {
Object objPortType = iterator.next();
if (objPortType != null && objPortType instanceof PortType) {
portType = (PortType) objPortType;
break;
}
}
}
if (portType == null) {
portType = WSDLFactory.eINSTANCE.createPortType();
portType.setQName(new QName(definition.getTargetNamespace(),
ServiceTemplateConstants.DEFAULT_PORT_NAME));
portType.setEnclosingDefinition(definition);
definition.addPortType(portType);
}
return portType;
}
/**
* Returns true if the operation with with given name exists in the given
* port type. This is a case sensitive API. Remember an operation with the
* same name can exist in the WSDL under a different port type and clients
* are supposed to use this API with different port types to achieve a full
* length uniqueness.
*
* @param portType the port type
* @param opName the op name
* @return true, if successful
*/
public static boolean operationExists(PortType portType, String opName) {
for (Object operationObject : portType.getOperations()) {
if (operationObject instanceof Operation) {
if (StringUtils.equals(((Operation) operationObject).getName(),
opName)) {
return true;
}
}
}
return false;
}
/**
* Adds an operation with the given name to the port type. Does not check
* for existing operations. In that case clients can use
*
* @param portType the port type
* @param operationName the operation name
* @return the operation
* @see ServiceTemplateUtil#operationExists(PortType, String) API before
* calling this API.
*/
public static Operation addOperation(PortType portType, String operationName) {
Operation operation = WSDLFactory.eINSTANCE.createOperation();
operation.setName(operationName);
operation.setEnclosingDefinition(portType.getEnclosingDefinition());
if (portType.getElement() != null && portType.getElement().getOwnerDocument() != null) {
//adding the wsdl:documentation tag to the PortType->Operation
final Document ownerDoc = portType.getElement().getOwnerDocument();
final Element element = ownerDoc.createElement(
ServiceTemplateConstants.TAG_DOCUMENTATION_ELEMENT);
element.appendChild(ownerDoc.createTextNode(
ServiceTemplateConstants.DEFAULT_DOCUMENT_TEXT));
operation.setDocumentationElement(element);
}
portType.addOperation(operation);
return operation;
}
/**
* Adds the specified input parameter to the operation, creates a message
* for it and return the part. Remember for setting the input parameter we
* need the message and part to be added as well.
*
* @param operation the operation
* @param inputParamName the input param name
* @return the part
* @throws CommandFailedException the command failed exception
*/
public static Part addInput(Operation operation, String inputParamName)
throws CommandFailedException {
MessageReference messageReference = WSDLFactory.eINSTANCE.createInput();
messageReference.setEnclosingDefinition(operation
.getEnclosingDefinition());
operation.setInput((Input) messageReference);
return createMessage(operation, inputParamName, messageReference);
}
/**
* Adds the specified output parameter to the operation, creates a message
* for it and return the part. Remember for setting the output parameter we
* need the message and part to be added as well.
*
* @param operation the operation
* @param outputParamName the output param name
* @return the part
* @throws CommandFailedException the command failed exception
*/
public static Part addOutput(Operation operation, String outputParamName)
throws CommandFailedException {
MessageReference messageReference = WSDLFactory.eINSTANCE
.createOutput();
messageReference.setEnclosingDefinition(operation
.getEnclosingDefinition());
operation.setOutput((Output) messageReference);
return createMessage(operation, outputParamName, messageReference);
}
/**
* Creates the message with the given message name. The WSDL definition is
* automatically discovered from the operation passed. Finally the part is
* also created with this message and returned to the client.
*
* @param operation the operation
* @param messageName the message name
* @param messageReference the message reference
* @return the part
* @throws CommandFailedException the command failed exception
*/
public static Part createMessage(Operation operation, String messageName,
MessageReference messageReference) throws CommandFailedException {
Definition definition = operation.getEnclosingDefinition();
AddMessageCommand command = new AddMessageCommand(definition,
messageName, false);
command.run();
messageReference.setEMessage((Message) command.getWSDLElement());
return createPart((Message) command.getWSDLElement());
}
/**
* Creates the part for the message, the name of the part is the default
* name and the name space and name are discovered from the message
* parameter. It used the WTP EMF based command internally.
*
* @param message the message
* @return the part
*/
public static Part createPart(Message message) {
AddPartCommand command = new AddPartCommand(message,
ServiceTemplateConstants.DEFAULT_PART_NAME, message.getQName()
.getNamespaceURI(), message.getQName().getLocalPart(),
false);
command.run();
return (Part) command.getWSDLElement();
}
/**
* Wrapper over the method.
*
* @param part the part
* @param operation the operation
* @param typeFolding the type folding
* @return the xSD complex type definition
* @throws CommandFailedException the command failed exception
* @see ServiceTemplateUtil#createXSDElementDefinition(String, Part, String,
* String).
*
* The input parameter element name is computed from the part name using the
* WTP API and the operation parameter. The base service request type name
* and name space are parsed from the "service_config.properties" and passed
* to the API
*/
public static XSDComplexTypeDefinition createInputComplexType(Part part,
Operation operation, boolean typeFolding)
throws CommandFailedException {
return createXSDElementDefinition(NameUtil.getPartName(operation
.getEInput()), part, GlobalRepositorySystem.instanceOf()
.getActiveRepositorySystem().getConfigurationRegistry()
.getBaseServiceRequestType(), GlobalRepositorySystem
.instanceOf().getActiveRepositorySystem()
.getConfigurationRegistry().getBaseServiceRequestNameSpace(),
typeFolding, operation.getEnclosingDefinition()
.getTargetNamespace());
}
/**
* Wrapper over the method.
*
* @param part the part
* @param operation the operation
* @param typeFolding the type folding
* @return the xSD complex type definition
* @throws CommandFailedException the command failed exception
* @see ServiceTemplateUtil#createXSDElementDefinition(String, Part, String,
* String).
*
* The output parameter element name is computed from the part name using
* the WTP API and the operation parameter. The base service response type
* name and name space are parsed from the "service_config.properties" and
* passed to the API
*/
public static XSDComplexTypeDefinition createOutputComplexType(Part part,
Operation operation, boolean typeFolding)
throws CommandFailedException {
return createXSDElementDefinition(NameUtil.getPartName(operation
.getEOutput()), part, GlobalRepositorySystem.instanceOf()
.getActiveRepositorySystem().getConfigurationRegistry()
.getBaseServiceResponseType(), GlobalRepositorySystem
.instanceOf().getActiveRepositorySystem()
.getConfigurationRegistry().getBaseServiceResponseNameSpace(),
typeFolding, operation.getEnclosingDefinition()
.getTargetNamespace());
}
/**
* Add the element declaration based on the parameter element passed. The
* default w3c data types will carry the prefix from corresponding to w3c
* name space and rest all will be computed and if the prefix does not exist
* then it is added before using it.
*
* @param parameterElement the parameter element
* @param complexTypeDefinition the complex type definition
* @param typeFolding the type folding
* @throws CommandFailedException the command failed exception
*/
public static void addElementDeclaration(
IParameterElement parameterElement,
XSDComplexTypeDefinition complexTypeDefinition, boolean typeFolding)
throws CommandFailedException {
String prefixedElementType = "";
if (parameterElement.getDatatype() instanceof String) {
prefixedElementType = TypeLibraryUIActivator.getPrefix(complexTypeDefinition
.getSchema(), SOATypeLibraryConstants.W3C_NAMEPSACE)
+ (String) parameterElement.getDatatype();
} else {
try {
LibraryType libElementType = (LibraryType) parameterElement
.getDatatype();
String elementName = libElementType.getName();
String elementNamespace = typeFolding ? complexTypeDefinition
.getTargetNamespace() : TypeLibraryUtil
.getNameSpace(libElementType);
prefixedElementType = TypeLibraryUIActivator.getPrefix(
complexTypeDefinition.getSchema(), elementNamespace)
+ elementName;
} catch (Exception e) {
throw new CommandFailedException(StringUtil.formatString(
SOAMessages.TYPE_NOT_FOUND, parameterElement
.getDatatype()));
}
}
XSDParticle xsdParticle = TypeLibraryUIActivator.createXSDElementDeclaration(
parameterElement.getName(), prefixedElementType,
parameterElement.getMinOccurs(), parameterElement
.getMaxOccurs());
XSDModelGroup xsdModelGroup = TypeLibraryUIActivator
.getModelGroup(complexTypeDefinition);
xsdModelGroup.getContents().add(xsdParticle);
}
/**
* Adds a binding to the definition model being used. Here we have both SOAP
* and HTTP bindings generation logic embedded. First we parse all the
* bindings to see if we have an existing binding. If we don't have one the
* we will add it. Note there is no hard coding for HTTP or SOAP here, but
* is there in the UI and we just consult the WTP model before adding it.
*
* @param definition the definition
* @param bindings the bindings
* @return the binding
*/
public static Binding addBinding(Definition definition,
Set<TemplateBinding> bindings) {
Binding binding = null;
if (definition.getBindings() != null) {
Iterator iterator = definition.getBindings().values().iterator();
while (iterator.hasNext()) {
Object objBinding = iterator.next();
if (objBinding != null && objBinding instanceof Binding) {
binding = (Binding) objBinding;
BindingGenerator bindingGenerator = new BindingGenerator(
definition, binding);
for (TemplateBinding templBinding : bindings) {
// This is bad we are identifying the type
// of binding (ie if it is http or soap)
// by the name
if (StringUtils.containsIgnoreCase(binding.getQName()
.getLocalPart(), templBinding.name())) {
if (bindingGenerator.getContentGenerator() == null) {
bindingGenerator.setOverwrite(true);
bindingGenerator
.setContentGenerator(ContentGeneratorExtensionFactoryRegistry
.getInstance()
.getGeneratorClassFromName(
templBinding.name()));
}
bindingGenerator.generateBinding();
}
}
}
}
}
if (binding == null) {
binding = WSDLFactory.eINSTANCE.createBinding();
binding.setQName(new QName(definition.getTargetNamespace(),
ServiceTemplateConstants.DEFAULT_BINDING_NAME));
binding.setEnclosingDefinition(definition);
definition.addBinding(binding);
BindingGenerator bindingGenerator = new BindingGenerator(
definition, binding);
bindingGenerator.generateBinding();
}
return binding;
}
/**
* Creates an element definition with the given element name. The parent
* type of this definition would be the base element name parameter and the
* name space is the base element name space. THe definition is derived from
* the part and the anonymous type is also created based on the part. This
* API also makes sure that the parent type is imported before usage.
*
* @param elementName the element name
* @param part the part
* @param baseElementName the base element name
* @param baseElementNameSpace the base element name space
* @param typeFolding the type folding
* @param targetNameSpace the target name space
* @return the xSD complex type definition
*/
public static XSDComplexTypeDefinition createXSDElementDefinition(
String elementName, Part part, String baseElementName,
String baseElementNameSpace, boolean typeFolding,
String targetNameSpace) {
XSDElementDeclaration elementDef = XSDComponentHelper
.createAnonymousXSDElementDefinition(elementName, part);
XSDComplexTypeDefinition complexType = XSDFactory.eINSTANCE
.createXSDComplexTypeDefinition();
complexType.setName(StringUtils.capitalize(elementName));
complexType.setTargetNamespace(part.getEnclosingDefinition()
.getTargetNamespace());
XSDParticle newXSDParticle = XSDFactory.eINSTANCE.createXSDParticle();
XSDModelGroup newXSDModelGroup = XSDFactory.eINSTANCE
.createXSDModelGroup();
newXSDModelGroup.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
newXSDParticle.setContent(newXSDModelGroup);
complexType.setContent(newXSDParticle);
elementDef.setTypeDefinition(complexType);
XSDComplexTypeDefinition complexTypeDefition = XSDComponentHelper
.createXSDComplexTypeDefiniion(complexType.getName(), part);
XSDComplexTypeDefinition restriction = XSDFactory.eINSTANCE
.createXSDComplexTypeDefinition();
if (!StringUtils.isEmpty(baseElementName)
&& !StringUtils.isEmpty(baseElementNameSpace)) {
if (!typeFolding)
restriction.setName(TypeLibraryUIActivator.getPrefix(complexTypeDefition
.getSchema(), baseElementNameSpace)
+ baseElementName);
else
restriction.setName(TypeLibraryUIActivator.getPrefix(complexTypeDefition
.getSchema(), targetNameSpace)
+ baseElementName);
complexTypeDefition
.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
complexTypeDefition.setBaseTypeDefinition(restriction);
}
TypeLibraryUIActivator.addDocumentation(complexTypeDefition, "Document goes here");
return complexTypeDefition;
}
/**
* Computes the dependency of the parameter library type and in line the
* WSDL with the computed types. Duplicate types will be ignored. This is
* more a Wrapper over the method
*
* @param libraryType the library type
* @param definition the definition
* @param typeFolding the type folding
* @throws Exception the exception
* @see WTPTypeLibUtil#inlineType(LibraryType, Definition, boolean, boolean)
*/
public static void inlineTypesInWSDL(LibraryType libraryType,
Definition definition, boolean typeFolding) throws Exception {
ArrayList<LibraryType> librarylist = new ArrayList<LibraryType>();
librarylist.add(libraryType);
for (LibraryType loopLibraryType : SOAGlobalRegistryAdapter.getInstance()
.getGlobalRegistry().getDependentParentTypeFiles(libraryType)) {
librarylist.add(loopLibraryType);
}
for (LibraryType selectedType : librarylist) {
WTPTypeLibUtil.inlineType(selectedType, definition, true,
typeFolding);
}
}
private static void addImportDirective(XSDTypeDefinition typeDefinition,
String libElementNamespace) {
if (!StringUtil.broadEquals(typeDefinition.getSchema()
.getTargetNamespace(), libElementNamespace)
&& !importExists(typeDefinition.getSchema(),
libElementNamespace)) {
XSDImport xsdSchemaDirective = XSDFactory.eINSTANCE
.createXSDImport();
xsdSchemaDirective.setNamespace(libElementNamespace);
typeDefinition.getSchema().getContents().add(0, xsdSchemaDirective);
}
}
private static boolean importExists(XSDSchema schema,
String libElementNamespace) {
for (Iterator i = schema.getContents().iterator(); i.hasNext();) {
Object o = i.next();
if (o instanceof XSDImport) {
if (StringUtil.broadEquals(((XSDImport) o).getNamespace(),
libElementNamespace)) {
return true;
}
}
}
return false;
}
}