/*************************************************************************** * Copyright (C) 2008 by Fabrizio Montesi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 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 General Public License for more details. * * * * You should have received a copy of the GNU Library 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. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.xml; import com.sun.xml.xsom.XSAttributeUse; import com.sun.xml.xsom.XSComplexType; import com.sun.xml.xsom.XSContentType; import com.sun.xml.xsom.XSElementDecl; import com.sun.xml.xsom.XSModelGroup; import com.sun.xml.xsom.XSModelGroupDecl; import com.sun.xml.xsom.XSParticle; import com.sun.xml.xsom.XSTerm; import com.sun.xml.xsom.XSType; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import jolie.lang.Constants; import jolie.runtime.Value; 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 jolie.runtime.ValueVector; /** * Utilities for interactions and transformations with XML. * @author Fabrizio Montesi */ public class XmlUtils { private static final String JOLIE_TYPE_ATTRIBUTE = "_jolie_type"; /** * Transforms a jolie.Value object to an XML Document instance preserving types. * @see Document * @param value the source Value * @param rootNodeName the name to give to the root node of the document * @param document the XML document receiving the transformation * @author Claudio Guidi 7/1/2011 */ public static void valueToStorageDocument( Value value, String rootNodeName, Document document ) { Element root = document.createElement( rootNodeName ); document.appendChild( root ); _valueToStorageDocument( value, root, document ); } /** * Transforms a jolie.Value object to an XML Document instance. * @see Document * @param value the source Value * @param rootNodeName the name to give to the root node of the document * @param document the XML document receiving the transformation */ public static void valueToDocument( Value value, String rootNodeName, Document document ) { Element root = document.createElement( rootNodeName ); document.appendChild( root ); _valueToDocument( value, root, document ); } /** * Transforms a jolie.Value object to an XML Document instance following a given XML Type Definition. * @see Document * @param value the source Value * @param rootNodeName the name to give to the root node of the document. * @param document the XML document receiving the transformation. * @param type the XML type definition to follow in writing the XML document. */ public static void valueToDocument( Value value, String rootNodeName, Document document, XSType type ) { Element root = document.createElement( rootNodeName ); document.appendChild( root ); _valueToDocument( value, root, document, type ); } private static void _valueToDocument( Value value, Element element, Document doc, XSModelGroup modelGroup ) { String name; XSModelGroup.Compositor compositor = modelGroup.getCompositor(); if ( compositor.equals( XSModelGroup.SEQUENCE ) ) { XSParticle[] children = modelGroup.getChildren(); XSTerm currTerm; XSElementDecl currElementDecl; Value v; ValueVector vec; for( int i = 0; i < children.length; i++ ) { currTerm = children[i].getTerm(); if ( currTerm.isElementDecl() ) { currElementDecl = currTerm.asElementDecl(); name = currElementDecl.getName(); Element childElement = null; if ( (vec=value.children().get( name )) != null ) { int k = 0; while( vec.isEmpty() == false && (children[i].getMaxOccurs() == XSParticle.UNBOUNDED || children[i].getMaxOccurs() > k) ) { childElement = doc.createElement( name ); element.appendChild( childElement ); v = vec.remove( 0 ); _valueToDocument( v, childElement, doc, currElementDecl.getType() ); k++; } } else if ( children[i].getMinOccurs() > 0 ) { // TODO throw some error here } } else if ( currTerm.isModelGroupDecl() ) { _valueToDocument( value, element, doc, currTerm.asModelGroupDecl().getModelGroup() ); } else if ( currTerm.isModelGroup() ) { _valueToDocument( value, element, doc, currTerm.asModelGroup() ); } } } else if ( compositor.equals( XSModelGroup.CHOICE ) ) { XSParticle[] children = modelGroup.getChildren(); XSTerm currTerm; XSElementDecl currElementDecl; Value v; ValueVector vec; boolean found = false; for( int i = 0; i < children.length && !found; i++ ) { currTerm = children[i].getTerm(); if ( currTerm.isElementDecl() ) { currElementDecl = currTerm.asElementDecl(); name = currElementDecl.getName(); Element childElement = null; if ( (vec=value.children().get( name )) != null ) { childElement = doc.createElement( name ); element.appendChild( childElement ); found = true; v = vec.remove( 0 ); _valueToDocument( v, childElement, doc, currElementDecl.getType() ); } else if ( children[i].getMinOccurs() > 0 ) { // TODO throw some error here } } } } } public static void documentToValue( Document document, Value value ) { documentToValue( document, value, true ); } private static void _valueToDocument( Value value, Element element, Document doc, XSType type ) { if ( type.isSimpleType() ) { element.appendChild( doc.createTextNode( value.strValue() ) ); } else if ( type.isComplexType() ) { String name; Value currValue; XSComplexType complexType = type.asComplexType(); // Iterate over attributes Collection< ? extends XSAttributeUse > attributeUses = complexType.getAttributeUses(); for( XSAttributeUse attrUse : attributeUses ) { name = attrUse.getDecl().getName(); if ( (currValue=getAttributeOrNull( value, name )) != null ) { element.setAttribute( name, currValue.strValue() ); } } XSParticle particle; XSContentType contentType = complexType.getContentType(); if ( contentType.asSimpleType() != null ) { element.appendChild( doc.createTextNode( value.strValue() ) ); } else if ( (particle=contentType.asParticle()) != null ) { XSTerm term = particle.getTerm(); XSModelGroupDecl modelGroupDecl; XSModelGroup modelGroup = null; if ( (modelGroupDecl=term.asModelGroupDecl()) != null ) { modelGroup = modelGroupDecl.getModelGroup(); } else if ( term.isModelGroup() ) { modelGroup = term.asModelGroup(); } if ( modelGroup != null ) { _valueToDocument( value, element, doc, modelGroup ); } } } } private static void _valueToDocument( Value value, Element element, Document doc ) { element.appendChild( doc.createTextNode( value.strValue() ) ); Map< String, ValueVector > attrs = getAttributesOrNull( value ); if ( attrs != null ) { for( Entry< String, ValueVector > attrEntry : attrs.entrySet() ) { element.setAttribute( attrEntry.getKey(), attrEntry.getValue().first().strValue() ); } } Element currentElement; for( Entry< String, ValueVector > entry : value.children().entrySet() ) { if ( !entry.getKey().startsWith( "@" ) ) { for( Value val : entry.getValue() ) { currentElement = doc.createElement( entry.getKey() ); element.appendChild( currentElement ); _valueToDocument( val, currentElement, doc ); } } } } /* * author Claudio Guidi * 7/1/2011 */ private static void _valueToStorageDocument( Value value, Element element, Document doc ) { // Supports only string, int and double if ( value.isString() ) { element.appendChild( doc.createTextNode( value.strValue() ) ); element.setAttribute( JOLIE_TYPE_ATTRIBUTE, "string" ); } else if ( value.isInt() ) { element.appendChild( doc.createTextNode( new Integer( value.intValue() ).toString() ) ); element.setAttribute( JOLIE_TYPE_ATTRIBUTE, "int" ); } else if ( value.isDouble() ) { element.appendChild( doc.createTextNode( new Double( value.doubleValue() ).toString() ) ); element.setAttribute( JOLIE_TYPE_ATTRIBUTE, "double" ); } else { element.setAttribute( JOLIE_TYPE_ATTRIBUTE, "void" ); } // adding other attributes Map<String, ValueVector> attrs = getAttributesOrNull( value ); if ( attrs != null ) { for( Entry<String, ValueVector> attrEntry : attrs.entrySet() ) { element.setAttribute( attrEntry.getKey(), attrEntry.getValue().first().strValue() ); } } // adding subelements Element currentElement; for( Entry<String, ValueVector> entry : value.children().entrySet() ) { if ( !entry.getKey().startsWith( "@" ) ) { for( Value val : entry.getValue() ) { currentElement = doc.createElement( entry.getKey() ); element.appendChild( currentElement ); _valueToStorageDocument( val, currentElement, doc ); } } } } public static Map< String, ValueVector > getAttributesOrNull( Value value ) { Map< String, ValueVector > ret = null; ValueVector vec = value.children().get( Constants.Predefined.ATTRIBUTES.token().content() ); if ( vec != null && vec.size() > 0 ) { ret = vec.first().children(); } if ( ret == null ) { ret = new HashMap< String, ValueVector >(); } return ret; } /** * Transforms an XML Document to a Value representation * @see Document * @param document the source XML document * @param value the Value receiving the JOLIE representation of document */ public static void documentToValue( Document document, Value value, boolean includeAttributes ) { if ( includeAttributes ) { setAttributes( value, document.getDocumentElement() ); elementsToSubValues( value, document.getDocumentElement().getChildNodes(), true ); } else { elementsToSubValues( value, document.getDocumentElement().getChildNodes(), false ); } } /* * author: Claudio Guidi * 7/1/2011 */ public static void storageDocumentToValue( Document document, Value value ) { String type = setAttributesForStoring( value, document.getDocumentElement() ); elementsToSubValuesForStoring( value, document.getDocumentElement().getChildNodes(), type ); } private static Value getAttribute( Value value, String attrName ) { return value.getFirstChild( Constants.Predefined.ATTRIBUTES.token().content() ) .getFirstChild( attrName ); } private static Value getAttributeOrNull( Value value, String attributeName ) { Value ret = null; Map< String, ValueVector > attrs = getAttributesOrNull( value ); if ( attrs != null ) { ValueVector vec = attrs.get( attributeName ); if ( vec != null && vec.size() > 0 ) { ret = vec.first(); } } return ret; } private static void setAttributes( Value value, Node node ) { NamedNodeMap map = node.getAttributes(); if ( map != null ) { Node attr; for( int i = 0; i < map.getLength(); i++ ) { attr = map.item( i ); getAttribute( value, ( attr.getLocalName() == null ) ? attr.getNodeName() : attr.getLocalName() ).setValue( attr.getNodeValue() ); } } } /* * author Claudio Guidi * 7/1/2011 * @return the type of the JOLIE_TYPE */ private static String setAttributesForStoring( Value value, Node node ) { NamedNodeMap map = node.getAttributes(); String type = "string"; if ( map != null ) { Node attr; for( int i = 0; i < map.getLength(); i++ ) { attr = map.item( i ); if ( !attr.getNodeName().equals( JOLIE_TYPE_ATTRIBUTE ) ) { // do not consider attribute type getAttribute( value, (attr.getLocalName() == null) ? attr.getNodeName() : attr.getLocalName() ).setValue( attr.getNodeValue() ); } else { type = attr.getNodeValue(); } } } return type; } /* * author Claudio Guidi * 7/1/2011 */ private static void elementsToSubValuesForStoring( Value value, NodeList list, String type ) { Node node; Value childValue; StringBuilder builder = new StringBuilder(); for( int i = 0; i < list.getLength(); i++ ) { node = list.item( i ); switch( node.getNodeType() ) { case Node.ATTRIBUTE_NODE: if ( !node.getNodeName().equals( JOLIE_TYPE_ATTRIBUTE ) ) { getAttribute( value, node.getNodeName() ).setValue( node.getNodeValue() ); } break; case Node.ELEMENT_NODE: childValue = value.getNewChild( (node.getLocalName() == null) ? node.getNodeName() : node.getLocalName() ); String subElType = setAttributesForStoring( childValue, node ); elementsToSubValuesForStoring( childValue, node.getChildNodes(), subElType ); break; case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: builder.append( node.getNodeValue() ); break; } } if ( builder.length() > 0 ) { if ( type.equals( "string" ) ) { value.setValue( builder.toString() ); } else if ( type.equals( "int" ) ) { value.setValue( new Integer( builder.toString() ) ); } else if ( type.equals( "double" ) ) { value.setValue( new Double( builder.toString() ) ); } } } private static void elementsToSubValues( Value value, NodeList list, boolean includeAttributes ) { Node node; Value childValue; StringBuilder builder = new StringBuilder(); for( int i = 0; i < list.getLength(); i++ ) { node = list.item( i ); switch( node.getNodeType() ) { case Node.ATTRIBUTE_NODE: if ( includeAttributes ) { getAttribute( value, node.getNodeName() ).setValue( node.getNodeValue() ); } break; case Node.ELEMENT_NODE: childValue = value.getNewChild( ( node.getLocalName() == null ) ? node.getNodeName() : node.getLocalName() ); if ( includeAttributes ){ setAttributes( childValue, node ); } elementsToSubValues( childValue, node.getChildNodes(), includeAttributes ); break; case Node.CDATA_SECTION_NODE: case Node.TEXT_NODE: builder.append( node.getNodeValue() ); break; } } if ( builder.length() > 0 ) { value.setValue( builder.toString() ); } } }