blob: 16dc1b23b0d15a72d36f67474b6babc3a4e7a917 [file] [log] [blame]
/*
* 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.vysper.xml.fragment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
* TODO For now, this is mostly a copy of StanzaBuilder. Both classes needs to be refactored.
*
* @author The Apache MINA Project (dev@mina.apache.org)
*/
@SuppressWarnings("unchecked")
public abstract class AbstractXMLElementBuilder<B extends AbstractXMLElementBuilder, T extends XMLElement> {
class ElementStruct {
public ElementStruct parentElement = null;
public XMLElement element = null;
public List<Attribute> attributes = null;
public Map<String, String> namespaces = null;
public List<XMLFragment> innerFragments = null;
}
/**
* parent hierarchy for current element
*/
private Stack<ElementStruct> stack = new Stack<ElementStruct>();
protected ElementStruct currentElement = null;
private XMLElement resultingElement = null;
private boolean isReset = false;
public AbstractXMLElementBuilder(String elementName) {
this(elementName, null);
}
public AbstractXMLElementBuilder(String elementName, String namespaceURI) {
this(elementName, namespaceURI, null);
}
public AbstractXMLElementBuilder(String elementName, String namespaceURI, String namespacePrefix) {
startNewElement(elementName, namespaceURI, namespacePrefix);
resultingElement = currentElement.element;
stack.push(currentElement);
}
public AbstractXMLElementBuilder(String elementName, String namespaceURI, String namespacePrefix, List<Attribute> attributes, Map<String, String> namespaces, List<XMLFragment> innerFragments) {
startNewElement(elementName, namespaceURI, namespacePrefix);
resultingElement = currentElement.element;
if(attributes != null) currentElement.attributes.addAll(attributes);
if(namespaces != null) currentElement.namespaces.putAll(namespaces);
if(innerFragments != null) currentElement.innerFragments.addAll(innerFragments);
stack.push(currentElement);
}
protected XMLElement createElement(String namespaceURI, String name, String namespacePrefix, List<Attribute> attributes, Map<String, String> namespaces, List<XMLFragment> innerFragments) {
return new XMLElement(namespaceURI, name, namespacePrefix, attributes, innerFragments, namespaces);
}
private void startNewElement(String name, String namespaceURI, String namespacePrefix) {
// TODO assert that name does not contain namespace (":")
// TODO handle the namespace, given by URI, currently always NULL in XMLElement constructors
ElementStruct element = new ElementStruct();
element.attributes = new ArrayList<Attribute>();
element.namespaces = new HashMap<String, String>();
element.innerFragments = new ArrayList<XMLFragment>();
element.element = createElement(namespaceURI, name, namespacePrefix, element.attributes, element.namespaces, element.innerFragments);
currentElement = element;
}
public B declareNamespace(String namespacePrefix, String value) {
currentElement.namespaces.put(namespacePrefix, value);
return (B) this;
}
public B addAttribute(String name, String value) {
addAttribute(new Attribute(name, value));
return (B) this;
}
public B addAttribute(String namespaceUris, String name, String value) {
addAttribute(new Attribute(namespaceUris, name, value));
return (B) this;
}
public B addAttribute(Attribute attribute) {
checkReset();
currentElement.attributes.add(attribute);
return (B) this;
}
public B addText(String text) {
checkReset();
currentElement.innerFragments.add(new XMLText(text));
return (B) this;
}
public B startInnerElement(String name) {
return this.startInnerElement(name, null);
}
public B startInnerElement(String name, String namespaceURI) {
checkReset();
startNewElement(name, namespaceURI, null);
stack.peek().innerFragments.add(currentElement.element); // add new one to its parent
stack.push(currentElement);
return (B) this;
}
public B endInnerElement() {
checkReset();
if (stack.isEmpty()) throw new IllegalStateException("cannot end beyond top element");
stack.pop(); // take current off stack and forget (it was added to its parent before)
currentElement = stack.peek(); // we again deal with parent, which can be receive additions
return (B) this;
}
public B addPreparedElement(XMLElement preparedElement) {
checkReset();
currentElement.innerFragments.add(preparedElement);
return (B) this;
}
/**
* the stanza can only be retrieved once
* @return retrieves the XML element and invalidates the builder
*/
public T build() {
checkReset();
XMLElement returnStanza = resultingElement;
resultingElement = null;
isReset = true; // reset
stack.clear();
return (T) returnStanza;
}
/**
* assure that the immutable XML element object is not changed after it was retrieved
*/
private void checkReset() {
if (isReset) throw new IllegalStateException("XML element builder was reset after retrieving stanza");
}
}