/*
* Copyright 2005-2010 the original author or authors.
*
* 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.springframework.ws.soap.stroap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Source;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils;
import org.springframework.ws.soap.SoapElement;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.xml.stream.AbstractXMLEventReader;
/**
* @author Arjen Poutsma
*/
abstract class StroapElement implements SoapElement {
protected static final String DEFAULT_PREFIX = "SOAP-ENV";
private final StroapMessageFactory messageFactory;
private StartElement startElement;
private EndElement endElement;
protected StroapElement(QName name, StroapMessageFactory messageFactory) {
this(createStartElement(name, messageFactory), messageFactory);
}
private static StartElement createStartElement(QName name, StroapMessageFactory messageFactory) {
if (!StringUtils.hasLength(name.getPrefix())) {
name = new QName(name.getNamespaceURI(), name.getLocalPart(), DEFAULT_PREFIX);
}
return messageFactory.getEventFactory().createStartElement(name, null, null);
}
protected StroapElement(StartElement startElement, StroapMessageFactory messageFactory) {
Assert.notNull(startElement, "'startElement' must not be null");
Assert.notNull(messageFactory, "'messageFactory' must not be null");
this.messageFactory = messageFactory;
this.startElement = startElement;
this.endElement = getEventFactory().createEndElement(startElement.getName(), startElement.getNamespaces());
}
public final Source getSource() {
return StaxUtils.createCustomStaxSource(getEventReader(true));
}
protected XMLEventReader getEventReader(boolean documentEvents) {
return new StroapElementEventReader(documentEvents);
}
public void writeTo(XMLEventWriter eventWriter) throws XMLStreamException {
eventWriter.add(getEventReader(false));
}
protected StroapMessageFactory getMessageFactory() {
return messageFactory;
}
protected final XMLEventFactory getEventFactory() {
return getMessageFactory().getEventFactory();
}
protected SoapVersion getSoapVersion() {
return getMessageFactory().getSoapVersion();
}
public final QName getName() {
return getStartElement().getName();
}
protected abstract XMLEventReader getChildEventReader();
public final Iterator<QName> getAllAttributes() {
List<QName> result = new LinkedList<QName>();
for (Iterator iterator = getStartElement().getAttributes(); iterator.hasNext();) {
Attribute attribute = (Attribute) iterator.next();
result.add(attribute.getName());
}
return result.iterator();
}
public final String getAttributeValue(QName name) {
Attribute attribute = getStartElement().getAttributeByName(name);
return attribute != null ? attribute.getValue() : null;
}
public final void removeAttribute(QName name) {
List<Attribute> newAttributes = new LinkedList<Attribute>();
for (Iterator iterator = getStartElement().getAttributes(); iterator.hasNext();) {
Attribute attribute = (Attribute) iterator.next();
if (!name.equals(attribute.getName())) {
newAttributes.add(attribute);
}
}
StartElement oldStartElement = getStartElement();
this.startElement = getEventFactory().createStartElement(oldStartElement.getName(), newAttributes.iterator(),
oldStartElement.getNamespaces());
}
public final void addAttribute(QName name, String value) {
List<Attribute> newAttributes = new LinkedList<Attribute>();
for (Iterator iterator = getStartElement().getAttributes(); iterator.hasNext();) {
Attribute attribute = (Attribute) iterator.next();
newAttributes.add(attribute);
}
Attribute newAttribute = getEventFactory().createAttribute(name, value);
newAttributes.add(newAttribute);
StartElement oldStartElement = getStartElement();
this.startElement = getEventFactory().createStartElement(oldStartElement.getName(), newAttributes.iterator(),
oldStartElement.getNamespaces());
}
public final void addNamespaceDeclaration(String prefix, String namespaceUri) {
List<Namespace> newNamespaces = new LinkedList<Namespace>();
for (Iterator iterator = getStartElement().getNamespaces(); iterator.hasNext();) {
Namespace namespace = (Namespace) iterator.next();
newNamespaces.add(namespace);
}
Namespace newNamespace;
if (StringUtils.hasLength(prefix)) {
newNamespace = getEventFactory().createNamespace(prefix, namespaceUri);
}
else {
newNamespace = getEventFactory().createNamespace(namespaceUri);
}
newNamespaces.add(newNamespace);
StartElement oldStartElement = getStartElement();
this.startElement = getEventFactory()
.createStartElement(oldStartElement.getName(), oldStartElement.getAttributes(),
newNamespaces.iterator());
}
protected final StartElement getStartElement() {
return startElement;
}
protected final EndElement getEndElement() {
return endElement;
}
private enum EVENT_READER_STATE {
START_DOCUMENT,
START_ELEMENT,
CHILDREN,
END_ELEMENT,
END_DOCUMENT,
DONE
}
private class StroapElementEventReader extends AbstractXMLEventReader {
private EVENT_READER_STATE state;
private boolean documentEvents;
private final XMLEventReader childEventReader;
private StroapElementEventReader(boolean documentEvents) {
this.documentEvents = documentEvents;
state = documentEvents ? EVENT_READER_STATE.START_DOCUMENT : EVENT_READER_STATE.START_ELEMENT;
this.childEventReader = getChildEventReader();
}
public boolean hasNext() {
if (documentEvents && state == EVENT_READER_STATE.DONE) {
return false;
}
else if (!documentEvents && state == EVENT_READER_STATE.END_DOCUMENT) {
return false;
}
else {
return true;
}
}
public XMLEvent nextEvent() throws XMLStreamException {
switch (state) {
case START_DOCUMENT:
state = EVENT_READER_STATE.START_ELEMENT;
return getEventFactory().createStartDocument();
case START_ELEMENT:
state = EVENT_READER_STATE.CHILDREN;
return getStartElement();
case CHILDREN:
if (!childEventReader.hasNext()) {
state = EVENT_READER_STATE.END_ELEMENT;
return nextEvent();
}
return childEventReader.nextEvent();
case END_ELEMENT:
state = EVENT_READER_STATE.END_DOCUMENT;
return getEndElement();
case END_DOCUMENT:
state = EVENT_READER_STATE.DONE;
return getEventFactory().createEndDocument();
case DONE:
throw new NoSuchElementException();
default:
throw new IllegalStateException();
}
}
public XMLEvent peek() throws XMLStreamException {
switch (state) {
case START_DOCUMENT:
return getEventFactory().createStartDocument();
case START_ELEMENT:
return getStartElement();
case CHILDREN:
XMLEvent event = childEventReader.peek();
if (event == null) {
state = EVENT_READER_STATE.END_ELEMENT;
event = getEndElement();
}
return event;
case END_ELEMENT:
return getEndElement();
case END_DOCUMENT:
return getEventFactory().createEndDocument();
case DONE:
return null;
default:
throw new IllegalStateException();
}
}
}
}