package org.jbpm.gd.common.xml;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.jbpm.gd.common.model.SemanticElement;
import org.jbpm.gd.common.model.SemanticElementFactory;
import org.jbpm.gd.common.registry.XmlAdapterRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
public abstract class XmlAdapter implements PropertyChangeListener, INodeAdapter {
private static final String PADDING = "\t";
private static final String INTER_NODE_TYPE_SPACING = "\n";
private static final String INTER_CHILD_SPACING = "\n";
private static final String[] CHILD_ELEMENTS = {};
private static HashMap NODE_TYPES = null;
private Node node;
private SemanticElement semanticElement;
private XmlAdapterFactory factory;
private boolean changing = false;
protected void initialize(String elementType, Document document) {
node = document.createElement(elementType);
}
protected void initialize() {
}
public String getElementType() {
return node != null ? node.getNodeName() : null;
}
protected String[] getChildElements() {
return CHILD_ELEMENTS;
}
protected Map getNodeTypes() {
if (NODE_TYPES == null) {
NODE_TYPES = new HashMap();
}
return NODE_TYPES;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
protected void addElements(SemanticElement[] jpdlElements) {
for (int i = 0; i < jpdlElements.length; i++) {
addElement(jpdlElements[i]);
}
}
protected void addElement(XmlAdapter adapter) {
if (adapter == null) return;
if (contains(adapter)) return;
Node insertionPoint = null;
int level = getLevel();
if (isEmpty()) {
insertionPoint = node.appendChild(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing() + getPaddingString(level - 1)));
insertionPoint = node.insertBefore(adapter.getNode(), insertionPoint);
node.insertBefore(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing() + getPaddingString(level)), insertionPoint);
} else if (hasNode(getNodeType(adapter.getElementType()))) {
insertionPoint = getLastNode(getNodeType(adapter.getElementType())).getNextSibling();
if (insertionPoint == null) {
insertionPoint = node.appendChild(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing()));
}
insertionPoint = node.insertBefore(adapter.getNode(), insertionPoint);
node.insertBefore(node.getOwnerDocument().createTextNode(getInterChildSpacing() + getPaddingString(level)), insertionPoint);
} else {
insertionPoint = prepareInsertionPoint(getNodeType(adapter.getElementType()), level);
node.insertBefore(adapter.getNode(), insertionPoint);
}
adapter.initialize();
}
private boolean contains(XmlAdapter adapter) {
Node parent = getNode();
Node child = adapter.getNode();
NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
if (children.item(i) == child) {
return true;
}
}
return false;
}
protected String getInterNodeTypeSpacing() {
return INTER_NODE_TYPE_SPACING;
}
protected String getInterChildSpacing() {
return INTER_CHILD_SPACING;
}
private Node prepareInsertionPoint(String nodeType, int level) {
boolean before = false;
Node insertAfter = null;
Node insertBefore = null;
for (int i = 0; i < getChildElements().length; i++) {
if (getChildElements()[i].equals(nodeType)) {
before = true;
} else {
if (before) {
if (insertBefore == null) {
insertBefore = getFirstNode(getChildElements()[i]);
}
}
else {
Node candidateAfter = getLastNode(getChildElements()[i]);
if (candidateAfter != null) {
insertAfter = candidateAfter;
}
}
}
}
if (before && insertBefore != null) {
return node.insertBefore(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing() + getPaddingString(level)), insertBefore);
} else if (insertAfter != null) {
Node candidate = insertAfter.getNextSibling();
if (candidate != null) {
node.insertBefore(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing() + getPaddingString(level)), candidate);
return candidate;
} else {
return node.appendChild(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing()));
}
} else {
return node.appendChild(node.getOwnerDocument().createTextNode(getInterNodeTypeSpacing()));
}
}
protected void removeElement(XmlAdapter adapter) {
if (adapter == null) return;
Node last = getLastNode(getNodeType(adapter.getElementType()));
Node toDelete = adapter.getNode();
Node parent = toDelete.getParentNode();
if (parent != null && parent == node) {
if (toDelete == last) {
removeTextChild(toDelete.getPreviousSibling());
} else {
removeTextChild(toDelete.getNextSibling());
}
node.removeChild(toDelete);
if (hasOnlyTextChildren()) {
emptyNode();
}
}
}
protected void removeFirst(String nodeType) {
Node toDelete = getFirstNode(nodeType);
if (toDelete != null) {
removeTextChild(toDelete.getPreviousSibling());
removeTextChild(toDelete.getNextSibling());
getNode().removeChild(toDelete);
}
}
private int getLevel() {
int result = 0;
Node node = getNode();
Document document = getNode().getOwnerDocument();
while (node != null && node != document) {
result++;
node = node.getParentNode();
}
return result;
}
private String getPaddingString(int level) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < level; i++) {
result.append(PADDING);
}
return result.toString();
}
protected String getNodeType(String elementType) {
return (String)getNodeTypes().get(elementType);
}
protected boolean isEmpty() {
NodeList childNodes = node.getChildNodes();
int length = childNodes.getLength();
if (length == 0) return true;
for (int i = 0; i < length; i++) {
if (childNodes.item(i).getNodeType() != Node.TEXT_NODE || !("".equals(childNodes.item(i).getNodeValue().trim()))) {
return false;
}
}
return true;
}
protected boolean hasOnlyTextChildren() {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i).getNodeType() != Node.TEXT_NODE) {
return false;
}
}
return true;
}
protected boolean isNodeOfType(Node node, String type) {
boolean result = false;
XmlAdapter adapter = getFactory().adapt(node);
if (adapter != null) {
result = adapter.getElementType().equals(type);
}
return type.equals(getNodeTypes().get(node.getNodeName())) || result;
}
protected void emptyNode() {
Node first = node.getFirstChild();
while (first != null) {
node.removeChild(first);
first = node.getFirstChild();
}
}
protected Node getFirstNode(String nodeType) {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node item = list.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE && isNodeOfType(item, nodeType)) {
return item;
}
}
return null;
}
protected Node getLastNode(String nodeType) {
NodeList list = node.getChildNodes();
for (int i = list.getLength(); i > 0; i--) {
Node item = list.item(i - 1);
if (item.getNodeType() == Node.ELEMENT_NODE && isNodeOfType(item, nodeType)) {
return item;
}
}
return null;
}
protected boolean hasNode(String nodeType) {
return getFirstNode(nodeType) != null;
}
protected void removeTextChild(Node child) {
if (child != null && child.getNodeType() == Node.TEXT_NODE && "".equals(child.getNodeValue().trim())) {
node.removeChild(child);
}
}
protected void setTextContent(String content) {
String oldContents = getTextContent();
if (oldContents == null && content == null) return;
if (oldContents.equals(content)) return;
removeTextChildren();
if (content != null) {
replaceTextChild(content);
}
}
private void removeTextChildren() {
NodeList list = getNode().getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i).getNodeType() == Node.TEXT_NODE) {
getNode().removeChild(list.item(i));
}
}
}
private void replaceTextChild(String content) {
int level = getLevel();
StringBuffer buff = new StringBuffer();
buff.append(getInterNodeTypeSpacing());
buff.append(getPaddingString(level));
buff.append(content);
buff.append(getInterNodeTypeSpacing());
buff.append(getPaddingString(level - 1));
Text text = getNode().getOwnerDocument().createTextNode(buff.toString());
getNode().appendChild(text);
}
protected String getTextContent() {
StringBuffer buffer = new StringBuffer();
NodeList list = getNode().getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
String nodeValue = list.item(i).getNodeValue();
if (nodeValue != null) {
buffer.append(nodeValue);
}
}
return buffer.toString().trim();
}
protected void setAttribute(String name, String value) {
if (name == null) return;
String defaultValue = getDefaultValue(name);
if (defaultValue != null && defaultValue.equals(value)) {
value = null;
}
Node attr = getNode().getAttributes().getNamedItem(name);
if (value == null) {
if (attr != null) {
getNode().getAttributes().removeNamedItem(name);
}
} else {
if (attr == null) {
attr = getNode().getOwnerDocument().createAttribute(name);
attr.setNodeValue(value);
getNode().getAttributes().setNamedItem(attr);
} else if (!value.equals(attr.getNodeValue())) {
attr.setNodeValue(value);
}
}
}
protected String getDefaultValue(String attributeName) {
return null;
}
protected String getAttribute(String name) {
if (name == null) return null;
String defaultValue = getDefaultValue(name);
Node attr = getNode().getAttributes().getNamedItem(name);
return attr == null ? defaultValue : attr.getNodeValue();
}
protected Map getAttributes() {
HashMap result = new HashMap();
NamedNodeMap map = getNode().getAttributes();
for (int i = 0; i < map.getLength(); i++) {
Node node = map.item(i);
result.put(node.getNodeName(), node.getNodeValue());
}
return result;
}
protected XmlAdapter getAdapter(Node node) {
return getFactory().adapt(node);
}
protected void setElement(String elementType, SemanticElement oldElement, SemanticElement newElement) {
removeElement(oldElement);
addElement(newElement);
}
protected void removeElement(SemanticElement jpdlElement) {
if (jpdlElement == null) return;
XmlAdapter jpdlElementDomAdapter = getFactory().getRegisteredAdapterFor(jpdlElement);
if (jpdlElementDomAdapter != null) {
jpdlElementDomAdapter.unregister();
jpdlElement.removePropertyChangeListener(jpdlElementDomAdapter);
jpdlElementDomAdapter.setSemanticElement(null);
}
removeElement(jpdlElementDomAdapter);
}
protected void addElement(SemanticElement jpdlElement) {
if (jpdlElement == null) return;
XmlAdapter jpdlElementDomAdapter = getFactory().getRegisteredAdapterFor(jpdlElement);
if (jpdlElementDomAdapter == null) {
jpdlElementDomAdapter = getFactory().createAdapterFromModel(jpdlElement);
jpdlElementDomAdapter.setSemanticElement(jpdlElement);
jpdlElement.addPropertyChangeListener(jpdlElementDomAdapter);
jpdlElementDomAdapter.register();
}
addElement(jpdlElementDomAdapter);
}
protected void register() {
getFactory().register(this);
}
private void unregister() {
getFactory().unregister(this);
NodeList nodeList = getNode().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
XmlAdapter jpdlElementDomAdapter = getAdapter(nodeList.item(i));
if (jpdlElementDomAdapter != null) {
jpdlElementDomAdapter.unregister();
}
}
}
protected void setSemanticElement(SemanticElement semanticElement) {
this.semanticElement = semanticElement;
}
public SemanticElement getSemanticElement() {
return semanticElement;
}
protected void setFactory(XmlAdapterFactory factory) {
this.factory = factory;
}
protected XmlAdapterFactory getFactory() {
return factory;
}
protected SemanticElementFactory getSemanticElementFactory() {
return factory.getSemanticElementFactory();
}
protected XmlAdapterRegistry getXmlAdapterRegistry() {
return factory.getXmlAdapterRegistry();
}
public boolean isAdapterForType(Object type) {
return type == getFactory() ;
}
public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) {
if (INodeNotifier.ADD == eventType) {
handleDomAdd(newValue);
} else if (INodeNotifier.REMOVE == eventType) {
handleDomRemove(oldValue);
} else if (changedFeature != null) {
modelUpdate(((Node)changedFeature).getNodeName(), (String)newValue);
}
}
protected void handleDomAdd(Object newObject) {
if (!(newObject instanceof INodeNotifier)) return;
XmlAdapter adapter = (XmlAdapter)getFactory().adapt((INodeNotifier)newObject);
if (adapter != null) {
modelAdd(adapter);
}
}
protected void handleDomRemove(Object oldObject) {
if (!(oldObject instanceof INodeNotifier)) return;
XmlAdapter adapter = (XmlAdapter)getFactory().adapt((INodeNotifier)oldObject);
if (adapter != null) {
modelRemove(adapter);
}
}
protected void modelUpdate(String name, String newValue) {
if (changing) return;
SemanticElement semanticElement = getSemanticElement();
if (semanticElement == null) return;
changing = true;
doModelUpdate(name, newValue);
changing = false;
}
protected void modelAdd(XmlAdapter child) {
if (changing) return;
changing = true;
doModelAdd(child);
changing = false;
}
protected void modelRemove(XmlAdapter child) {
if (changing) return;
changing = true;
doModelRemove(child);
changing = false;
}
public void propertyChange(PropertyChangeEvent event) {
if (changing) return;
changing = true;
doPropertyChange(event);
changing = false;
}
protected abstract void doModelUpdate(String name, String newValue);
protected abstract void doModelAdd(XmlAdapter child);
protected abstract void doModelRemove(XmlAdapter child);
protected abstract void doPropertyChange(PropertyChangeEvent event);
public void initialize(SemanticElement jpdlElement) {
setSemanticElement(jpdlElement);
NodeList nodeList = getNode().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
XmlAdapter jpdlElementDomAdapter = getAdapter(nodeList.item(i));
if (jpdlElementDomAdapter != null && !"#comment".equals(jpdlElementDomAdapter.getNode().getNodeName())) {
doModelAdd(jpdlElementDomAdapter);
}
}
register();
}
protected SemanticElement createSemanticElementFor(XmlAdapter child) {
IConfigurationElement configurationElement =
getXmlAdapterRegistry().getConfigurationElementByXmlNode(child.getNode());
if (configurationElement == null) return null;
String id = configurationElement.getAttribute("semanticElement");
return getSemanticElementFactory().createById(id);
}
}