/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
*
* 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.switchyard.config;
import static javax.xml.XMLConstants.NULL_NS_URI;
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.OutputKeys;
import org.switchyard.common.io.pull.ElementPuller;
import org.switchyard.common.lang.Strings;
import org.switchyard.common.property.PropertyResolver;
import org.switchyard.common.property.SystemAndTestPropertyResolver;
import org.switchyard.common.xml.XMLHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* A DOM (Document Object Model) representation of a Configuration.
*
* @author David Ward <<a href="mailto:dward@jboss.org">dward@jboss.org</a>> (C) 2011 Red Hat Inc.
*/
public class DOMConfiguration extends BaseConfiguration {
private static final String CHILDREN_ORDER_KEY = "childrenOrder";
private static final String CHILDREN_ORDER_DELIM = ",";
private static final String PROPERTY_RESOLVER_KEY = "propertyResolver";
private Element _element;
private Element _parent_element;
private DOMConfiguration _parent_config;
DOMConfiguration(Element element) {
_element = new ElementPuller().pull(element);
getParent(); // initializes parent
}
private DOMConfiguration(Element element, boolean normalize) {
_element = new ElementPuller().pull(element, normalize);
getParent(); // initializes parent
}
private DOMConfiguration(Configuration from) {
DOMConfiguration config;
if (from instanceof DOMConfiguration) {
config = (DOMConfiguration)from;
} else {
Element element = new ElementPuller().pull(from.getQName());
config = new DOMConfiguration(element);
for (QName qname : from.getAttributeQNames()) {
config.setAttribute(qname, from.getAttribute(qname));
}
for (Configuration grandchild : from.getChildren()) {
config.addChild(grandchild);
}
}
_element = config._element;
getParent(); // initializes parent
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return XMLHelper.nameOf(_element);
}
/**
* {@inheritDoc}
*/
@Override
public QName getQName() {
return XMLHelper.createQName(_element);
}
/**
* {@inheritDoc}
*/
@Override
public String lookupNamespaceURI(String prefix) {
return _element.lookupNamespaceURI(prefix);
}
/**
* {@inheritDoc}
*/
@Override
public String lookupPrefix(String namespaceURI) {
return _element.lookupPrefix(namespaceURI);
}
/**
* {@inheritDoc}
*/
@Override
public String getValue() {
Node text_node = getTextNode(false);
String value = null;
if (text_node instanceof Text) {
value = ((Text)text_node).getWholeText();
} else if (text_node != null) {
value = text_node.getNodeValue();
}
if (value != null) {
return Strings.replaceProperties(value, getPropertyResolver());
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration setValue(String value) {
if (value != null) {
Node text_node = getTextNode(true);
text_node.setNodeValue(value);
} else {
Node text_node = getTextNode(false);
if (text_node != null) {
_element.removeChild(text_node);
}
}
return this;
}
private Node getTextNode(boolean create) {
Node first_child_node = _element.getFirstChild();
if (first_child_node != null) {
if (first_child_node.getNodeType() == Node.TEXT_NODE) {
return first_child_node;
}
Node next_child_node = first_child_node.getNextSibling();
while (next_child_node != null) {
if (next_child_node.getNodeType() == Node.TEXT_NODE) {
return next_child_node;
}
next_child_node = next_child_node.getNextSibling();
}
if (create) {
Node text_node = _element.getOwnerDocument().createTextNode("");
_element.insertBefore(text_node, first_child_node);
return text_node;
}
}
if (create) {
Node text_node = _element.getOwnerDocument().createTextNode("");
_element.appendChild(text_node);
return text_node;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getAttributeNames() {
List<String> names = new ArrayList<String>();
NamedNodeMap attrs = _element.getAttributes();
for (int i=0; i < attrs.getLength(); i++) {
Attr attr = (Attr)attrs.item(i);
String name = XMLHelper.nameOf(attr);
names.add(name);
}
return names;
}
/**
* {@inheritDoc}
*/
@Override
public List<QName> getAttributeQNames() {
List<QName> qnames = new ArrayList<QName>();
NamedNodeMap attrs = _element.getAttributes();
for (int i=0; i < attrs.getLength(); i++) {
Attr attr = (Attr)attrs.item(i);
String name = XMLHelper.nameOf(attr);
if (name != null && name.length() > 0) {
qnames.add(XMLHelper.createQName(attr.getNamespaceURI(), name, attr.getPrefix()));
}
}
return qnames;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasAttribute(String name) {
if (name != null) {
return _element.hasAttribute(name);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasAttribute(QName qname) {
if (qname != null) {
String namespaceURI = qname.getNamespaceURI();
String localPart = qname.getLocalPart();
if (namespaceURI != null && !namespaceURI.equals(NULL_NS_URI)) {
return _element.hasAttributeNS(namespaceURI, localPart);
} else {
return hasAttribute(localPart);
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public String getAttribute(String name) {
if (name != null) {
String value = _element.getAttribute(name);
return "".equals(value) ? null : Strings.replaceProperties(value, getPropertyResolver());
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String getAttribute(QName qname) {
if (qname != null) {
String namespaceURI = qname.getNamespaceURI();
String localPart = qname.getLocalPart();
if (namespaceURI != null && !namespaceURI.equals(NULL_NS_URI)) {
Attr attr = _element.getAttributeNodeNS(namespaceURI, localPart);
if (attr != null) {
String value = attr.getValue();
return "".equals(value) ? null : Strings.replaceProperties(value, getPropertyResolver());
}
} else {
return getAttribute(localPart);
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration setAttribute(String name, String value) {
if (name == null) {
throw new IllegalArgumentException("name == null");
}
if (value == null) {
_element.removeAttribute(name);
} else {
_element.setAttribute(name, value);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration setAttribute(QName qname, String value) {
if (qname == null) {
throw new IllegalArgumentException("qname == null");
}
String namespaceURI = qname.getNamespaceURI();
String localPart = qname.getLocalPart();
Attr attr = _element.getAttributeNodeNS(namespaceURI, localPart);
if (attr != null) {
if (value == null) {
_element.removeAttributeNode(attr);
} else {
attr.setValue(value);
}
} else if (value != null) {
if (XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) {
_element.setAttributeNS(XMLNS_ATTRIBUTE_NS_URI, "xmlns:"+localPart, value);
} else {
attr = _element.getOwnerDocument().createAttributeNS(namespaceURI, localPart);
String prefix = qname.getPrefix();
if (prefix != null && prefix.length() > 0) {
attr.setPrefix(prefix);
}
attr.setValue(value);
_element.setAttributeNode(attr);
}
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasParent() {
return _element.getParentNode() instanceof Element;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration getParent() {
Node node = _element.getParentNode();
if (node instanceof Element) {
Element e = (Element)node;
if (_parent_element != null && _parent_element != e) {
// somebody changed the document structure underneath us!
_parent_config = null;
}
if (_parent_config == null) {
_parent_config = new DOMConfiguration(e, false);
_parent_element = e;
}
}
return _parent_config;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasChildren() {
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasChildren(String name) {
if (name != null) {
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
String node_name = XMLHelper.nameOf(node);
if (node_name.equals(name)) {
return true;
}
}
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasChildren(QName qname) {
if (qname != null) {
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
QName node_qname = XMLHelper.createQName((Element)node);
if (node_qname.equals(qname)) {
return true;
}
}
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public List<Configuration> getChildren() {
List<Configuration> configs = new ArrayList<Configuration>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
configs.add(new DOMConfiguration((Element)node));
}
}
return configs;
}
/**
* {@inheritDoc}
*/
@Override
public List<Configuration> getChildren(String name) {
List<Configuration> configs = new ArrayList<Configuration>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).equals(name)) {
configs.add(new DOMConfiguration((Element)node));
}
}
}
return configs;
}
/**
* {@inheritDoc}
*/
@Override
public List<Configuration> getChildrenStartsWith(String name) {
List<Configuration> configs = new ArrayList<Configuration>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).startsWith(name)) {
configs.add(new DOMConfiguration((Element)node));
}
}
}
return configs;
}
/**
* {@inheritDoc}
*/
@Override
public List<Configuration> getChildrenMatches(String regexp) {
List<Configuration> configs = new ArrayList<Configuration>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).matches(regexp)) {
configs.add(new DOMConfiguration((Element)node));
}
}
}
return configs;
}
/**
* {@inheritDoc}
*/
@Override
public List<Configuration> getChildren(QName qname) {
List<Configuration> configs = new ArrayList<Configuration>();
NodeList nodes = _element.getElementsByTagNameNS(qname.getNamespaceURI(), qname.getLocalPart());
for (int i=0; i < nodes.getLength(); i++) {
configs.add(new DOMConfiguration((Element)nodes.item(i)));
}
return configs;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration getFirstChild(String name) {
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).equals(name)) {
return new DOMConfiguration((Element)node);
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration getFirstChildStartsWith(String name) {
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).startsWith(name)) {
return new DOMConfiguration((Element)node);
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration getFirstChild(QName qname) {
NodeList nodes = _element.getElementsByTagNameNS(qname.getNamespaceURI(), qname.getLocalPart());
if (nodes.getLength() > 0) {
Node node = nodes.item(0);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
return new DOMConfiguration((Element)node);
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration addChild(Configuration child) {
Document this_doc = _element.getOwnerDocument();
this_doc.normalizeDocument();
DOMConfiguration child_config = new DOMConfiguration(child);
Document child_doc = child_config._element.getOwnerDocument();
if (child_doc != this_doc) {
child_doc.normalizeDocument();
this_doc.adoptNode(child_config._element);
}
_element.appendChild(child_config._element);
String child_xmlns = Strings.trimToNull(child_config._element.getNamespaceURI());
if (child_xmlns == null) {
String this_xmlns = Strings.trimToNull(this._element.getNamespaceURI());
if (this_xmlns != null) {
this_doc.renameNode(child_config._element, this_xmlns, child_config.getName());
}
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration removeChildren() {
List<Node> removals = new ArrayList<Node>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
removals.add(node);
}
}
for (Node node : removals) {
_element.removeChild(node);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration removeChildren(String name) {
List<Node> removals = new ArrayList<Node>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).equals(name)) {
removals.add(node);
}
}
}
for (Node node : removals) {
_element.removeChild(node);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration removeChildrenStartsWith(String name) {
List<Node> removals = new ArrayList<Node>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).startsWith(name)) {
removals.add(node);
}
}
}
for (Node node : removals) {
_element.removeChild(node);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration removeChildrenMatches(String regexp) {
List<Node> removals = new ArrayList<Node>();
NodeList nodes = _element.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
if (XMLHelper.nameOf(node).matches(regexp)) {
removals.add(node);
}
}
}
for (Node node : removals) {
_element.removeChild(node);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration removeChildren(QName qname) {
List<Node> removals = new ArrayList<Node>();
NodeList nodes = _element.getElementsByTagNameNS(qname.getNamespaceURI(), qname.getLocalPart());
for (int i=0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
removals.add(node);
}
}
for (Node node : removals) {
_element.removeChild(node);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public String[] getChildrenOrder() {
String co = (String)_element.getUserData(CHILDREN_ORDER_KEY);
return Strings.splitTrimToNullArray(co, CHILDREN_ORDER_DELIM);
}
/**
* {@inheritDoc}
*/
@Override
public Configuration setChildrenOrder(String... childrenOrder) {
String co = Strings.concat(CHILDREN_ORDER_DELIM, childrenOrder);
_element.setUserData(CHILDREN_ORDER_KEY, co, null);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public PropertyResolver getPropertyResolver() {
PropertyResolver propertyResolver = (PropertyResolver)_element.getUserData(PROPERTY_RESOLVER_KEY);
if (propertyResolver == null) {
Configuration parent = getParent();
propertyResolver = parent != null ? parent.getPropertyResolver() : SystemAndTestPropertyResolver.INSTANCE;
}
return propertyResolver;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration setPropertyResolver(PropertyResolver propertyResolver) {
_element.setUserData(PROPERTY_RESOLVER_KEY, propertyResolver, null);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration copy() {
Element clone = (Element)_element.cloneNode(true);
DOMConfiguration copy = new DOMConfiguration(clone, false);
copy.setChildrenOrder(getChildrenOrder());
return copy;
}
/**
* {@inheritDoc}
*/
@Override
public Configuration normalize() {
_element.normalize();
return this;
}
/**
* {@inheritDoc}
*/
@Override
public void write(Writer writer, OutputKey... keys) throws IOException {
List<OutputKey> key_list = Arrays.asList(keys);
if (key_list.contains(OutputKey.NORMALIZE)) {
normalize();
}
if (key_list.contains(OutputKey.ORDER_CHILDREN)) {
orderChildren();
}
Map<String, String> outputProperties = new HashMap<String, String>();
if (key_list.contains(OutputKey.OMIT_XML_DECLARATION)) {
outputProperties.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
}
if (key_list.contains(OutputKey.PRETTY_PRINT)) {
outputProperties.put(OutputKey.PRETTY_PRINT.hint(), "yes");
}
XMLHelper.write(_element, writer, outputProperties);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_element == null) ? 0 : _element.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
DOMConfiguration other = (DOMConfiguration)obj;
if (_element == null) {
if (other._element != null) {
return false;
}
} else {
_element.normalize();
other._element.normalize();
if (!_element.isEqualNode(other._element)) {
return false;
}
}
return true;
}
}