/*******************************************************************************
* 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.external.wsdl.parser;
/**
* A single Namespace wsdl conversion to MultiNamespace Wsdl.
* @author aupadhay
*
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.tools.codegen.exception.CodeGenFailedException;
import org.ebayopensource.turmeric.tools.codegen.util.ClonedSchemaNodeInfo;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
public class WSDLConversionToSingleNamespace {
private static final String XML_SCHEMA = "schema";
private static final String XML_BASE = "base";
private static final String VALUE_YES = "yes";
private static final String SOURCE_TAG = "typeLibrarySource";
private static final String SOURCE_NAMESPACE = "namespace";
private static final String XML_TARGETNAMESPACE = "targetNamespace";
private static final String XML_COMPLEXTYPE = "complexType";
private static final String XML_SIMPLETYPE = "simpleType";
private static final String TYPELIBRARY_TAG = "library";
private static final String XMLNS_TAG = "xmlns:";
private static final String NAMESPACECOLON = ":";
private static final String TYPE_TAG = "type";
private static final String NAME_TAG = "name";
private static final String XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
private static final String IMPORT_TAG = "import";
private static final String APPINFO_TAG = "appinfo";
private static final String ANNOTATION_TAG = "annotation";
private static String XML_DEFINITION = "definitions";
private static Logger s_logger = LogManager.getInstance(WSDLConversionToSingleNamespace.class);
private Set<Node> m_AllClonedNodes = new HashSet<Node>();
private Map<Element, Set<String>> m_ElementToAddedImportsMap = new HashMap<Element, Set<String>>();
private Set<String> m_prefixAdded = new HashSet<String>();
private Set<String> m_allNamespaces = new HashSet<String>();
private List<ClonedSchemaNodeInfo> m_AllClonedNodeswithAdditionalInfo = new ArrayList<ClonedSchemaNodeInfo>();
private Set<String> m_AllTypes = new HashSet<String>();
private Map<String, String> m_TypeWithNamespaceMapOrig = new HashMap<String, String>();
private Map<String, String> m_TypeWithNamespaceMapFinal = new HashMap<String, String>();
private Map<String, Set<Node>> m_typeToNodeMap = new HashMap<String, Set<Node>>();
private Map<String, Node> m_prefixToSchemaNodeMap = new HashMap<String, Node>();
private Map<String, String> m_prefixToNamespaceMap = new HashMap<String, String>();
private Set<Node> m_SchemaNodesSet = new HashSet<Node>();
private Document m_Document;
private Node m_schemaNode;
private String m_WsdlNamespace;
private Node mWsdlDefNode;
public WSDLConversionToSingleNamespace() {
}
/**
* This method creates a new wsdl with multipleNamespaces in the specified
* location
*
* @param originalWsdlFileLocation -
* the original wsdlfile location
* @param newWsdlLocation
* -new location where converted wsdl needs to be created.
* @throws CodeGenFailedException
*/
public void convertWSDL(String originalWsdlFileLocation,
String newWsdlLocation) throws CodeGenFailedException {
//check if the wsdl passed has only one namespace
try {
boolean isValid = WSDLConversionToSingleNsHelper.isValidWsdl(originalWsdlFileLocation);
if(!isValid)
{
s_logger.log(Level.SEVERE,"INVALID INPUT: WSDL does not have single Namespace");
throw new CodeGenFailedException("WSDL does not have single Namespace. When enable namespace folding is true, WSDL should not have multiple namespace");
}
}catch (Exception e) {
throw new CodeGenFailedException(e.getMessage(), e);
}
s_logger.log(Level.INFO,
"BEGIN:WSDL ConversionToMultiPleNamespace......");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
s_logger.log(Level.FINE, "Creating a new Document builder..");
builder = factory.newDocumentBuilder();
m_Document = builder.parse(originalWsdlFileLocation);
s_logger.log(Level.FINE, "Parsed the original wsdl file...");
NodeList nodelist = m_Document.getElementsByTagName("*");
for (int i = 0; i < nodelist.getLength(); i++) {
Node node = nodelist.item(i);
if (node.getNodeName().contains(XML_DEFINITION)) {
m_WsdlNamespace = node.getAttributes().getNamedItem(
XML_TARGETNAMESPACE).getNodeValue();
mWsdlDefNode = node;
}
if (node.getNodeName().contains(XML_SCHEMA)) {
m_schemaNode = node;
s_logger.log(Level.FINE,
"Traversing schema Section of the wsdl");
// only schema section inside a wsdl needs to be visited.
break;
}
}
s_logger.log(Level.INFO, "recursivelyVisiting Schema......");
recurseSchemSection(m_schemaNode);
//Need to add the additionalSchemas
s_logger.log(Level.INFO, "addingAdditional Schema......");
addAdditionalSchemas();
//prefix to be added to <wsdl:definitions> tag
addPrefix();
s_logger.log(Level.INFO, "changing prefixesForAdded Schema......");
//Some prefix needs to be changed and some needs to be added in the new wsdl.
checkPrefixForNewWsdl();
// need to add import seperately for the original xsd
// Currently imports are added for each schema type created.
//addImports((Element)m_schemaNode, m_WsdlNamespace);
writeNewwsdl(newWsdlLocation);
s_logger.log(Level.INFO,
"END:WSDL ConversionToMultiPleNamespace......");
} catch (Exception e) {
s_logger.log(Level.SEVERE, e.getMessage());
throw new CodeGenFailedException(e.getMessage());
}
}
private void recurseSchemSection(Node node) throws CodeGenFailedException, DOMException {
for(int i=0;i<node.getChildNodes().getLength();i++)
recursivelyVisitNode(node);
}
/**
* add the prefix for new namespaces.
*/
private void addPrefix() {
Iterator<String> newNamespacesAdded = m_allNamespaces.iterator();
while (newNamespacesAdded.hasNext()) {
String namespace = newNamespacesAdded.next();
String prefix = namespace.substring(namespace.lastIndexOf("/") + 1)
.toLowerCase();
String actualPrefix = prefix;
int i=1;
while(m_prefixAdded.contains(prefix))
{
prefix = actualPrefix.concat(String.valueOf(i));
i++;
}
m_prefixAdded.add(prefix);
m_prefixToNamespaceMap.put(prefix, namespace);
String attribute = XMLNS_TAG + prefix;
((Element) mWsdlDefNode).setAttribute(attribute, namespace);
}
}
/**
* check if the earlier prefix needs to be changed after schema section is changed.
*/
private void checkPrefixForNewWsdl() {
Iterator<String> allTypes = m_AllTypes.iterator();
while (allTypes.hasNext()) {
String typeName = allTypes.next();
// check if this type has been moved to new namespace
if(typeName.contains(NAMESPACECOLON))
{
typeName = typeName.substring(typeName.lastIndexOf(NAMESPACECOLON)+1);
}
if (m_TypeWithNamespaceMapFinal.containsKey(typeName)) {
// get the proper set of nodea whose prefix needs to be changed
// now
Set<Node> tobeChangedNodes = m_typeToNodeMap.get(typeName);
Iterator<Node> itr = tobeChangedNodes.iterator();
while (itr.hasNext()) {
Node node = itr.next();
String newNamespace = m_TypeWithNamespaceMapFinal
.get(typeName);
// prefix used for a particular namespace
String prefix = getprefixUsedForAddedNamespace(newNamespace);
String TypeValue = prefix + NAMESPACECOLON + typeName;
Node schemaNode = getProperSchemaNode(node);
//import statement to be added if there is a prefix change and targetNamespace of schema is diff.
if(isTargetNamespaceDifferent(schemaNode,newNamespace))
addImports((Element)schemaNode, newNamespace);
addProperType(node, TypeValue);
m_prefixToSchemaNodeMap.put(prefix, schemaNode);
}
}
}
}
private String getprefixUsedForAddedNamespace(String newNamespace) {
String associatedPrefix = null;
Iterator<Entry<String, String>> itr= m_prefixToNamespaceMap.entrySet().iterator();
while(itr.hasNext())
{
Entry<String, String> currentEntry = itr.next();
if(currentEntry.getValue().equals(newNamespace))
associatedPrefix = currentEntry.getKey();
}
return associatedPrefix;
}
private void addProperType(Node node, String TypeValue) {
if ((!((Element) node).getAttribute(XML_BASE).equals(""))
&& ((Element) node).getAttribute(TYPE_TAG).equals("")) {
((Element) node).setAttribute(XML_BASE, TypeValue);
return;
}
if (((Element) node).getAttribute(XML_BASE).equals("")
&& (!((Element) node).getAttribute(TYPE_TAG).equals(""))) {
((Element) node).setAttribute(TYPE_TAG, TypeValue);
return;
}
if (((Element) node).getAttribute(XML_BASE).equals("")
&& ((Element) node).getAttribute(TYPE_TAG).equals("")) {
((Element) node).setAttribute(XML_BASE, TypeValue);
((Element) node).setAttribute(TYPE_TAG, TypeValue);
return;
}
}
/**
* this method adds <import> tag inside the root passed.
* @param schemaElement- root
* @param Namespace-Namespace attribute
*/
private void addImports(Element schemaElement, String Namespace) {
s_logger.log(Level.FINE, "adding importStatements.....");
NamedNodeMap attrMap = schemaElement.getAttributes();
String prefix = null;
String importElementName = null;
for (int i = 0; i < attrMap.getLength(); i++) {
if (attrMap.item(i).getNodeValue().equals(XML_SCHEMA_NAMESPACE)) {
prefix = attrMap.item(i).getNodeName();
}
}
if(prefix==null)
{
String nodeName = schemaElement.getNodeName();
prefix = nodeName.substring(0, nodeName.indexOf(NAMESPACECOLON));
}
//if prefix extracted from above logic is xmlns thn import should not have any prefix.
if( prefix.equals("xmlns"))
importElementName = IMPORT_TAG;
else
importElementName = prefix.substring(prefix
.indexOf(NAMESPACECOLON) + 1)
+ NAMESPACECOLON + IMPORT_TAG;
//check needs to be done if a particulat <import namespace=""> has already been added for a particular schema.
boolean isImportWrittenBefore = m_ElementToAddedImportsMap.containsKey(schemaElement);
if(!isImportWrittenBefore)
{
//add import
writeImportstatement(importElementName,Namespace,schemaElement);
}
else
{
boolean isImportWrittenForPrefix = m_ElementToAddedImportsMap.get(schemaElement).contains(Namespace);
if(!isImportWrittenForPrefix)
writeImportstatement(importElementName,Namespace,schemaElement);
}
}
/**
* This methods adds an import statement at the start of a schema passed to it.
* @param importElementName - importElement statement
* @param namespace - Nmaespace that is to be added inside <import>
* @param schemaElement - import will be added to this schemaElement
*/
private void writeImportstatement(String importElementName,
String namespace, Element schemaElement) {
Element importElement = m_Document.createElement(importElementName);
importElement.setAttribute(SOURCE_NAMESPACE, namespace);
schemaElement.insertBefore(importElement, schemaElement.getFirstChild());
if(m_ElementToAddedImportsMap.containsKey(schemaElement))
{
m_ElementToAddedImportsMap.get(schemaElement).add(namespace);
}
else
{
Set<String> newSet = new HashSet<String>();
newSet.add(namespace);
m_ElementToAddedImportsMap.put(schemaElement, newSet);
}
}
/**
*
* @param node - CurrentNode in the treee
* @return - the schema node to which currentNode belongs to.
*/
private Node getProperSchemaNode(Node node) {
if (node == null)
return null;
if (node.getNodeName().contains(XML_SCHEMA))
return node;
else
return getProperSchemaNode(node.getParentNode());
}
/**
*
* @param string-
* the location of the converted wsdl. this method uses a
* transformer and writes the modified wsdl in to destination
* location.
* @throws CodeGenFailedException
*/
private void writeNewwsdl(String fileLocation)
throws CodeGenFailedException {
s_logger.log(Level.FINE, "BEGIN writeNewwsdl()....");
TransformerFactory transferFact = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = transferFact.newTransformer();
// Bug in java5 transformer, indentation does not work.
// refer to
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446
transformer.setOutputProperty(OutputKeys.INDENT, VALUE_YES);
} catch (TransformerConfigurationException e) {
s_logger.log(Level.SEVERE, e.getMessageAndLocation());
throw new CodeGenFailedException(e.getMessage());
}
DOMSource sourcewsdl = new DOMSource(m_Document);
FileOutputStream output = null;
try {
File file = new File(fileLocation);
try {
s_logger.log(Level.FINE, "Creating new File...");
boolean created = file.createNewFile();
if (created) {
s_logger.log(Level.FINE, "File created: " + fileLocation);
}
} catch (IOException e) {
s_logger.log(Level.SEVERE, e.getMessage());
throw new CodeGenFailedException(e.getMessage());
}
output = new FileOutputStream(file);
StreamResult newWsdl = new StreamResult(output);
sourcewsdl.setNode(m_Document);
transformer.transform(sourcewsdl, newWsdl);
} catch (FileNotFoundException e) {
s_logger.log(Level.SEVERE, e.getMessage());
throw new CodeGenFailedException(e.getMessage());
} catch (TransformerException e) {
s_logger.log(Level.SEVERE, e.getMessage());
throw new CodeGenFailedException(e.getMessage());
}finally{
CodeGenUtil.flushAndCloseQuietly(output);
}
}
/**
*
* @param node-
* the node being visited currently. This method recursively
* visits each node inside <xsd:schema> and checks for <source>
* tag
* @throws DOMException
* @throws CodeGenFailedException
*/
private void recursivelyVisitNode(Node currentNode) throws CodeGenFailedException, DOMException {
if (currentNode == null)
return;
//for SimpleTypes refering to other sinpleTypes with restriction "base"
if(currentNode.getAttributes()!=null &&
currentNode.getAttributes().getNamedItem("base")!= null)
{
String originalType = currentNode.getAttributes().getNamedItem(
"base").getNodeValue();
if (originalType.contains(NAMESPACECOLON)) {
originalType = originalType.substring(originalType
.indexOf(NAMESPACECOLON) + 1);
}
m_AllTypes.add(originalType);
m_TypeWithNamespaceMapOrig.put(originalType, m_WsdlNamespace);
// all types and related nodes
if (m_typeToNodeMap.containsKey(originalType)) {
m_typeToNodeMap.get(originalType).add(currentNode);
} else {
Set<Node> newSet = new HashSet<Node>();
newSet.add(currentNode);
m_typeToNodeMap.put(originalType, newSet);
}
}
if (currentNode.getAttributes() != null
&& currentNode.getAttributes().getNamedItem(TYPE_TAG) != null) {
//populate details about originalType and the namespace it belongs to.
//also populate details about type and the node.
String originalType = currentNode.getAttributes().getNamedItem(
TYPE_TAG).getNodeValue();
// in case type already has a prefix which might needs to be changed
// later.
if (originalType.contains(NAMESPACECOLON)) {
originalType = originalType.substring(originalType
.indexOf(NAMESPACECOLON) + 1);
}
// set of all types present in schema
m_AllTypes.add(originalType);
m_TypeWithNamespaceMapOrig.put(originalType, m_WsdlNamespace);
// all types and related nodes
if (m_typeToNodeMap.containsKey(originalType)) {
m_typeToNodeMap.get(originalType).add(currentNode);
} else {
Set<Node> newSet = new HashSet<Node>();
newSet.add(currentNode);
m_typeToNodeMap.put(originalType, newSet);
}
}
if (currentNode.getNodeName().contains(SOURCE_TAG)) {
if (checkIfvalid(currentNode) && isNamespaceDifferent(currentNode)) {
String associatedNamespace = currentNode.getAttributes()
.getNamedItem(SOURCE_NAMESPACE).getNodeValue();
m_allNamespaces.add(associatedNamespace);
String libraryName = currentNode.getAttributes().getNamedItem(
TYPELIBRARY_TAG).getNodeValue();
Node toBeclonedNode = getproperParentNode(currentNode);
if(m_SchemaNodesSet.contains(toBeclonedNode))
{
// multiple source tag inside a particular complexType/simpleType
String errorMsg = "Input wsdl is invalid..Multiple <source> inside "+toBeclonedNode.getNodeName();
String nameOftype = null;
if(toBeclonedNode.hasAttributes() && toBeclonedNode.getAttributes().getNamedItem(NAME_TAG)!=null)
{
nameOftype = toBeclonedNode.getAttributes().getNamedItem(NAME_TAG).getNodeValue();
errorMsg = errorMsg + " with name = " + nameOftype;
}
throw new CodeGenFailedException(errorMsg);
}
else
{
m_SchemaNodesSet.add(toBeclonedNode);
}
m_AllClonedNodes.add(toBeclonedNode);
s_logger.log(Level.FINE, "Node" + toBeclonedNode.getNodeValue()
+ " cloned...");
Node clonedNode = toBeclonedNode.cloneNode(true);
ClonedSchemaNodeInfo clonedNodewithAdditionalInfo = new ClonedSchemaNodeInfo(
toBeclonedNode, associatedNamespace, libraryName);
m_AllClonedNodeswithAdditionalInfo
.add(clonedNodewithAdditionalInfo);
toBeclonedNode.getParentNode().removeChild(toBeclonedNode);
m_AllClonedNodes.add(clonedNode);
s_logger.log(Level.FINE,
"Adding additional information for the cloned node+"
+ clonedNode.getNodeValue());
}
}
for (int i = 0; i < currentNode.getChildNodes().getLength(); i++) {
s_logger.log(Level.FINE, "Calling recursivelyVisitNode()...");
recursivelyVisitNode(currentNode.getChildNodes().item(i));
}
}
/**
*
* @param node
* -the current node of the DOM
* @return if the namespace for this node is similar to that of wsdl.
*/
private boolean isNamespaceDifferent(Node node) {
s_logger.log(Level.FINE, "BEGIN isNamespaceDifferent()...");
if (node.getAttributes().getNamedItem(SOURCE_NAMESPACE).getNodeValue().equals(
m_WsdlNamespace))
return false;
else
return true;
}
/**
*
* @param node
* -current node of DOM
* @return if the <source> tag found is in correct position.
* @throws CodeGenFailedException
*/
private boolean checkIfvalid(Node node) throws CodeGenFailedException {
// TODO and condition
checkIfSourceTagHasProperAttributes(node);
if (node.getParentNode().getNodeName().contains(APPINFO_TAG)
&& node.getParentNode().getParentNode().getNodeName().contains(
ANNOTATION_TAG))
return true;
else
return false;
}
private void checkIfSourceTagHasProperAttributes(Node node) throws CodeGenFailedException {
if(!(node.hasAttributes()))
{
throw new CodeGenFailedException("source tag does not have attributes");
}
if(node.getAttributes().getNamedItem(SOURCE_NAMESPACE)==null ||
node.getAttributes().getNamedItem(TYPELIBRARY_TAG)==null)
{
throw new CodeGenFailedException("Attributes for the source Tag are Invalid");
}
}
/**
* Need to check if the targetNamespace of the current schema is different form the namespace being added.
* @param node -- schemaNode
* @param namespaceTobeAdded===the namespace which is to be added
* @return
*/
private boolean isTargetNamespaceDifferent(Node node,String namespaceTobeAdded) {
s_logger.log(Level.FINE, "BEGIN isNamespaceDifferent()...");
if (node.getAttributes().getNamedItem(XML_TARGETNAMESPACE).getNodeValue().equals(
namespaceTobeAdded))
return false;
else
return true;
}
/**
* This method adds the additional schemas inside the <Wsdl:types> section
*/
private void addAdditionalSchemas() {
Iterator<String> alNamespaces = m_allNamespaces.iterator();
while (alNamespaces.hasNext()) {
String targetnamespaceForCurrentNode = (String) alNamespaces.next();
List<Node> allNodesWithSameNamespace = getallNodesWithNamespace(targetnamespaceForCurrentNode);
addnewSchemaInwsdl(targetnamespaceForCurrentNode,
allNodesWithSameNamespace);
}
}
/**
* All Elements belonging to a particular namespace should go into One schema.
* @param newNamespace --the namespace to be checked.
* @param NodesWithSameNamespace -- all the nodes with same namespace in the wsdl.
*/
private void addnewSchemaInwsdl(String newNamespace,
List<Node> NodesWithSameNamespace) {
NamedNodeMap attributemap = m_schemaNode.getAttributes();
int length = attributemap.getLength();
String[] oldAttributeName = new String[length];
String[] oldAttributeValue = new String[length];
Element element = m_Document.createElement(m_schemaNode.getNodeName());
for (int i = 0; i < attributemap.getLength(); i++) {
oldAttributeName[i] = attributemap.item(i).getNodeName();
oldAttributeValue[i] = attributemap.item(i).getNodeValue();
if (oldAttributeName[i].equals(XML_TARGETNAMESPACE)) {
oldAttributeValue[i] = newNamespace;
}
element.setAttribute(oldAttributeName[i], oldAttributeValue[i]);
}
for (int i = 0; i < NodesWithSameNamespace.size(); i++) {
Node currentNode = NodesWithSameNamespace.get(i);
if (currentNode.hasAttributes()
&& currentNode.getAttributes().getNamedItem(NAME_TAG) != null) {
String typeName = currentNode.getAttributes().getNamedItem(
NAME_TAG).getNodeValue();
m_TypeWithNamespaceMapFinal.put(typeName, newNamespace);
}
element.appendChild(currentNode);
}
m_schemaNode.getParentNode().appendChild(element);
}
/**
* This method returns the proper parent (complextype or SimpleType)
* @param node --curretnNode in the schema
* @return --proper parent
*/
private Node getproperParentNode(Node node) {
s_logger.log(Level.FINE, "BEGIN getproperParentNode()...");
if (node.getParentNode().getNodeName().contains(XML_COMPLEXTYPE)
|| node.getParentNode().getNodeName().contains(XML_SIMPLETYPE))
return node.getParentNode();
else
return getproperParentNode(node.getParentNode());
}
/**
* This method returns all the nodes with a particlar namespace in the schema
* @param namespace --the namespace
* @return list of nodes
*/
private List<Node> getallNodesWithNamespace(String namespace) {
List<Node> allNodes = new ArrayList<Node>();
Iterator<ClonedSchemaNodeInfo> iterator = m_AllClonedNodeswithAdditionalInfo.iterator();
while (iterator.hasNext()) {
ClonedSchemaNodeInfo currentNode = iterator.next();
if (currentNode.getAssociatedNamespace().equals(namespace)) {
allNodes.add(currentNode.getNode());
}
}
return allNodes;
}
}