/***************************************************************************
* 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() );
}
}
}