blob: e6e0fbbca668f9a79231e10f46215c0e027e839b [file] [log] [blame]
/*
* Copyright (C) 2003 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate. Authorship
* of the modifications may be determined from the ChangeLog placed at
* the end of this file.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
****************************************************************
* Source code information
* -----------------------
* Filename $RCSfile: XMLSplitterSerialisationHelper.java,v $
* Revision $Revision: 1.2 $
* Release status $State: Exp $
* Last modified on $Date: 2008/08/08 10:28:08 $
* by $Author: stain $
* Created on 16-May-2006
*****************************************************************/
package net.sf.taverna.wsdl.xmlsplitter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import net.sf.taverna.wsdl.parser.ArrayTypeDescriptor;
import net.sf.taverna.wsdl.parser.AttributeTypeDescriptor;
import net.sf.taverna.wsdl.parser.BaseTypeDescriptor;
import net.sf.taverna.wsdl.parser.ComplexTypeDescriptor;
import net.sf.taverna.wsdl.parser.TypeDescriptor;
import org.apache.log4j.Logger;
import org.jdom.Content;
import org.jdom.Element;
import org.jdom.Namespace;
/**
* A helper class that supports the XMLOutputSplitter and XMLInputSplitter,
* providing the ability for each to be serialised/deserialised to and from the
* extensions XML for the ScuflModel XML when storing a workflow. This XML
* describes the TypeDescriptor tree that the Splitter wraps.
*
* @author Stuart Owen
* @author Asger Askov-Bleking
*
*/
public class XMLSplitterSerialisationHelper {
public final static Namespace XScuflNS = Namespace.getNamespace("s",
"http://org.embl.ebi.escience/xscufl/0.1alpha");
private static Logger logger = Logger
.getLogger(XMLSplitterSerialisationHelper.class);
/**
* Generates the extensions XML that describes the TypeDescriptor to allow
* an XMLInputSplitter or XMLOutputSplitter to be reconstructed using
* consumeXML.
*/
public static Element typeDescriptorToExtensionXML(TypeDescriptor descriptor) {
Element result = new Element("extensions", XScuflNS);
Element type = null;
if (descriptor instanceof ComplexTypeDescriptor) {
type = constructElementForComplexType(
(ComplexTypeDescriptor) descriptor, new ArrayList<String>());
}
else if (descriptor instanceof ArrayTypeDescriptor) {
type = constructElementForArrayType(
(ArrayTypeDescriptor) descriptor, new ArrayList<String>());
}
result.addContent(type);
return result;
}
/**
* Generates the TypeDescriptor structurefrom the extensions XML element
* provided. This assumes that the root of the structure is <complextype/>.
* This will be the same xml generated by provideXML.
*/
public static TypeDescriptor extensionXMLToTypeDescriptor(Element element) {
Element child = (Element) element.getChildren().get(0);
return buildTypeDescriptorFromElement(child,
new HashMap<String, TypeDescriptor>());
}
private static Element constructElementForArrayType(
ArrayTypeDescriptor descriptor, List<String> existingsTypes) {
Element result = new Element("arraytype", XScuflNS);
if (existingsTypes.contains(descriptor.getQname().toString())) {
result.setAttribute("id", descriptor.getQname().toString());
populateElement(result, descriptor);
result.removeAttribute("qname");
} else {
existingsTypes.add(descriptor.getQname().toString());
populateElement(result, descriptor);
Element elementType = new Element("elementtype", XScuflNS);
if (descriptor.getElementType() instanceof ComplexTypeDescriptor) {
elementType.addContent(constructElementForComplexType(
(ComplexTypeDescriptor) descriptor.getElementType(),
existingsTypes));
} else if (descriptor.getElementType() instanceof ArrayTypeDescriptor) {
elementType.addContent(constructElementForArrayType(
(ArrayTypeDescriptor) descriptor.getElementType(),
existingsTypes));
} else if (descriptor.getElementType() instanceof BaseTypeDescriptor) {
Element element = new Element("basetype", XScuflNS);
populateElement(element, descriptor.getElementType());
elementType.addContent(element);
}
result.addContent(elementType);
}
return result;
}
private static Element constructElementForComplexType(
ComplexTypeDescriptor descriptor, List<String> existingsTypes) {
Element result = new Element("complextype", XScuflNS);
if (existingsTypes.contains(descriptor.getQname().toString())) {
result.setAttribute("id", descriptor.getQname().toString());
populateElement(result, descriptor);
result.removeAttribute("qname");
} else {
existingsTypes.add(descriptor.getQname().toString());
populateElement(result, descriptor);
Element elements = new Element("elements", XScuflNS);
for (TypeDescriptor desc : descriptor.getElements()) {
Element element = null;
if (desc instanceof ComplexTypeDescriptor) {
element = constructElementForComplexType(
(ComplexTypeDescriptor) desc, existingsTypes);
} else if (desc instanceof ArrayTypeDescriptor) {
element = constructElementForArrayType(
(ArrayTypeDescriptor) desc, existingsTypes);
} else if (desc instanceof BaseTypeDescriptor) {
element = new Element("basetype", XScuflNS);
populateElement(element, desc);
}
if (element != null)
elements.addContent(element);
}
result.addContent(elements);
List<TypeDescriptor> attributeDescriptors = descriptor.getAttributes();
if (attributeDescriptors != null && attributeDescriptors.size() > 0) {
Element attributes = new Element("attributes", XScuflNS);
for (TypeDescriptor desc : attributeDescriptors) {
Element attribute = new Element("attribute", XScuflNS);
populateElement(attribute, desc);
attributes.addContent(attribute);
}
result.addContent(attributes);
}
}
return result;
}
private static void populateElement(Element element,
TypeDescriptor descriptor) {
element.setAttribute("optional", String
.valueOf(descriptor.isOptional()));
element.setAttribute("unbounded", String.valueOf(descriptor
.isUnbounded()));
if (descriptor instanceof ArrayTypeDescriptor) {
element.setAttribute("wrapped", String
.valueOf(((ArrayTypeDescriptor) descriptor).isWrapped()));
}
element.setAttribute("typename", descriptor.getType());
element.setAttribute("name", descriptor.getName() == null ? ""
: descriptor.getName());
element.setAttribute("qname", descriptor.getQname().toString());
if (descriptor.getDocumentation() != null){
Element annotationElement =
new Element("annotation", Namespace.getNamespace("xsd", "http://www.w3.org/2001/XMLSchema"));
Element documentationElemenet =
new Element("documentation", Namespace.getNamespace("xsd", "http://www.w3.org/2001/XMLSchema"));
documentationElemenet.setText(descriptor.getDocumentation());
annotationElement.addContent(documentationElemenet);
element.addContent(annotationElement);
}
}
private static TypeDescriptor buildTypeDescriptorFromElement(
Element element, HashMap<String, TypeDescriptor> existingsTypes) {
TypeDescriptor result = null;
if (element.getAttributeValue("id") != null) {
TypeDescriptor stored = existingsTypes.get(element
.getAttributeValue("id"));
if (stored == null)
logger.fatal("Missing reference to parent type with id="
+ element.getAttributeValue("id"));
else {
result = createFromCache(stored, element);
}
}
if (result == null) {
if (element.getName().equalsIgnoreCase("complextype")) {
result = new ComplexTypeDescriptor();
populateDescriptor(element, result);
existingsTypes.put(result.getQname().toString(), result);
Element elements = element.getChild("elements", XScuflNS);
for (Iterator<?> iterator = elements.getChildren().iterator(); iterator
.hasNext();) {
Element childElement = (Element) iterator.next();
((ComplexTypeDescriptor) result).getElements().add(
buildTypeDescriptorFromElement(childElement,
existingsTypes));
}
Element attributes = element.getChild("attributes", XScuflNS);
if (attributes != null) {
for (Iterator<?> iterator = attributes.getChildren().iterator(); iterator
.hasNext();) {
Element childElement = (Element) iterator.next();
((ComplexTypeDescriptor) result).getAttributes().add(
buildTypeDescriptorFromElement(childElement,
existingsTypes));
}
}
} else if (element.getName().equalsIgnoreCase("arraytype")) {
result = new ArrayTypeDescriptor();
populateDescriptor(element, result);
existingsTypes.put(result.getQname().toString(), result);
Element elementType = element.getChild("elementtype", XScuflNS);
((ArrayTypeDescriptor) result)
.setElementType(buildTypeDescriptorFromElement(
(Element) elementType.getChildren().get(0),
existingsTypes));
if (element.getAttribute("wrapped") != null) {
((ArrayTypeDescriptor) result).setWrapped(element
.getAttributeValue("wrapped").equalsIgnoreCase(
"true"));
} else {
// prior to the addition of the wrapped attribute, in the
// majority of cases an array
// would not be wrapped if it was flagged as unbounded.
((ArrayTypeDescriptor) result).setWrapped(!result
.isUnbounded());
}
} else if (element.getName().equalsIgnoreCase("basetype")) {
result = new BaseTypeDescriptor();
populateDescriptor(element, result);
} else if (element.getName().equalsIgnoreCase("attribute")) {
result = new AttributeTypeDescriptor();
populateDescriptor(element, result);
} else if (element.getName().equalsIgnoreCase("attribute")) {
result = new AttributeTypeDescriptor();
populateDescriptor(element, result);
}
}
return result;
}
/**
* Performs a shallow copy of the descriptor stored, but updates its name,
* isbounded and optional This means that descriptors of the same type do
* not need to be repeated throught the stored XML but also takes into
* account parameters of the same type may have different name and
* attributes to that stored
*
* @param descriptor
* @param element
* @return
*/
private static TypeDescriptor createFromCache(TypeDescriptor descriptor,
Element element) {
TypeDescriptor result = null;
if (descriptor instanceof ArrayTypeDescriptor) {
ArrayTypeDescriptor array = new ArrayTypeDescriptor();
array.setQname(descriptor.getQname());
array.setElementType(((ArrayTypeDescriptor) descriptor)
.getElementType());
array.setWrapped(((ArrayTypeDescriptor) descriptor).isWrapped());
result = array;
} else if (descriptor instanceof ComplexTypeDescriptor) {
ComplexTypeDescriptor complex = new ComplexTypeDescriptor();
complex.setQname(descriptor.getQname());
complex.setElements(((ComplexTypeDescriptor) descriptor)
.getElements());
result = complex;
} else {
throw new IllegalArgumentException("Unexpected type descriptor: "
+ descriptor);
}
result.setType(descriptor.getType());
String name = element.getAttributeValue("name");
result.setName(name != null ? name : descriptor.getName());
String optional = element.getAttributeValue("optional");
if (optional != null) {
result.setOptional(optional.equalsIgnoreCase("true"));
} else {
result.setOptional(descriptor.isOptional());
}
String unbounded = element.getAttributeValue("unbounded");
if (unbounded != null) {
result.setUnbounded(unbounded.equalsIgnoreCase("true"));
} else {
result.setUnbounded(descriptor.isUnbounded());
}
return result;
}
private static void populateDescriptor(Element element,
TypeDescriptor result) {
result.setName(element.getAttributeValue("name"));
result.setType(element.getAttributeValue("typename"));
result.setOptional(element.getAttributeValue("optional")
.equalsIgnoreCase("true"));
result.setUnbounded(element.getAttributeValue("unbounded")
.equalsIgnoreCase("true"));
Element annotationChild =
element.getChild("annotation", Namespace.getNamespace("xsd", "http://www.w3.org/2001/XMLSchema"));
if (annotationChild != null){
List documentationChildren = annotationChild
.getChildren("documentation", Namespace.getNamespace("xsd", "http://www.w3.org/2001/XMLSchema"));
String documentation = "";
for (Object documentationChild : documentationChildren) {
documentation += ((Element)documentationChild).getText();
}
if (!documentation.isEmpty()){
result.setDocumentation(documentation);
}
}
// qname has been added since 1.3.2-RC1 so need to test if missing for
// older workflows
// if missing it is resolved to an empty namespace and typename:
// {}typename
String qname = element.getAttributeValue("qname");
if (qname != null)
result.setQnameFromString(qname);
}
}