/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 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
*
* $Id: JDOMAxisDeserializer.java 1607 2006-09-29 12:32:13Z drmlipp $
*
* $Log$
* Revision 1.1.1.1 2003/06/30 20:05:17 drmlipp
* Initial import
*
* Revision 1.4 2003/06/27 08:51:44 lipp
* Fixed copyright/license information.
*
* Revision 1.3 2003/06/26 22:07:46 lipp
* Fixed deserializer.
*
* Revision 1.2 2003/06/26 15:32:57 lipp
* Improved version.
*
* Revision 1.1 2003/06/25 15:50:29 lipp
* New SOAP client that makes wsif obsolete.
*
*/
package de.danet.an.workflow.tools.soapclient;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import javax.xml.namespace.QName;
import org.apache.axis.Constants;
import org.apache.axis.Part;
import org.apache.axis.encoding.DeserializationContext;
import org.apache.axis.encoding.Deserializer;
import org.apache.axis.encoding.DeserializerTarget;
import org.apache.axis.encoding.Target;
import org.apache.axis.message.EnvelopeHandler;
import org.apache.axis.message.MessageElement;
import org.apache.axis.message.SAX2EventRecorder;
import org.apache.axis.message.SOAPHandler;
import org.apache.axis.soap.SOAPConstants;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* General purpose serializer/deserializerFactory for JDOM in the AXIS
* implementation of the JAX-RPC interface.
* Creates a simple org.jdom.Element for the parsed xml content,
* preserving the stucture and text content.
* This class uses a stack to notice the created nodes. So we do not
* need to generate new instances of this class for every recursive
* descent (initialized by the onStartChild() call).
*/
public class JDOMAxisDeserializer
extends SOAPHandler implements Deserializer {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog
(JDOMAxisDeserializer.class);
private Element value = null;
private QName defaultType;
private Vector targets = null;
private StringBuffer textBuffer = null;
/**
* Default constructor.
*/
public JDOMAxisDeserializer() {
super();
}
/**
* This method is invoked when an element start tag is encountered.
* @param namespace is the namespace of the element
* @param localName is the name of the element
* @param prefix is the prefix
* @param attributes are the attributes on the element
* @param context is the DeserializationContext
* @throws SAXException not thrown
*/
public void startElement(String namespace, String localName,
String prefix, Attributes attributes,
DeserializationContext context)
throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("startElement: {" + namespace + "}" + localName);
}
super.startElement(namespace, localName, prefix, attributes, context);
// axis sometimes resends the start event, ignore
if (value != null) {
return;
}
value = new Element (localName, prefix, namespace);
if (attributes != null) {
for (int i = 0; i < attributes.getLength(); i++) {
String aName = attributes.getLocalName(i); // Attr name
if ("".equals(aName)) {
aName = attributes.getQName(i);
}
String aValue = attributes.getValue(i);
value.setAttribute(aName, aValue);
}
}
String id = attributes.getValue("id");
if (id != null) {
context.addObjectById(id, value);
}
SOAPConstants soapConstants
= context.getMessageContext().getSOAPConstants();
String href = attributes.getValue(soapConstants.getAttrHref());
if (href == null) {
onStartElement(namespace, localName, prefix, attributes, context);
} else {
Object ref = context.getObjectByRef(href);
if (ref == null) {
// Nothing yet... register for later interest.
context.registerFixup(href, this);
} else if (ref instanceof MessageElement) {
context.replaceElementHandler(new EnvelopeHandler(this));
SAX2EventRecorder r = context.getRecorder();
context.setRecorder(null);
((MessageElement)ref)
.publishToHandler((DefaultHandler) context);
context.setRecorder(r);
} else {
if(!href.startsWith("#") && defaultType != null
&& ref instanceof Part) {
//For attachments this is the end of the road--
//invoke deserializer
Deserializer dser
= context.getDeserializerForType(defaultType );
if(null != dser) {
dser.startElement(namespace, localName, prefix,
attributes, context);
ref = dser.getValue();
}
}
// If the ref is not a MessageElement, then it must be an
// element that has already been deserialized. Use it directly.
value = (Element)ref;
return;
}
}
}
/**
* This method is invoked after startElement when the element
* requires deserialization (i.e. the element is not an href and
* the value is not nil.) DeserializerImpl provides default
* behavior, which simply involves obtaining a correct
* Deserializer and plugging its handler.
* @param namespace is the namespace of the element
* @param localName is the name of the element
* @param prefix is the prefix of the element
* @param attributes are the attributes on the element
* @param context is the DeserializationContext
* @throws SAXException not thrown
*/
public void onStartElement(String namespace, String localName,
String prefix, Attributes attributes,
DeserializationContext context)
throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("onStartElement: {" + namespace + "}" + localName);
}
}
/**
* onStartChild is called on each child element.
* The default behavior supplied by DeserializationImpl is to do nothing.
* A specific deserializer may perform other tasks. For example a
* BeanDeserializer will construct a deserializer for the indicated
* property and return it.
* @param namespace is the namespace of the child element
* @param localName is the local name of the child element
* @param prefix is the prefix used on the name of the child element
* @param attributes are the attributes of the child element
* @param context is the deserialization context.
* @return is a Deserializer to use to deserialize a child (must be
* a derived class of SOAPHandler) or null if no deserialization should
* be performed.
* @throws SAXException not thrown
*/
public SOAPHandler onStartChild(String namespace, String localName,
String prefix, Attributes attributes,
DeserializationContext context)
throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("onStartChild: {" + namespace + "}" + localName);
}
SOAPHandler hdlr = new JDOMAxisDeserializer ();
((Deserializer)hdlr).registerValueTarget
(new DeserializerTarget(this, value));
return hdlr;
}
/**
* Gets called when the text of an XML element handled by this
* deserializer is reached.
* We simply add the current text to a text buffer. The completed
* buffer will be evaluated in the onEndElement() method.
* @param chars characters
* @param start start
* @param end end
* @throws SAXException SAXException
*/
public void characters(char [] chars, int start, int end)
throws SAXException {
String s = new String(chars, start, (end-start));
if (textBuffer == null) {
textBuffer = new StringBuffer(s);
} else {
textBuffer.append(s);
}
}
/**
* endElement is called when the end element tag is reached.
* It handles href/id information for multi-ref processing
* and invokes the valueComplete() method of the deserializer
* which sets the targets with the deserialized value.
* @param namespace is the namespace of the child element
* @param localName is the local name of the child element
* @param context is the deserialization context
* @throws SAXException not thrown
*/
public void endElement(String namespace, String localName,
DeserializationContext context)
throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("endElement: {" + namespace + "}" + localName);
}
super.endElement(namespace, localName, context);
if (textBuffer!=null) {
value.addContent(new Text(textBuffer.toString()));
textBuffer=null;
}
valueComplete();
}
/**
* onEndElement is called by endElement. It is not called
* if the element has an href.
* @param namespace is the namespace of the child element
* @param localName is the local name of the child element
* @param context is the deserialization context
* @throws SAXException not thrown
*/
public void onEndElement(String namespace, String localName,
DeserializationContext context)
throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("onEndElement: {" + namespace + "}" + localName);
}
}
/**
* Get the deserialized value.
* @return Object representing deserialized value or null
*/
public Object getValue() {
if (logger.isDebugEnabled ()) {
logger.debug ("getValue returns: " + value);
}
return value;
}
/**
* Set the deserialized value.
* @param value Object representing deserialized value
*/
public void setValue(Object value) {
if (logger.isDebugEnabled ()) {
logger.debug ("setValue to: " + value);
}
this.value = (Element)value;
}
/**
* If the deserializer has component values (like ArrayDeserializer)
* this method gets the specific component via the hint.
* The default implementation returns null.
* @return Object representing deserialized value or null
* @param hint a hint
*/
public Object getValue(Object hint) {
if (logger.isDebugEnabled ()) {
logger.debug ("getValue with hint returns: null");
}
return null;
}
/**
* From axis callback interface.
* @param value the value
* @param hint the hint
* @throws SAXException not thrown
*/
public void setValue(Object value, Object hint) throws SAXException {
}
/**
* If the deserializer has component values (like ArrayDeserializer)
* this method sets the specific component via the hint.
* The default implementation does nothing.
* @param value Object representing deserialized value or null
* @throws SAXException if an error occurs
* @param hint a hint
*/
public void setChildValue(Object value, Object hint) throws SAXException {
if (logger.isDebugEnabled ()) {
logger.debug ("setChildValue with hint " + hint + " to: " + value);
}
Element child = (Element)value;
if (!child.getNamespace().equals (Namespace.NO_NAMESPACE)
|| !child.getName().equals("multiRef")) {
this.value.addContent (child);
} else {
for (Iterator i = child.getChildren().iterator(); i.hasNext();) {
this.value.addContent ((Element)((Element)i.next()).clone());
}
if (child.getText () != null) {
this.value.addContent (child.getText ());
}
}
}
/**
* In some circumstances an element may not have
* a type attribute, but a default type qname is known from
* information in the container. For example,
* an element of an array may not have a type= attribute,
* so the default qname is the component type of the array.
* This method is used to communicate the default type information
* to the deserializer.
* @param qName default type
*/
public void setDefaultType(QName qName) {
defaultType = qName;
}
/**
* Return the default type.
* @return default type
* @see #setDefaultType
*/
public QName getDefaultType() {
return defaultType;
}
/**
* For deserializers of non-primitives, the value may not be
* known until later (due to multi-referencing). In such
* cases the deserializer registers Target object(s). When
* the value is known, the set(value) will be invoked for
* each Target registered with the Deserializer. The Target
* object abstracts the function of setting a target with a
* value. See the Target interface for more info.
* @param target Target
*/
public void registerValueTarget(Target target) {
if (targets == null) {
targets = new Vector();
}
targets.addElement(target);
}
/**
* Get the Value Targets of the Deserializer.
* @return Vector of Target objects or null
*/
public Vector getValueTargets() {
return targets;
}
/**
* Remove the Value Targets of the Deserializer.
*/
public void removeValueTargets() {
if (targets != null) {
targets.clear();
targets = null;
}
}
/**
* Move someone else's targets to our own (see DeserializationContext)
*
* The DeserializationContext only allows one Deserializer to
* wait for a unknown multi-ref'ed value. So to ensure
* that all of the targets are updated, this method is invoked
* to copy the Target objects to the waiting Deserializer.
* @param other is the Deserializer to copy targets from.
*/
public void moveValueTargets(Deserializer other) {
if ((other == null) || (other.getValueTargets() == null)) {
return;
}
if (targets == null) {
targets = new Vector();
}
Enumeration e = other.getValueTargets().elements();
while (e.hasMoreElements()) {
targets.addElement(e.nextElement());
}
other.removeValueTargets();
}
/**
* Some deserializers (ArrayDeserializer) require
* all of the component values to be known before the
* value is complete.
* (For the ArrayDeserializer this is important because
* the elements are stored in an ArrayList, and all values
* must be known before the ArrayList is converted into the
* expected array.
*
* This routine is used to indicate when the components are ready.
* The default (true) is useful for most Deserializers.
* @return <code>true</code> if component ready
*/
public boolean componentsReady() {
return true;
}
/**
* The valueComplete() method is invoked when the
* end tag of the element is read. This results
* in the setting of all registered Targets (see
* registerValueTarget).
* Note that the valueComplete() only processes
* the Targets if componentReady() returns true.
* So if you override componentReady(), then your
* specific Deserializer will need to call valueComplete()
* when your components are ready (See ArrayDeserializer)
* @throws SAXException not thrown
*/
public void valueComplete() throws SAXException {
if (componentsReady()) {
if (targets != null) {
Enumeration e = targets.elements();
while (e.hasMoreElements()) {
Target target = (Target)e.nextElement();
target.set(value);
}
// Don't need targets any more, so clear them
removeValueTargets();
}
}
}
/**
* JAX-RPC compliant method which returns mechanism type.
* @return the mechanism
*/
public String getMechanismType() {
return Constants.AXIS_SAX;
}
}