001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: JDOMAxisDeserializer.java,v 1.2 2006/09/29 12:32:13 drmlipp Exp $
021: *
022: * $Log: JDOMAxisDeserializer.java,v $
023: * Revision 1.2 2006/09/29 12:32:13 drmlipp
024: * Consistently using WfMOpen as projct name now.
025: *
026: * Revision 1.1.1.1 2003/06/30 20:05:17 drmlipp
027: * Initial import
028: *
029: * Revision 1.4 2003/06/27 08:51:44 lipp
030: * Fixed copyright/license information.
031: *
032: * Revision 1.3 2003/06/26 22:07:46 lipp
033: * Fixed deserializer.
034: *
035: * Revision 1.2 2003/06/26 15:32:57 lipp
036: * Improved version.
037: *
038: * Revision 1.1 2003/06/25 15:50:29 lipp
039: * New SOAP client that makes wsif obsolete.
040: *
041: */
042: package de.danet.an.workflow.tools.soapclient;
043:
044: import java.util.Enumeration;
045: import java.util.Iterator;
046: import java.util.Vector;
047:
048: import javax.xml.namespace.QName;
049:
050: import org.apache.axis.Constants;
051: import org.apache.axis.Part;
052: import org.apache.axis.encoding.DeserializationContext;
053: import org.apache.axis.encoding.Deserializer;
054: import org.apache.axis.encoding.DeserializerTarget;
055: import org.apache.axis.encoding.Target;
056: import org.apache.axis.message.EnvelopeHandler;
057: import org.apache.axis.message.MessageElement;
058: import org.apache.axis.message.SAX2EventRecorder;
059: import org.apache.axis.message.SOAPHandler;
060: import org.apache.axis.soap.SOAPConstants;
061:
062: import org.jdom.Element;
063: import org.jdom.Namespace;
064: import org.jdom.Text;
065: import org.xml.sax.Attributes;
066: import org.xml.sax.SAXException;
067: import org.xml.sax.helpers.DefaultHandler;
068:
069: /**
070: * General purpose serializer/deserializerFactory for JDOM in the AXIS
071: * implementation of the JAX-RPC interface.
072: * Creates a simple org.jdom.Element for the parsed xml content,
073: * preserving the stucture and text content.
074: * This class uses a stack to notice the created nodes. So we do not
075: * need to generate new instances of this class for every recursive
076: * descent (initialized by the onStartChild() call).
077: */
078: public class JDOMAxisDeserializer extends SOAPHandler implements
079: Deserializer {
080:
081: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
082: .getLog(JDOMAxisDeserializer.class);
083:
084: private Element value = null;
085: private QName defaultType;
086: private Vector targets = null;
087:
088: private StringBuffer textBuffer = null;
089:
090: /**
091: * Default constructor.
092: */
093: public JDOMAxisDeserializer() {
094: super ();
095: }
096:
097: /**
098: * This method is invoked when an element start tag is encountered.
099: * @param namespace is the namespace of the element
100: * @param localName is the name of the element
101: * @param prefix is the prefix
102: * @param attributes are the attributes on the element
103: * @param context is the DeserializationContext
104: * @throws SAXException not thrown
105: */
106: public void startElement(String namespace, String localName,
107: String prefix, Attributes attributes,
108: DeserializationContext context) throws SAXException {
109: if (logger.isDebugEnabled()) {
110: logger.debug("startElement: {" + namespace + "}"
111: + localName);
112: }
113: super .startElement(namespace, localName, prefix, attributes,
114: context);
115:
116: // axis sometimes resends the start event, ignore
117: if (value != null) {
118: return;
119: }
120:
121: value = new Element(localName, prefix, namespace);
122: if (attributes != null) {
123: for (int i = 0; i < attributes.getLength(); i++) {
124: String aName = attributes.getLocalName(i); // Attr name
125: if ("".equals(aName)) {
126: aName = attributes.getQName(i);
127: }
128: String aValue = attributes.getValue(i);
129: value.setAttribute(aName, aValue);
130: }
131: }
132:
133: String id = attributes.getValue("id");
134: if (id != null) {
135: context.addObjectById(id, value);
136: }
137:
138: SOAPConstants soapConstants = context.getMessageContext()
139: .getSOAPConstants();
140: String href = attributes.getValue(soapConstants.getAttrHref());
141: if (href == null) {
142: onStartElement(namespace, localName, prefix, attributes,
143: context);
144: } else {
145: Object ref = context.getObjectByRef(href);
146: if (ref == null) {
147: // Nothing yet... register for later interest.
148: context.registerFixup(href, this );
149: } else if (ref instanceof MessageElement) {
150: context
151: .replaceElementHandler(new EnvelopeHandler(this ));
152: SAX2EventRecorder r = context.getRecorder();
153: context.setRecorder(null);
154: ((MessageElement) ref)
155: .publishToHandler((DefaultHandler) context);
156: context.setRecorder(r);
157: } else {
158: if (!href.startsWith("#") && defaultType != null
159: && ref instanceof Part) {
160: //For attachments this is the end of the road--
161: //invoke deserializer
162: Deserializer dser = context
163: .getDeserializerForType(defaultType);
164: if (null != dser) {
165: dser.startElement(namespace, localName, prefix,
166: attributes, context);
167: ref = dser.getValue();
168: }
169: }
170:
171: // If the ref is not a MessageElement, then it must be an
172: // element that has already been deserialized. Use it directly.
173: value = (Element) ref;
174: return;
175: }
176: }
177: }
178:
179: /**
180: * This method is invoked after startElement when the element
181: * requires deserialization (i.e. the element is not an href and
182: * the value is not nil.) DeserializerImpl provides default
183: * behavior, which simply involves obtaining a correct
184: * Deserializer and plugging its handler.
185: * @param namespace is the namespace of the element
186: * @param localName is the name of the element
187: * @param prefix is the prefix of the element
188: * @param attributes are the attributes on the element
189: * @param context is the DeserializationContext
190: * @throws SAXException not thrown
191: */
192: public void onStartElement(String namespace, String localName,
193: String prefix, Attributes attributes,
194: DeserializationContext context) throws SAXException {
195: if (logger.isDebugEnabled()) {
196: logger.debug("onStartElement: {" + namespace + "}"
197: + localName);
198: }
199: }
200:
201: /**
202: * onStartChild is called on each child element.
203: * The default behavior supplied by DeserializationImpl is to do nothing.
204: * A specific deserializer may perform other tasks. For example a
205: * BeanDeserializer will construct a deserializer for the indicated
206: * property and return it.
207: * @param namespace is the namespace of the child element
208: * @param localName is the local name of the child element
209: * @param prefix is the prefix used on the name of the child element
210: * @param attributes are the attributes of the child element
211: * @param context is the deserialization context.
212: * @return is a Deserializer to use to deserialize a child (must be
213: * a derived class of SOAPHandler) or null if no deserialization should
214: * be performed.
215: * @throws SAXException not thrown
216: */
217: public SOAPHandler onStartChild(String namespace, String localName,
218: String prefix, Attributes attributes,
219: DeserializationContext context) throws SAXException {
220: if (logger.isDebugEnabled()) {
221: logger.debug("onStartChild: {" + namespace + "}"
222: + localName);
223: }
224: SOAPHandler hdlr = new JDOMAxisDeserializer();
225: ((Deserializer) hdlr)
226: .registerValueTarget(new DeserializerTarget(this , value));
227: return hdlr;
228: }
229:
230: /**
231: * Gets called when the text of an XML element handled by this
232: * deserializer is reached.
233: * We simply add the current text to a text buffer. The completed
234: * buffer will be evaluated in the onEndElement() method.
235: * @param chars characters
236: * @param start start
237: * @param end end
238: * @throws SAXException SAXException
239: */
240: public void characters(char[] chars, int start, int end)
241: throws SAXException {
242: String s = new String(chars, start, (end - start));
243: if (textBuffer == null) {
244: textBuffer = new StringBuffer(s);
245: } else {
246: textBuffer.append(s);
247: }
248: }
249:
250: /**
251: * endElement is called when the end element tag is reached.
252: * It handles href/id information for multi-ref processing
253: * and invokes the valueComplete() method of the deserializer
254: * which sets the targets with the deserialized value.
255: * @param namespace is the namespace of the child element
256: * @param localName is the local name of the child element
257: * @param context is the deserialization context
258: * @throws SAXException not thrown
259: */
260: public void endElement(String namespace, String localName,
261: DeserializationContext context) throws SAXException {
262: if (logger.isDebugEnabled()) {
263: logger.debug("endElement: {" + namespace + "}" + localName);
264: }
265: super .endElement(namespace, localName, context);
266:
267: if (textBuffer != null) {
268: value.addContent(new Text(textBuffer.toString()));
269: textBuffer = null;
270: }
271: valueComplete();
272: }
273:
274: /**
275: * onEndElement is called by endElement. It is not called
276: * if the element has an href.
277: * @param namespace is the namespace of the child element
278: * @param localName is the local name of the child element
279: * @param context is the deserialization context
280: * @throws SAXException not thrown
281: */
282: public void onEndElement(String namespace, String localName,
283: DeserializationContext context) throws SAXException {
284: if (logger.isDebugEnabled()) {
285: logger.debug("onEndElement: {" + namespace + "}"
286: + localName);
287: }
288: }
289:
290: /**
291: * Get the deserialized value.
292: * @return Object representing deserialized value or null
293: */
294: public Object getValue() {
295: if (logger.isDebugEnabled()) {
296: logger.debug("getValue returns: " + value);
297: }
298: return value;
299: }
300:
301: /**
302: * Set the deserialized value.
303: * @param value Object representing deserialized value
304: */
305: public void setValue(Object value) {
306: if (logger.isDebugEnabled()) {
307: logger.debug("setValue to: " + value);
308: }
309: this .value = (Element) value;
310: }
311:
312: /**
313: * If the deserializer has component values (like ArrayDeserializer)
314: * this method gets the specific component via the hint.
315: * The default implementation returns null.
316: * @return Object representing deserialized value or null
317: * @param hint a hint
318: */
319: public Object getValue(Object hint) {
320: if (logger.isDebugEnabled()) {
321: logger.debug("getValue with hint returns: null");
322: }
323: return null;
324: }
325:
326: /**
327: * From axis callback interface.
328: * @param value the value
329: * @param hint the hint
330: * @throws SAXException not thrown
331: */
332: public void setValue(Object value, Object hint) throws SAXException {
333: }
334:
335: /**
336: * If the deserializer has component values (like ArrayDeserializer)
337: * this method sets the specific component via the hint.
338: * The default implementation does nothing.
339: * @param value Object representing deserialized value or null
340: * @throws SAXException if an error occurs
341: * @param hint a hint
342: */
343: public void setChildValue(Object value, Object hint)
344: throws SAXException {
345: if (logger.isDebugEnabled()) {
346: logger.debug("setChildValue with hint " + hint + " to: "
347: + value);
348: }
349: Element child = (Element) value;
350: if (!child.getNamespace().equals(Namespace.NO_NAMESPACE)
351: || !child.getName().equals("multiRef")) {
352: this .value.addContent(child);
353: } else {
354: for (Iterator i = child.getChildren().iterator(); i
355: .hasNext();) {
356: this .value.addContent((Element) ((Element) i.next())
357: .clone());
358: }
359: if (child.getText() != null) {
360: this .value.addContent(child.getText());
361: }
362: }
363: }
364:
365: /**
366: * In some circumstances an element may not have
367: * a type attribute, but a default type qname is known from
368: * information in the container. For example,
369: * an element of an array may not have a type= attribute,
370: * so the default qname is the component type of the array.
371: * This method is used to communicate the default type information
372: * to the deserializer.
373: * @param qName default type
374: */
375: public void setDefaultType(QName qName) {
376: defaultType = qName;
377: }
378:
379: /**
380: * Return the default type.
381: * @return default type
382: * @see #setDefaultType
383: */
384: public QName getDefaultType() {
385: return defaultType;
386: }
387:
388: /**
389: * For deserializers of non-primitives, the value may not be
390: * known until later (due to multi-referencing). In such
391: * cases the deserializer registers Target object(s). When
392: * the value is known, the set(value) will be invoked for
393: * each Target registered with the Deserializer. The Target
394: * object abstracts the function of setting a target with a
395: * value. See the Target interface for more info.
396: * @param target Target
397: */
398: public void registerValueTarget(Target target) {
399: if (targets == null) {
400: targets = new Vector();
401: }
402: targets.addElement(target);
403: }
404:
405: /**
406: * Get the Value Targets of the Deserializer.
407: * @return Vector of Target objects or null
408: */
409: public Vector getValueTargets() {
410: return targets;
411: }
412:
413: /**
414: * Remove the Value Targets of the Deserializer.
415: */
416: public void removeValueTargets() {
417: if (targets != null) {
418: targets.clear();
419: targets = null;
420: }
421: }
422:
423: /**
424: * Move someone else's targets to our own (see DeserializationContext)
425: *
426: * The DeserializationContext only allows one Deserializer to
427: * wait for a unknown multi-ref'ed value. So to ensure
428: * that all of the targets are updated, this method is invoked
429: * to copy the Target objects to the waiting Deserializer.
430: * @param other is the Deserializer to copy targets from.
431: */
432: public void moveValueTargets(Deserializer other) {
433: if ((other == null) || (other.getValueTargets() == null)) {
434: return;
435: }
436: if (targets == null) {
437: targets = new Vector();
438: }
439: Enumeration e = other.getValueTargets().elements();
440: while (e.hasMoreElements()) {
441: targets.addElement(e.nextElement());
442: }
443: other.removeValueTargets();
444: }
445:
446: /**
447: * Some deserializers (ArrayDeserializer) require
448: * all of the component values to be known before the
449: * value is complete.
450: * (For the ArrayDeserializer this is important because
451: * the elements are stored in an ArrayList, and all values
452: * must be known before the ArrayList is converted into the
453: * expected array.
454: *
455: * This routine is used to indicate when the components are ready.
456: * The default (true) is useful for most Deserializers.
457: * @return <code>true</code> if component ready
458: */
459: public boolean componentsReady() {
460: return true;
461: }
462:
463: /**
464: * The valueComplete() method is invoked when the
465: * end tag of the element is read. This results
466: * in the setting of all registered Targets (see
467: * registerValueTarget).
468: * Note that the valueComplete() only processes
469: * the Targets if componentReady() returns true.
470: * So if you override componentReady(), then your
471: * specific Deserializer will need to call valueComplete()
472: * when your components are ready (See ArrayDeserializer)
473: * @throws SAXException not thrown
474: */
475: public void valueComplete() throws SAXException {
476: if (componentsReady()) {
477: if (targets != null) {
478: Enumeration e = targets.elements();
479: while (e.hasMoreElements()) {
480: Target target = (Target) e.nextElement();
481: target.set(value);
482: }
483: // Don't need targets any more, so clear them
484: removeValueTargets();
485: }
486: }
487: }
488:
489: /**
490: * JAX-RPC compliant method which returns mechanism type.
491: * @return the mechanism
492: */
493: public String getMechanismType() {
494: return Constants.AXIS_SAX;
495: }
496:
497: }
|