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: SAXEventBufferAxisSerializer.java,v 1.3 2007/03/27 21:59:44 mlipp Exp $
021: *
022: * $Log: SAXEventBufferAxisSerializer.java,v $
023: * Revision 1.3 2007/03/27 21:59:44 mlipp
024: * Fixed lots of checkstyle warnings.
025: *
026: * Revision 1.2 2006/09/29 12:32:13 drmlipp
027: * Consistently using WfMOpen as projct name now.
028: *
029: * Revision 1.1.1.2 2004/08/18 15:17:39 drmlipp
030: * Update to 1.2
031: *
032: * Revision 1.3 2004/03/17 12:43:02 lipp
033: * Added arrayType generation for arrays.
034: *
035: * Revision 1.2 2003/06/27 08:51:44 lipp
036: * Fixed copyright/license information.
037: *
038: * Revision 1.1 2003/06/24 16:08:12 lipp
039: * Implemented invocation.
040: *
041: */
042: package de.danet.an.workflow.tools.soapclient;
043:
044: import java.io.IOException;
045: import java.io.Serializable;
046:
047: import java.util.HashSet;
048: import java.util.Iterator;
049: import java.util.Set;
050:
051: import javax.xml.namespace.QName;
052:
053: import org.w3c.dom.Element;
054:
055: import org.apache.axis.Constants;
056: import org.apache.axis.MessageContext;
057: import org.apache.axis.encoding.SerializationContext;
058: import org.apache.axis.encoding.Serializer;
059: import org.apache.axis.schema.SchemaVersion;
060: import org.apache.axis.soap.SOAPConstants;
061: import org.apache.axis.utils.Messages;
062: import org.apache.axis.wsdl.fromJava.Types;
063:
064: import org.xml.sax.Attributes;
065: import org.xml.sax.ContentHandler;
066: import org.xml.sax.Locator;
067: import org.xml.sax.SAXException;
068: import org.xml.sax.helpers.AttributesImpl;
069:
070: import de.danet.an.util.XMLUtil;
071: import de.danet.an.util.sax.HandlerStack;
072: import de.danet.an.util.sax.StackedHandler;
073:
074: import de.danet.an.workflow.api.SAXEventBuffer;
075:
076: /**
077: * General purpose serializer/deserializerFactory for SAXEventBuffer in
078: * the AXIS mplementation of the JAX-RPC interface.
079: * Creates a xml output for a given SAXEventBuffer,
080: * preserving the stucture and text content.
081: */
082: public class SAXEventBufferAxisSerializer implements Serializer,
083: Serializable {
084:
085: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
086: .getLog(SAXEventBufferAxisSerializer.class);
087:
088: /**
089: * Helper class that holds a namespace uri and a local name.
090: */
091: private class NsAndLocal {
092: private String namespace;
093: private String localName;
094:
095: /**
096: * Create a new object with attributes initialized to the
097: * given values.
098: * @param n the namespace
099: * @param l the local name
100: */
101: public NsAndLocal(String n, String l) {
102: namespace = n;
103: localName = l;
104: }
105:
106: /**
107: * Return the namespace.
108: * @return namespace
109: */
110: public String namespace() {
111: return namespace;
112: }
113:
114: /**
115: * Return the local name.
116: * @return local name
117: */
118: public String localName() {
119: return localName;
120: }
121:
122: /**
123: * Compare with other object.
124: * @param o other object
125: * @return <code>true</code> if equal
126: */
127: public boolean equals(Object o) {
128: String ons = ((NsAndLocal) o).namespace;
129: if (namespace == null && ons != null || namespace != null
130: && ons == null || namespace != null && ons != null
131: && !namespace.equals(ons)) {
132: return false;
133: }
134: return localName.equals(((NsAndLocal) o).localName);
135: }
136:
137: /**
138: * Return a hash code.
139: * @return the hash code
140: */
141: public int hashCode() {
142: return namespace.hashCode() ^ localName.hashCode();
143: }
144:
145: /**
146: * Return a string representation for debugging purposes.
147: * @return string representation
148: */
149: public String toString() {
150: return "{" + namespace + "}" + localName;
151: }
152: }
153:
154: /**
155: * Default constructor.
156: */
157: public SAXEventBufferAxisSerializer() {
158: }
159:
160: /**
161: * From the Serializer interface.
162: * @param name name
163: * @param attributes attributes
164: * @param value value
165: * @param context context
166: * @throws IOException IOException
167: */
168: public void serialize(QName name, Attributes attributes,
169: Object value, SerializationContext context)
170: throws IOException {
171: logger.debug("SAXEventBufferAxis serialize() called for \""
172: + name + "\"");
173: if (!(value instanceof SAXEventBuffer)) {
174: throw new IOException(Messages
175: .getMessage("can not serialize!"));
176: }
177:
178: MessageContext msgContext = context.getMessageContext();
179: SchemaVersion schema = SchemaVersion.SCHEMA_2001;
180: SOAPConstants soap = SOAPConstants.SOAP11_CONSTANTS;
181: if (msgContext != null) {
182: schema = msgContext.getSchemaVersion();
183: soap = msgContext.getSOAPConstants();
184: }
185:
186: try {
187: int typeIdx = attributes.getIndex(schema.getXsiURI(),
188: "type");
189: if (typeIdx >= 0) {
190: String prefixForSoapEnc = context.getPrefixForURI(soap
191: .getEncodingURI());
192: if (logger.isDebugEnabled()) {
193: logger.debug("Type is \""
194: + attributes.getValue(typeIdx) + "\""
195: + " (\"" + prefixForSoapEnc
196: + ":Array\" denotes array)");
197: }
198: if (attributes.getValue(typeIdx).equals(
199: prefixForSoapEnc + ":Array")
200: && attributes.getIndex(soap.getEncodingURI(),
201: soap.getAttrItemType()) == -1) {
202: AttributesImpl attrs = new AttributesImpl(
203: attributes);
204: attrs.addAttribute(soap.getEncodingURI(), soap
205: .getAttrItemType(), prefixForSoapEnc
206: + ":arrayType", "CDATA",
207: determineArrayType((SAXEventBuffer) value,
208: context));
209: attributes = attrs;
210: }
211: }
212:
213: context.startElement(name, attributes);
214: ((SAXEventBuffer) value).emit(new EventForwarder(context));
215: context.endElement();
216: logger.debug("SAXEventBuffer serialize() called");
217: } catch (SAXException e) {
218: logger.error("Problem creating XML argument: "
219: + e.getMessage(), e);
220: throw new IOException(e.getMessage());
221: }
222: }
223:
224: private class ArrayTypeExtractor extends StackedHandler {
225:
226: private SchemaVersion schema = SchemaVersion.SCHEMA_2001;
227: private int items = 0;
228: private boolean typeValid = true;
229:
230: /**
231: * Create a new event forwarder.
232: * @param context the context to forward to
233: */
234: public ArrayTypeExtractor(SerializationContext context) {
235: MessageContext msgContext = context.getMessageContext();
236: if (msgContext != null) {
237: schema = msgContext.getSchemaVersion();
238: }
239: }
240:
241: /**
242: * Receive notification of the beginning of an element.
243: *
244: * @param namespaceURI The Namespace URI, or the empty string if the
245: * element has no Namespace URI or if Namespace
246: * processing is not being performed.
247: * @param localName The local name (without prefix), or the
248: * empty string if Namespace processing is not being
249: * performed.
250: * @param qName The qualified name (with prefix), or the
251: * empty string if qualified names are not available.
252: * @param atts The attributes attached to the element. If
253: * there are no attributes, it shall be an empty
254: * Attributes object.
255: * @throws SAXException Any SAX exception, possibly
256: * wrapping another exception.
257: * @see #endElement
258: * @see org.xml.sax.Attributes
259: */
260: public void startElement(String namespaceURI, String localName,
261: String qName, Attributes atts) throws SAXException {
262: if (getStack().getRelativeDepth() != 1) {
263: return;
264: }
265: // top level element, i.e. item
266: items += 1;
267: setContextData("items", new Integer(items));
268: if (!typeValid) {
269: return;
270: }
271: int typeIdx = atts.getIndex(schema.getXsiURI(), "type");
272: if (typeIdx < 0) {
273: logger
274: .warn("Type declaration missing in array element "
275: + items);
276: typeValid = false;
277: setContextData("typesValid", Boolean.FALSE);
278: return;
279: }
280: String typeQName = atts.getValue(typeIdx);
281: NsAndLocal type = null;
282: int colonPos = typeQName.indexOf(':');
283: if (colonPos < 0) {
284: type = new NsAndLocal(null, typeQName);
285: } else {
286: String typeURI = getStack().getURIForPrefix(
287: typeQName.substring(0, colonPos));
288: if (typeURI == null) {
289: logger
290: .warn("Invalid type declaration in array element "
291: + items
292: + ": uses undefined prefix in qname");
293: typeValid = false;
294: setContextData("typesValid", Boolean.FALSE);
295: return;
296: }
297: type = new NsAndLocal(typeURI, typeQName
298: .substring(colonPos + 1));
299: }
300: if (logger.isDebugEnabled()) {
301: logger
302: .debug("Type of element " + items + " is "
303: + type);
304: }
305: ((Set) getContextData("types")).add(type);
306: }
307: }
308:
309: private String determineArrayType(SAXEventBuffer buf,
310: SerializationContext context) throws SAXException {
311: HandlerStack hs = new HandlerStack(new ArrayTypeExtractor(
312: context));
313: hs.setContextData("types", new HashSet());
314: hs.setContextData("items", new Integer(0));
315: hs.setContextData("typesValid", Boolean.TRUE);
316: hs.contentHandler().startElement("", "root", "root",
317: new AttributesImpl());
318: buf.emit(hs.contentHandler());
319: hs.contentHandler().endElement("", "root", "root");
320: Set types = (Set) hs.getContextData("types");
321: String xsdPref = context.getPrefixForURI(XMLUtil.XMLNS_SCHEMA);
322: String arrayType;
323: String dim = "[" + (Integer) hs.getContextData("items") + "]";
324: if (!((Boolean) hs.getContextData("typesValid")).booleanValue()
325: || types.size() > 1) {
326: arrayType = xsdPref + ":" + "ur-type" + dim;
327: } else {
328: Iterator i = types.iterator();
329: NsAndLocal type = (NsAndLocal) i.next();
330: String typePref = context.getPrefixForURI(type.namespace());
331: arrayType = typePref + ":" + type.localName() + dim;
332: }
333: if (logger.isDebugEnabled()) {
334: logger.debug("Array type is " + arrayType);
335: }
336: return arrayType;
337: }
338:
339: private class EventForwarder implements ContentHandler {
340:
341: private SerializationContext ctx;
342:
343: /**
344: * Receive an object for locating the origin of SAX document events.
345: *
346: * @param locator An object that can return the location of any
347: * SAX document event.
348: * @see org.xml.sax.Locator
349: */
350: public void setDocumentLocator(Locator locator) {
351: // ignore
352: }
353:
354: /**
355: * Create a new event forwarder.
356: * @param context the context to forward to
357: */
358: public EventForwarder(SerializationContext context) {
359: ctx = context;
360: }
361:
362: /**
363: * Receive notification of the beginning of a document.
364: *
365: * @throws SAXException Any SAX exception, possibly
366: * wrapping another exception.
367: * @see #endDocument
368: */
369: public void startDocument() throws SAXException {
370: // shouldn't be called
371: }
372:
373: /**
374: * Receive notification of the end of a document.
375: *
376: * @throws SAXException Any SAX exception, possibly
377: * wrapping another exception.
378: * @see #startDocument
379: */
380: public void endDocument() throws SAXException {
381: // shouldn't be called
382: }
383:
384: /**
385: * Begin the scope of a prefix-URI Namespace mapping.
386: *
387: * @param prefix The Namespace prefix being declared.
388: * @param uri The Namespace URI the prefix is mapped to.
389: * @throws SAXException The client may throw an
390: * exception during processing.
391: * @see #endPrefixMapping
392: * @see #startElement
393: */
394: public void startPrefixMapping(String prefix, String uri)
395: throws SAXException {
396: ctx.registerPrefixForURI(prefix, uri);
397: }
398:
399: /**
400: * End the scope of a prefix-URI mapping.
401: *
402: * @param prefix The prefix that was being mapping.
403: * @throws SAXException The client may throw
404: * an exception during processing.
405: * @see #startPrefixMapping
406: * @see #endElement
407: */
408: public void endPrefixMapping(String prefix) throws SAXException {
409: // nothing to do
410: }
411:
412: /**
413: * Receive notification of the beginning of an element.
414: *
415: * @param namespaceURI The Namespace URI, or the empty string if the
416: * element has no Namespace URI or if Namespace
417: * processing is not being performed.
418: * @param localName The local name (without prefix), or the
419: * empty string if Namespace processing is not being
420: * performed.
421: * @param qName The qualified name (with prefix), or the
422: * empty string if qualified names are not available.
423: * @param atts The attributes attached to the element. If
424: * there are no attributes, it shall be an empty
425: * Attributes object.
426: * @throws SAXException Any SAX exception, possibly
427: * wrapping another exception.
428: * @see #endElement
429: * @see org.xml.sax.Attributes
430: */
431: public void startElement(String namespaceURI, String localName,
432: String qName, Attributes atts) throws SAXException {
433: try {
434: AttributesImpl a = new AttributesImpl();
435: // Axis has some problems with namespaces reported
436: // as attributes, filter them out
437: for (int i = 0; i < atts.getLength(); i++) {
438: if (!atts.getURI(i).equals(XMLUtil.XMLNS_NS)) {
439: a.addAttribute(atts.getURI(i), atts
440: .getLocalName(i), atts.getQName(i),
441: atts.getType(i), atts.getValue(i));
442: }
443: }
444: ctx.startElement(new QName(namespaceURI, localName), a);
445: } catch (IOException e) {
446: throw new SAXException(e);
447: }
448: }
449:
450: /**
451: * Receive notification of the end of an element.
452: *
453: * @param namespaceURI The Namespace URI, or the empty string if the
454: * element has no Namespace URI or if Namespace
455: * processing is not being performed.
456: * @param localName The local name (without prefix), or the
457: * empty string if Namespace processing is not being
458: * performed.
459: * @param qName The qualified XML 1.0 name (with prefix), or the
460: * empty string if qualified names are not available.
461: * @throws SAXException Any SAX exception, possibly
462: * wrapping another exception.
463: */
464: public void endElement(String namespaceURI, String localName,
465: String qName) throws SAXException {
466: try {
467: ctx.endElement();
468: } catch (IOException e) {
469: throw new SAXException(e);
470: }
471: }
472:
473: /**
474: * Receive notification of character data.
475: *
476: * @param ch The characters from the XML document.
477: * @param start The start position in the array.
478: * @param length The number of characters to read from the array.
479: * @throws SAXException Any SAX exception, possibly
480: * wrapping another exception.
481: * @see #ignorableWhitespace
482: * @see org.xml.sax.Locator
483: */
484: public void characters(char[] ch, int start, int length)
485: throws SAXException {
486: try {
487: ctx.writeChars(ch, start, length);
488: } catch (IOException e) {
489: throw new SAXException(e);
490: }
491: }
492:
493: /**
494: * Receive notification of ignorable whitespace in element content.
495: *
496: * @param ch The characters from the XML document.
497: * @param start The start position in the array.
498: * @param length The number of characters to read from the array.
499: * @throws SAXException Any SAX exception, possibly
500: * wrapping another exception.
501: * @see #characters
502: */
503: public void ignorableWhitespace(char[] ch, int start, int length)
504: throws SAXException {
505: try {
506: ctx.writeChars(ch, start, length);
507: } catch (IOException e) {
508: throw new SAXException(e);
509: }
510: }
511:
512: /**
513: * Receive notification of a processing instruction.
514: *
515: * @param target The processing instruction target.
516: * @param data The processing instruction data, or null if
517: * none was supplied. The data does not include any
518: * whitespace separating it from the target.
519: * @throws SAXException Any SAX exception, possibly
520: * wrapping another exception.
521: */
522: public void processingInstruction(String target, String data)
523: throws SAXException {
524: }
525:
526: /**
527: * Receive notification of a skipped entity.
528: *
529: * @param name The name of the skipped entity. If it is a
530: * parameter entity, the name will begin with '%', and if it is
531: * the external DTD subset, it will be the string "[dtd]".
532: * @throws SAXException Any SAX exception, possibly
533: * wrapping another exception.
534: */
535: public void skippedEntity(String name) throws SAXException {
536: }
537: }
538:
539: /**
540: * From the Serializer interface (Axis pre 1.1 version).
541: * Currently we do not support serialization.
542: * @param types types
543: * @return false
544: */
545: public boolean writeSchema(Types types) {
546: return false;
547: }
548:
549: /**
550: * Return XML schema for the specified type, suitable for insertion into
551: * the <types> element of a WSDL document, or underneath an
552: * <element> or <attribute> declaration.
553: *
554: * @param javaType the Java Class we're writing out schema for
555: * @param types the Java2WSDL Types object which holds the context
556: * for the WSDL being generated
557: * @return a type element containing a schema simpleType/complexType
558: * @throws Exception if an error occurs
559: * @see org.apache.axis.wsdl.fromJava.Types
560: */
561: public Element writeSchema(Class javaType, Types types)
562: throws Exception {
563: return null;
564: }
565:
566: /**
567: * From the Serializer and Deserializerinterface.
568: * @return Constants.AXIS_SAX
569: */
570: public String getMechanismType() {
571: return Constants.AXIS_SAX;
572: }
573:
574: }
|