/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.axis2.datasource.jaxb; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.NamespaceContext; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * In some cases, we want to marshal an array of objects as a series * of elements instead of a single element containing items. * * Unfortunately there is no way to tell the JAXB marshal method to * marshal an array or List as a series of elements. * * Instead, we use the JAXB marshal method to output a single element * with items and then filter the write events to transform it into * a series of elements. * * Thus * <myElement> * <item>Hello</item> * <item>World</item> * </myElement> * Becomes * <myElement>Hello</myElement> * <myElement>World</myElement> * * Special care is taken to ensure that namespace declarations are property preserved. */ public class XMLStreamWriterArrayFilter implements XMLStreamWriter { private static final Log log = LogFactory.getLog(XMLStreamWriterArrayFilter.class); XMLStreamWriter writer; int depth = 0; boolean isBuffering = true; List<List<String>> bufferedCommands = new ArrayList<List<String>>(); // The extensive debug was used during development but is not necessary in production. // Change DEBUG_ENABLED = log.isDebugEnabled(); // to get extensive debug output. private static boolean DEBUG_ENABLED = false; private static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance"; public XMLStreamWriterArrayFilter(XMLStreamWriter writer) throws XMLStreamException { super(); if (DEBUG_ENABLED) { log.debug("XMLStreamWriterArrayFilter " + writer); } this.writer = writer; } public void close() throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("close"); } if (writer != null) { writer.close(); } } public void flush() throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("flush"); } if (writer != null) { writer.flush(); } } public NamespaceContext getNamespaceContext() { if (DEBUG_ENABLED) { log.debug("getNamespaceContext"); } return writer.getNamespaceContext(); } public String getPrefix(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("getPrefix " + arg0); } return writer.getPrefix(arg0); } public Object getProperty(String arg0) throws IllegalArgumentException { if (DEBUG_ENABLED) { log.debug("getProperty " + arg0); } return writer.getProperty(arg0); } public void setDefaultNamespace(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("setDefaultNamespace " + arg0); } writer.setDefaultNamespace(arg0); } public void setNamespaceContext(NamespaceContext arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("setNamespaceContext " + arg0); } writer.setNamespaceContext(arg0); } public void setPrefix(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("setPrefix " + arg0 + " " + arg1); } writer.setPrefix(arg0, arg1); } public void writeAttribute(String prefix, String uri, String localName, String value) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeAttribute " + prefix + " " + uri + " " + localName + " " + value); } writer.writeAttribute(prefix, uri, localName, value); } public void writeAttribute(String arg0, String arg1, String arg2) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeAttribute " + arg0 + " " + arg1 + " " + arg2 ); } writer.writeAttribute(arg0, arg1, arg2); } public void writeAttribute(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeAttribute " + arg0 + " " + arg1); } writer.writeAttribute(arg0, arg1); } public void writeCData(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeCData " + arg0 ); } writer.writeCData(arg0); } public void writeCharacters(char[] arg0, int arg1, int arg2) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeCharacters " + arg0 + " " + arg1 + " " + arg2 ); } writer.writeCharacters(arg0, arg1, arg2); } public void writeCharacters(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeCharacters " + arg0); } writer.writeCharacters(arg0); } public void writeComment(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeComment " + arg0 ); } writer.writeComment(arg0); } public void writeDefaultNamespace(String uri) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeDefaultNamespace (" + uri + ")" ); } if (isBuffering) { if (DEBUG_ENABLED) { log.debug(" Supress writeDefaultNamespace on top element"); } } else if (depth >= 2) { writer.writeDefaultNamespace(uri); } } public void writeDTD(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeDTD " + arg0 ); } writer.writeDTD(arg0); } public void writeEmptyElement(String arg0, String arg1, String arg2) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeEmptyElement" + arg0 + " " + arg1 + " " + arg2 ); } writeStartElement(arg0, arg1, arg2); writeEndElement(); } public void writeEmptyElement(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeEmptyElement " + arg0 + " " + arg1); } writeStartElement(arg0, arg1); writeEndElement(); } public void writeEmptyElement(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeEmptyElement " + arg0 ); } writeStartElement(arg0); writeEndElement(); } public void writeEndDocument() throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeEndDocument " ); } writer.writeEndDocument(); } public void writeEndElement() throws XMLStreamException { depth--; if (DEBUG_ENABLED) { log.debug("writeEndElement " ); } if (depth != 0) { writer.writeEndElement(); } else { if (DEBUG_ENABLED) { log.debug(" Suppress writeEndElement for the top element..but performing a flush"); } writer.flush(); } } public void writeEntityRef(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeEntityRef " + arg0 ); } writer.writeEntityRef(arg0); } public void writeNamespace(String prefix, String uri) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeNamespace (" + prefix + ") (" + uri + ")" ); } // The namespaces are buffered while reading the root element. // They will be written out by writeCommands when the child // elements are encountered. if (isBuffering) { if ("".equals(prefix)) { if (DEBUG_ENABLED) { log.debug(" Supress default write namespace on top element to avoid collision"); } } else { List<String> command = new ArrayList<String>(); command.add("writeNamespace"); command.add(prefix); command.add(uri); bufferCommand(command); } } else if (depth == 2 && XSI_URI.equals(uri) && "xsi".equals(prefix)) { // The operation element already has an xsi namespace declaration; // thus this one is redundant and only makes the message larger. if (DEBUG_ENABLED) { log.debug(" Supressing xsi namespace declaration on array item"); } } else if (depth >= 2) { writer.writeNamespace(prefix, uri); } } public void writeProcessingInstruction(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeProcessingInstruction " + arg0 + " " + arg1 ); } writer.writeProcessingInstruction(arg0, arg1); } public void writeProcessingInstruction(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeProcessingInstruction " + arg0); } writer.writeProcessingInstruction(arg0); } public void writeStartDocument() throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartDocument " ); } writer.writeStartDocument(); } public void writeStartDocument(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartDocument " + arg0 + " " + arg1 ); } writer.writeStartDocument(arg0, arg1); } public void writeStartDocument(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartDocument " + arg0 ); } writer.writeStartDocument(arg0); } public void writeStartElement(String arg0, String arg1, String arg2) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartElement " + arg0 + " " + arg1 + " " + arg2 ); } depth++; if (depth > 1) { isBuffering = false; } // The start element is buffered when we encounter the // outermost element. The buffered event is written // (instead of the child element tag). if (isBuffering) { List<String> command = new ArrayList<String>(); command.add("writeStartElement"); command.add(arg0); command.add(arg1); command.add(arg2); bufferCommand(command); } else if (depth == 2) { writeCommands(); } else { writer.writeStartElement(arg0, arg1, arg2); } } public void writeStartElement(String arg0, String arg1) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartElement " + arg0 + " " + arg1); } depth++; if (depth > 1) { isBuffering = false; } // The start element is buffered when we encounter the // outermost element. The buffered event is written // (instead of the child element tag). if (isBuffering) { List<String> command = new ArrayList<String>(); command.add("writeStartElement"); command.add(arg0); command.add(arg1); bufferCommand(command); } else if (depth == 2) { writeCommands(); } else { writer.writeStartElement(arg0, arg1); } } public void writeStartElement(String arg0) throws XMLStreamException { if (DEBUG_ENABLED) { log.debug("writeStartElement " + arg0 ); } depth++; if (depth > 1) { isBuffering = false; } // The start element is buffered when we encounter the // outermost element. The buffered event is written // (instead of the child element tag). if (isBuffering) { List<String> command = new ArrayList<String>(); command.add("writeStartElement"); command.add(arg0); bufferCommand(command); } else if (depth == 2) { writeCommands(); } else { writer.writeStartElement(arg0); } } void bufferCommand(List<String> command) { if (DEBUG_ENABLED) { log.debug(" Buffering command " + command); } bufferedCommands.add(command); } void writeCommands() throws XMLStreamException { for (int i=0;i<bufferedCommands.size(); i++) { List<String> command = bufferedCommands.get(i); String m = command.get(0); if ("writeNamespace".equals(m)) { if (DEBUG_ENABLED) { log.debug(" Sending buffered writeNamespace " + command.get(1) + " " + command.get(2)); } writer.writeNamespace(command.get(1), command.get(2)); } else if ("writeStartElement".equals(m)) { int len = command.size(); if (len == 2) { if (DEBUG_ENABLED) { log.debug(" Sending buffered writeStartElement " + command.get(1)); } writer.writeStartElement(command.get(1)); } else if (len ==3) { if (DEBUG_ENABLED) { log.debug(" Sending buffered writeStartElement " + command.get(1) + " " + command.get(2)); } writer.writeStartElement(command.get(1), command.get(2)); } else if (len == 4) { if (DEBUG_ENABLED) { log.debug(" Sending buffered writeStartElement " + command.get(1) + " " + command.get(2) + " " + command.get(3)); } writer.writeStartElement(command.get(1), command.get(2), command.get(3)); } } } } }