/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.ws.util;
import org.apache.ws.util.i18n.Keys;
import org.apache.ws.util.i18n.Messages;
import org.apache.ws.util.i18n.MessagesImpl;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* LOG-DONE Generic utility methods for working with Apache XMLBeans.
*
* @author Ian P. Springer, Sal Campana
*/
public abstract class XmlBeanUtils
{
private static final Messages MSG = MessagesImpl.getInstance();
/**
* TODO
*
* @param xBean
* @param name name to look for, or null to return all child elements
*
* @return
*/
public static XmlObject[] getChildElements( XmlObject xBean,
QName name )
{
List foundElems = new ArrayList();
xBean = getRootElement( xBean );
XmlCursor xCursor = xBean.newCursor();
for ( boolean hasNext = xCursor.toFirstChild(); hasNext; hasNext = xCursor.toNextSibling() )
{
if ( ( name == null ) || getName( xCursor ).equals( name ) )
{
foundElems.add( xCursor.getObject() );
}
}
xCursor.dispose();
return (XmlObject[]) foundElems.toArray( new XmlObject[0] );
}
/**
* TODO
*
* @param xBean
*
* @return
*/
public static XmlObject[] getChildElements( XmlObject xBean )
{
return getChildElements( xBean, null );
}
/**
* DOCUMENT_ME
*
* @param xBean DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public static boolean isDocument( XmlObject xBean )
{
return xBean.schemaType().isDocumentType();
}
/**
* Returns the document XmlBean that is associated with the specified XmlBean.
*
* @param xBean an XmlBean
*
* @return the document XmlBean that is associated with the specified XmlBean
*/
public static XmlObject getDocument( XmlObject xBean )
{
XmlCursor xCursor = xBean.newCursor();
xCursor.toStartDoc();
XmlObject docXBean = xCursor.getObject();
xCursor.dispose();
return docXBean;
}
/**
* @param xBean
*
* @return
*/
public static QName getName( XmlObject xBean )
{
if ( xBean == null )
{
throw new IllegalArgumentException( MSG.getMessage( Keys.NULL_PARAM_NOT_ALLOWED ) );
}
SchemaType docSchemaType;
if ( isDocument( xBean ) )
{
docSchemaType = xBean.schemaType();
}
else
{
docSchemaType = xBean.schemaType().getOuterType();
}
QName name;
if ( docSchemaType != null )
{
name = docSchemaType.getDocumentElementName();
}
else
{
XmlCursor xCursor = xBean.newCursor();
name = getName( xCursor );
xCursor.dispose();
}
return name;
}
/**
* Note. this method does not dispose of the cursor
*
* @param xCursor
*
* @return
*/
public static QName getName( XmlCursor xCursor )
{
QName name;
if ( xCursor.currentTokenType().equals( XmlCursor.TokenType.STARTDOC ) )
{
xCursor.toFirstChild();
name = xCursor.getName();
xCursor.toParent();
}
else
{
name = xCursor.getName();
}
return name;
}
/**
* Returns the root element of the specified document XMLBean. If the specified XMLBean is not a document, the
* XMLBean itself is returned.
*
* @param docXBean a document XMLBean
*
* @return the root element of the specified document XMLBean, or, if the specified XMLBean is not a document, the
* XMLBean itself
*/
public static XmlObject getRootElement( XmlObject docXBean )
{
XmlObject rootElemXBean;
if ( isDocument( docXBean ) )
{
XmlCursor xCursor = docXBean.newCursor();
if ( xCursor.toFirstChild() )
{
rootElemXBean = xCursor.getObject();
}
else
{
rootElemXBean = null;
}
xCursor.dispose();
}
else
{
rootElemXBean = docXBean;
}
return rootElemXBean;
}
/**
* @param xBean
*
* @return
*/
public static String getValue( XmlObject xBean )
{
XmlCursor xCursor = xBean.newCursor();
String value = xCursor.getTextValue();
xCursor.dispose();
return value;
}
/**
* @param xBean
*
* @return
*/
public static QName getValueAsQName( XmlObject xBean )
{
String value = getValue( xBean );
return toQName( value, xBean );
}
/**
* @param xBean
* @param value
*/
public static void setValue( XmlObject xBean,
String value )
{
XmlCursor xCursor = xBean.newCursor();
if ( xCursor.isStartdoc() )
{
xCursor.toFirstChild();
}
xCursor.setTextValue( value );
xCursor.dispose();
}
/**
* @param xBean
* @param qName
*
*/
public static void setValueAsQName( XmlObject xBean, QName qName )
{
XmlCursor xCursor = xBean.newCursor();
String prefix = xCursor.prefixForNamespace( qName.getNamespaceURI() );
String value = prefix + ":" + qName.getLocalPart();
xCursor.setTextValue( value );
xCursor.dispose();
}
/**
*
* @param xBean
* @param attribName
* @return
*/
public static String getAttributeValue( XmlObject xBean, QName attribName )
{
XmlCursor xCursor = xBean.newCursor();
String value = xCursor.getAttributeText( attribName );
xCursor.dispose();
return value;
}
/**
* @param xBean
*
* @return
*/
public static QName getAttributeValueAsQName( XmlObject xBean, QName attribName )
{
String value = getAttributeValue( xBean, attribName );
return toQName( value, xBean );
}
/**
* Returns a Map keyed on Element QName to a List containing the "Any" XmlObjects types.
* <p/>
* The way we determine that an element is an Any is by asking for the element property of the schemaType for the
* element by the element's QName.
*
* @param xmlObjectToSearch The XmlObject to find the Any elements in.
*
* @return Map keyed on Element QName to a List containing the "Any" XmlObjects
*/
public static Map getXmlBeanAnyMap( XmlObject xmlObjectToSearch )
{
Map qnameToListMap = new HashMap();
XmlCursor xCursor = xmlObjectToSearch.newCursor();
for ( boolean hasNext = xCursor.toFirstChild(); hasNext; hasNext = xCursor.toNextSibling() )
{
XmlObject siblingXmlObject = xCursor.getObject();
QName siblingXmlObjectQname = getName(siblingXmlObject);
// TODO: should this be tested against the original bean
//if it contains the ElementProperty, then it is not an Any
if ( siblingXmlObject.schemaType().getElementProperty( siblingXmlObjectQname ) != null )
{
continue;
}
//check to see if we have a xmlObjectList to add to for this QName
List xmlObjectList = (List) qnameToListMap.get( siblingXmlObjectQname );
if ( xmlObjectList == null )
{
xmlObjectList = new ArrayList();
}
xmlObjectList.add( siblingXmlObject );
qnameToListMap.put( siblingXmlObjectQname, xmlObjectList );
}
xCursor.dispose();
return qnameToListMap;
}
/**
* Adds a copy of the specified XmlBean as the last child element of the
* specified parent XmlBean.
*
* @param parentXBean
* @param xBean
*/
public static XmlObject addChildElement( XmlObject parentXBean,
XmlObject xBean )
{
parentXBean = getRootElement( parentXBean );
xBean = getRootElement( xBean );
XmlCursor parentCursor = parentXBean.newCursor();
if ( parentCursor.toLastChild() ) // has children
{
parentCursor.toEndToken();
parentCursor.toNextToken();
}
else // childless
{
parentCursor.toEndToken();
}
parentCursor.insertElement( getName( xBean ) );
parentCursor.toPrevSibling();
XmlObject childXBean = parentCursor.getObject();
parentCursor.dispose();
return childXBean.set( xBean );
}
/**
* Adds an XmlBean with the specified name as the last of the specified parent XmlBean.
*
* @param parentXBean
* @param name
*/
public static XmlObject addChildElement( XmlObject parentXBean,
QName name )
{
parentXBean = getRootElement( parentXBean );
XmlCursor parentCursor = parentXBean.newCursor();
if ( parentCursor.toLastChild() ) // has children
{
parentCursor.toEndToken();
parentCursor.toNextToken();
}
else // childless
{
parentCursor.toEndToken();
}
parentCursor.insertElement( name );
parentCursor.toPrevSibling();
XmlObject childXBean = parentCursor.getObject();
parentCursor.dispose();
return childXBean;
}
/**
* Removes the specifed XMLBean from its parent element.
*
* @param xBean an XMLBean
*/
public static void remove( XmlObject xBean )
{
XmlCursor xCursor = xBean.newCursor();
xCursor.removeXml();
xCursor.dispose();
}
/**
* Removes all child elements, with the specified name, from the specified XMLBean.
*
* @param xBean an XMLBean
*
* @return true if all child elements, with the specified name, were successfully removed
*/
public static boolean removeChildElements( XmlObject xBean,
QName name )
{
boolean succeeded = true;
xBean = getRootElement( xBean );
XmlCursor xCursor = xBean.newCursor();
for ( boolean hasNext = xCursor.toFirstChild(); hasNext; hasNext = xCursor.toNextSibling() )
{
if ( name == null || getName( xCursor ).equals( name ) )
{
succeeded = succeeded && xCursor.removeXml();
}
}
xCursor.dispose();
return succeeded;
}
/**
* Removes all child elements of the specified XMLBean.
*
* @param xBean an XMLBean
*
* @return true if all child elements were successfully removed
*/
public static boolean removeChildElements( XmlObject xBean )
{
return removeChildElements( xBean, null );
}
/**
* @param xBean
*
* @return
*
* @throws Exception
*/
public static SOAPElement toSOAPElement( XmlObject xBean )
throws Exception
{
XmlOptions xSaveOpts = new XmlOptions().setSaveOuter().setSavePrettyPrint().setSavePrettyPrintIndent( 2 );
Node domNode = xBean.newDomNode( xSaveOpts );
Element domElem = null;
if ( domNode instanceof Document )
{
domElem = ( (Document) domNode ).getDocumentElement();
}
else if ( domNode instanceof DocumentFragment )
{
NodeList childNodes = domNode.getChildNodes();
for ( int i = 0; i < childNodes.getLength(); i++ )
{
if ( childNodes.item( i ).getNodeType() == Node.ELEMENT_NODE )
{
domElem = (Element) childNodes.item( i );
break;
}
}
}
return SaajUtils.toSOAPElement( domElem );
}
/**
* DOCUMENT_ME
*
* @param elems DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws Exception DOCUMENT_ME
*/
public static SOAPElement[] toSOAPElementArray( final XmlObject[] elems )
throws Exception
{
SOAPElement[] soapElems = new SOAPElement[elems.length];
for ( int i = 0; i < elems.length; i++ )
{
soapElems[i] = toSOAPElement( elems[i] );
}
return soapElems;
}
/**
* If possible, converts the specified object to an XMLBean.
*
* @param obj an object
*
* @return an XMLBean, or null if the parameter is null
*
* @throws Exception if the object cannot be converted to an XMLBean
*/
public static XmlObject toXmlObject( Object obj )
throws Exception
{
XmlObject xBean;
if ( obj == null)
{
return null;
}
if ( obj instanceof XmlObject )
{
xBean = (XmlObject) obj;
}
else if ( obj instanceof SOAPElement )
{
// !!!NOTE!!! It is critical to do parse( obj.toString() ) here, instead of parse( (Node)obj ),
// because Axis 1.2's DOM impl (MessageElement) strips off all attributes, including
// namespace decls! (TODO: file an Axis bug for the aforementioned issue)
xBean = XmlObject.Factory.parse( obj.toString() );
}
else if ( obj instanceof Node )
{
xBean = XmlObject.Factory.parse( (Node) obj );
}
else if ( obj instanceof String )
{
xBean = XmlObject.Factory.parse( (String) obj );
}
else if ( obj instanceof InputStream )
{
xBean = XmlObject.Factory.parse( (InputStream) obj );
}
else if ( obj instanceof Reader )
{
xBean = XmlObject.Factory.parse( (Reader) obj );
}
else if ( obj instanceof File )
{
xBean = XmlObject.Factory.parse( (File) obj );
}
else if ( obj instanceof URL )
{
xBean = XmlObject.Factory.parse( (URL) obj );
}
else
{
throw new IllegalArgumentException( MSG.getMessage( Keys.PARAM_MUST_BE_TYPE ) + " " + obj.getClass().getName());
}
return xBean;
}
/**
*
* @param elemName
* @return
*/
public XmlObject newInstance( QName elemName )
{
XmlObject xBean = XmlObject.Factory.newInstance();
// TODO: finish implementing
return xBean;
}
private static QName toQName( String value, XmlObject xBean )
{
int colonIndex = value.indexOf( ':' );
String nsURI;
if ( colonIndex != -1 )
{
String prefix = value.substring( 0, colonIndex );
XmlCursor xCursor = xBean.newCursor();
nsURI = xCursor.namespaceForPrefix( prefix );
if ( nsURI == null )
{
throw new RuntimeException( "No namespace is associated with prefix '" + prefix + "'" );
}
xCursor.dispose();
}
else
{
nsURI = "";
}
String localName = value.substring( colonIndex + 1 );
return new QName( nsURI, localName );
}
/**
* Makes and returns a copy of the specified XMLBean.
*
* @param srcXBean the XMLBean to be copied
*
* @return a copy of the specified XMLBean
*/
public static XmlObject copyXmlBean( XmlObject srcXBean )
{
try
{
return XmlObject.Factory.parse( srcXBean.xmlText( new XmlOptions().setSaveOuter() ) );
}
catch ( XmlException xe )
{
throw new RuntimeException( xe );
}
/*XmlCursor srcCursor = srcXBean.newCursor( );
// create an object of the appropriate type to copy to
XmlObject destXBean = XmlObject.Factory.newInstance( new XmlOptions( ).setDocumentType( srcXBean.schemaType() ) );
XmlCursor destCursor = destXBean.newCursor( );
// move into the document
destCursor.toNextToken( );
// copy the xml into the new document
srcCursor.copyXml( destCursor );
// clean up our cursors
destCursor.dispose( );
srcCursor.dispose( );
return destXBean;*/
}
/**
* Makes and returns a copy of the specified XMLBean array.
*
* @param srcXBeans the array of XMLBeans to be copied
*
* @return a copy of the specified XMLBean array
*/
public static XmlObject[] copyXmlBeans( XmlObject[] srcXBeans )
{
if ( srcXBeans == null )
{
return null;
}
XmlObject[] destXBeans = new XmlObject[srcXBeans.length];
for ( int i = 0; i < srcXBeans.length; i++ )
{
destXBeans[i] = copyXmlBean( srcXBeans[i] );
}
return destXBeans;
}
}