/**
Copyright (c) 2012 Delcyon, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.delcyon.capo.xml.cdom;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.w3c.dom.DOMException;
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;
import org.w3c.dom.UserDataHandler;
import com.delcyon.capo.util.CloneControl;
import com.delcyon.capo.util.CloneControl.Clone;
import com.delcyon.capo.util.ControlledClone;
import com.delcyon.capo.util.EqualityProcessor;
import com.delcyon.capo.util.ToStringControl;
import com.delcyon.capo.util.ToStringControl.Control;
import com.delcyon.capo.xml.cdom.CDOMEvent.EventType;
/**
* @author jeremiah
*
*/
@CloneControl(filter=CloneControl.Clone.exclude,modifiers=Modifier.STATIC+Modifier.FINAL)
@ToStringControl(control=Control.exclude,modifiers=Modifier.STATIC+Modifier.FINAL)
public abstract class CNode implements Node, ControlledClone, NodeValidationUtilitesFI
{
@CloneControl(filter=Clone.exclude)
private static final Pattern pattern = Pattern.compile("^[:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}][:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}.\\-0-9\\xB7\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]*");
@CloneControl(filter=Clone.exclude)
@ToStringControl(control=Control.exclude)
protected CNode parentNode;
@CloneControl(filter=Clone.exclude)
@ToStringControl(control=Control.exclude)
protected CDocument ownerDocument = null;
@CloneControl(filter=Clone.exclude)
protected Vector<CDOMEventListener> cdomEventListenerVector = new Vector<CDOMEventListener>();
@CloneControl(filter=Clone.exclude)
protected CNodeDefinition nodeDefinition = null;
protected CNodeList nodeList = new CNodeList();
protected CNamedNodeMap attributeList = new CNamedNodeMap();
protected String nodeName = null;
protected String nodeValue = null;
protected String namespaceURI = null;
@CloneControl(filter=Clone.exclude)
private transient CDOMEvent preparedEvent = null;
public void setParent(CNode parentNode)
{
this.parentNode = parentNode;
}
public boolean hasEventListeners()
{
if(cdomEventListenerVector.size() > 0)
{
return true;
}
if(parentNode != null)
{
return parentNode.hasEventListeners();
}
return false;
}
/**
* We only want one event to happen per change, so this method should act as a singleton for event creation.
* @param eventType
* @param sourceNode
* @return
*/
protected CDOMEvent prepareEvent(EventType eventType, CNode sourceNode)
{
if(ownerDocument != null && ownerDocument.isSilenceEvents() == true)
{
return null;
}
if(hasEventListeners() == false)
{
return null;
}
if (preparedEvent == null)
{
preparedEvent = new CDOMEvent(eventType, sourceNode);
return preparedEvent;
}
else
{
return null;
}
}
public void cascadeDOMEvent(CDOMEvent cdomEvent)
{
if(cdomEvent == null)
{
return;
}
//remove prepared event
preparedEvent = null;
//walk local list of event listeners
for (CDOMEventListener cdomEventListener : getCDOMEventListeners())
{
cdomEventListener.processEvent(cdomEvent);
if(cdomEvent.isHandled())
{
break;
}
}
if(cdomEvent.isHandled() == false && parentNode instanceof CDOMEventListener && this instanceof CDocument == false)
{
if(parentNode.hasEventListeners())
{
parentNode.cascadeDOMEvent(cdomEvent);
}
}
}
public Vector<CDOMEventListener> getCDOMEventListeners()
{
return cdomEventListenerVector;
}
public void addCDOMEventListener(CDOMEventListener eventListener)
{
cdomEventListenerVector.add(eventListener);
}
public void removeCDOMEventListener(CDOMEventListener eventListener)
{
cdomEventListenerVector.remove(eventListener);
}
public void setNodeName(String nodeName)
{
if(ownerDocument != null && ownerDocument.onlyAllowValidNodeNames() == true)
{
if(pattern.matcher(nodeName).matches() == false)
{
throw new RuntimeException(nodeName+" is not a valid node name");
}
}
this.nodeName = nodeName;
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNodeName()
*/
@Override
public String getNodeName()
{
return this.nodeName;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNodeValue()
*/
@Override
public String getNodeValue() throws DOMException
{
try
{
if(this.nodeValue != null && ownerDocument != null && ownerDocument.getVariableProcessor() != null)
{
return ownerDocument.getVariableProcessor().processVars(nodeValue);
}
return this.nodeValue;
}
catch (Exception exception)
{
throw new DOMException(DOMException.VALIDATION_ERR, exception.getMessage());
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setNodeValue(java.lang.String)
*/
@Override
public void setNodeValue(String nodeValue) throws DOMException
{
this.nodeValue = nodeValue;
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getParentNode()
*/
@Override
public Node getParentNode()
{
return this.parentNode;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getChildNodes()
*/
@Override
public NodeList getChildNodes()
{
return nodeList;
}
public List<CNode> getChildNodes(short...types)
{
return nodeList.stream().filter(node->contains(types,node.getNodeType())).map(node->(CNode)node).collect(Collectors.toList());
}
private boolean contains(short[] types, short searchType)
{
for (short s : types)
{
if(s == searchType)
{
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getFirstChild()
*/
@Override
public Node getFirstChild()
{
if(nodeList.size() > 0)
{
return nodeList.item(0);
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getLastChild()
*/
@Override
public Node getLastChild()
{
if(nodeList.size() > 0)
{
return nodeList.item(nodeList.size()-1);
}
else
{
return null;
}
}
private int getPosition()
{
NodeList siblingList = parentNode.getChildNodes();
int myPosition = 0;
for(int index = 0; index < siblingList.getLength(); index++)
{
Node siblingNode = siblingList.item(index);
if (siblingNode.isSameNode(this))
{
myPosition = index;
break;
}
}
return myPosition;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getPreviousSibling()
*/
@Override
public Node getPreviousSibling()
{
if (parentNode == null)
{
return null;
}
NodeList siblingList = parentNode.getChildNodes();
int myPosition = getPosition();
if (myPosition > 0)
{
return siblingList.item(myPosition-1);
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNextSibling()
*/
@Override
public Node getNextSibling()
{
NodeList siblingList = parentNode.getChildNodes();
int myPosition = getPosition();
if (myPosition+1 < siblingList.getLength())
{
return siblingList.item(myPosition+1);
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getAttributes()
*/
@Override
public NamedNodeMap getAttributes()
{
return attributeList;
}
public void removeAttributes()
{
while(attributeList.isEmpty() == false)
{
attributeList.remove(0);
}
}
public void setAttributes(CNamedNodeMap attributes)
{
this.attributeList = attributes;
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getOwnerDocument()
*/
@Override
public Document getOwnerDocument()
{
return this.ownerDocument ;
}
public void setOwnerDocument(Document ownerDocument)
{
this.ownerDocument = (CDocument) ownerDocument;
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node)
*/
@Override
public Node insertBefore(Node newChild, Node refChild) throws DOMException
{
CDOMEvent cdomEvent = prepareEvent(EventType.INSERT, this);
CNodeList children = (CNodeList) getChildNodes();
if (refChild == null)
{
children.add(newChild);
((CNode) newChild).setParent(this);
cascadeDOMEvent(cdomEvent);
return newChild;
}
int index = 0;
for(; index < children.getLength(); index++)
{
if (children.item(index).equals(refChild))
{
break;
}
}
children.add(index, newChild);
((CNode) newChild).setParent(this);
cascadeDOMEvent(cdomEvent);
return newChild;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node)
*/
@Override
public Node replaceChild(Node newChild, Node oldChild) throws DOMException
{
CDOMEvent cdomEvent = prepareEvent(EventType.UPDATE, this);
CNodeList children = (CNodeList) getChildNodes();
int index = 0;
for (Node node : children)
{
if(node.equals(oldChild))
{
if(newChild.getParentNode() != null)
{
try
{
newChild.getParentNode().removeChild(newChild);
}
catch (DOMException domException){}//ignore if we don't find it, this is just for safty
}
children.set(index, newChild);
((CNode) newChild).setParent(this);
//remove old child's parent, as it's now lightly detached
((CNode) oldChild).setParent(null);
cascadeDOMEvent(cdomEvent);
return oldChild;
}
index++;
}
preparedEvent = null;
return null;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#removeChild(org.w3c.dom.Node)
*/
@Override
public Node removeChild(Node oldChild) throws DOMException
{
if(nodeList.remove(oldChild) == true)
{
if(oldChild instanceof CNode)
{
((CNode) oldChild).setParent(null);
}
cascadeDOMEvent(prepareEvent(EventType.DELETE, this));
return oldChild;
}
else
{
throw new DOMException(DOMException.NOT_FOUND_ERR,"couldn't find "+oldChild.getLocalName() +" in "+ getLocalName());
}
}
public void removeChildrenAll()
{
for (Node node : nodeList)
{
((CNode) node).setParent(null);
}
nodeList.clear();
cascadeDOMEvent(prepareEvent(EventType.DELETE, this));
}
public void removeNodeTypeChildrenAll(short nodeType)
{
for(int index = 0; index < nodeList.getLength(); index++)
{
if(nodeList.item(index).getNodeType() == nodeType)
{
nodeList.remove(index);
index--;
}
}
cascadeDOMEvent(prepareEvent(EventType.DELETE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#appendChild(org.w3c.dom.Node)
*/
@Override
public Node appendChild(Node newChild) throws DOMException
{
CDOMEvent cdomEvent = prepareEvent(EventType.INSERT, this);
if(newChild instanceof CNode)
{
if(newChild.getParentNode() != null)
{
if(newChild.getParentNode().equals(this))
{
return newChild;
}
newChild.getParentNode().removeChild(newChild);
}
nodeList.add(newChild);
((CNode) newChild).setParent(this);
((CNode) newChild).setOwnerDocument(getOwnerDocument());
cascadeDOMEvent(cdomEvent);
return newChild;
}
else
{
preparedEvent = null;
Thread.dumpStack();
throw new UnsupportedOperationException();
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#hasChildNodes()
*/
@Override
public boolean hasChildNodes()
{
return nodeList.size() > 0;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#cloneNode(boolean)
*/
@Override
public Node cloneNode(boolean deep)
{
if(deep == false)
{
Thread.dumpStack();
throw new UnsupportedOperationException();
}
CNode clonedNode = null;
try
{
clonedNode = EqualityProcessor.clone(this);
final CDOMEvent cdomEvent = new CDOMEvent(EventType.INSERT,this);
NodeProcessor nodeProcessor = new NodeProcessor()
{
//Set all of the correct parent nodes
@Override
public void process(Node parentNode,Node node) throws Exception
{
CNode cNode = (CNode) node;
cNode.preparedEvent = cdomEvent;
cNode.setParent((CNode) parentNode);
cNode.preparedEvent = null;
}
};
walkTree(null,clonedNode, nodeProcessor, false);
}
catch (Exception exception)
{
Logger.getGlobal().log(Level.SEVERE, "Couldn't clone "+this, exception);
}
return clonedNode;
}
@Override
public void preClone(Object parentObject, Object clonedObject) throws Exception
{
}
public void postClone(Object parentObject, Object clonedObject)
{
CNode clonednode = (CNode) clonedObject;
//we treat these differently, because we don't want them to recurse
clonednode.ownerDocument = ownerDocument;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#normalize()
*/
@Override
public void normalize()
{
Text textNode = null;
for(int index = 0; index < nodeList.size(); index++)
{
Node node = nodeList.get(index);
if(node.getNodeType() == Node.TEXT_NODE)
{
if(textNode == null)
{
textNode = (Text) node;
}
else
{
textNode.appendData(node.getTextContent());
nodeList.remove(index);
index--;
}
}
else
{
textNode = null;
node.normalize();
}
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String)
*/
@Override
public boolean isSupported(String feature, String version)
{
Thread.dumpStack();
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNamespaceURI()
*/
@Override
public String getNamespaceURI()
{
return this.namespaceURI;
}
public void setNamespaceURI(String namespaceURI)
{
this.namespaceURI = namespaceURI;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getPrefix()
*/
@Override
public String getPrefix()
{
if(getNodeName().contains(":"))
{
return getNodeName().split(":")[0];
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setPrefix(java.lang.String)
*/
@Override
public void setPrefix(String prefix) throws DOMException
{
if(getNamespaceURI() == null)
{
throw new DOMException(DOMException.NAMESPACE_ERR, "Can't set prefix on an element w/o a namespaceURI");
}
if (getNodeName().contains(":"))
{
nodeName = prefix+":"+nodeName.split(":")[1];
}
else
{
nodeName = prefix+":"+nodeName;
}
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getLocalName()
*/
@Override
public String getLocalName()
{
if(getNodeName() == null)
{
System.out.println("WTF");
}
if(getNodeName().contains(":"))
{
try
{
//System.out.println(getNodeName().split(":")[1]);
return getNodeName().split(":")[1];
} catch (Exception exception)
{
exception.printStackTrace();
}
return getNodeName();
}
else
{
return getNodeName();
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#hasAttributes()
*/
@Override
public boolean hasAttributes()
{
return attributeList.size() > 0;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getBaseURI()
*/
@Override
public String getBaseURI()
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
private int[] getPositionIndexArray(Node node)
{
int[] positionIndexArray = null;
Node parentNode = node;
int depth = 0;
while(true)
{
if(parentNode == null || parentNode.getNodeType() == Node.DOCUMENT_NODE)
{
break;
}
depth++;
parentNode = parentNode.getParentNode();
}
//make the new array
positionIndexArray = new int[depth];
parentNode = node;
while(true)
{
if(parentNode == null || parentNode.getNodeType() == Node.DOCUMENT_NODE)
{
break;
}
int precedingSiblingCount = 0;
Node precedingSiblingNode = parentNode;
while(precedingSiblingNode != null)
{
precedingSiblingCount++;
precedingSiblingNode = precedingSiblingNode.getPreviousSibling();
}
depth--;
positionIndexArray[depth] = precedingSiblingCount;
parentNode = parentNode.getParentNode();
}
return positionIndexArray;
}
/*
* (non-Javadoc)
* @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
* This could be made more efficient, but it's designed to work with any DOMImplementation instead.
*/
@Override
public short compareDocumentPosition(Node other) throws DOMException
{
int[] localPositionIndexArray = getPositionIndexArray(this);
//System.out.println(Arrays.toString(localPositionIndexArray));
int[] otherPositionIndexArray = getPositionIndexArray(other);
//System.out.println(Arrays.toString(otherPositionIndexArray));
int matchIndex = 0;
for(; matchIndex < localPositionIndexArray.length && matchIndex < otherPositionIndexArray.length; matchIndex++)
{
//mark all matching with a neg one
if(localPositionIndexArray[matchIndex] != otherPositionIndexArray[matchIndex])
{
matchIndex--;
break;
}
}
if(localPositionIndexArray.length == otherPositionIndexArray.length)
{
int endPosition = localPositionIndexArray.length-1;
if(endPosition == matchIndex)
{
return 0;
}
else if (endPosition-1 == matchIndex)
{
if(localPositionIndexArray[endPosition] < otherPositionIndexArray[endPosition])
{
return Node.DOCUMENT_POSITION_FOLLOWING;
}
else
{
return Node.DOCUMENT_POSITION_PRECEDING;
}
}
else
{
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
}
else //one might contain the other or they are detached
{
//test for contains
if(localPositionIndexArray.length < otherPositionIndexArray.length)
{
if(matchIndex < localPositionIndexArray.length-2)
{
return Node.DOCUMENT_POSITION_CONTAINS;
}
else
{
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
}
//test for contained
else //(localPositionIndexArray.length > otherPositionIndexArray.length)
{
if(matchIndex < otherPositionIndexArray.length-2)
{
return Node.DOCUMENT_POSITION_CONTAINS;
}
else
{
return Node.DOCUMENT_POSITION_DISCONNECTED;
}
}
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getTextContent()
*/
@Override
public String getTextContent() throws DOMException
{
StringBuilder stringBuilder = new StringBuilder();
for (Node node : nodeList)
{
if(node instanceof Text)
{
stringBuilder.append(((Text) node).getData());
}
else
{
stringBuilder.append(node.getTextContent());
}
}
return stringBuilder.toString();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setTextContent(java.lang.String)
*/
@Override
public void setTextContent(String textContent) throws DOMException
{
CText text = new CText();
text.setData(textContent);
removeChildrenAll();
appendChild(text);
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
*/
@Override
public boolean isSameNode(Node other)
{
return this.equals(other);
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
*/
@Override
public String lookupPrefix(String namespaceURI)
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
*/
@Override
public boolean isDefaultNamespace(String namespaceURI)
{
return namespaceURI.equals(ownerDocument.getDefaultNamespace());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
*/
@Override
public String lookupNamespaceURI(String prefix)
{
if(prefix == null)
{
return ownerDocument.getDefaultNamespace();
}
else
{
if(prefix.equals(getPrefix()) && getNamespaceURI() != null)
{
return getNamespaceURI();
}
else if(parentNode != null)
{
return parentNode.lookupNamespaceURI(prefix);
}
else
{
return null;
}
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
*/
@Override
public boolean isEqualNode(Node arg)
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
*/
@Override
public Object getFeature(String feature, String version)
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
*/
@Override
public Object setUserData(String key, Object data, UserDataHandler handler)
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getUserData(java.lang.String)
*/
@Override
public Object getUserData(String key)
{
// TODO Auto-generated method stub
Thread.dumpStack();
throw new UnsupportedOperationException();
}
@Override
public String toString()
{
return getNodeName()+" @"+attributeList.toString();
}
/**
* gets a stream of all nodes including this and all children
* @return
*/
public Stream<CNode> stream()
{
return Stream.concat(
Stream.of(this).map((node)->(CNode)node),
Stream.concat(
attributeList.stream().map(node->(CNode)node),
nodeList.stream().map(node->(CNode)node).flatMap(CNode::stream)));
}
public int getDepth()
{
int depth = 0;
Node parentNode = getParentNode();
while(parentNode != null)
{
depth++;
parentNode = parentNode.getParentNode();
}
return depth;
}
/**
* This will run a nodeProcessor against the entire subtree of a node.
* @param node start node
* @param processor processor to run
* @param startWithChildren just recurse over the children of this element, but don't process the start node.
* @throws Exception
*/
public static void walkTree(Node parentNode, Node node, NodeProcessor processor,boolean startWithChildren) throws Exception
{
if(startWithChildren == false)
{
processor.process(parentNode, node);
NamedNodeMap attributes = node.getAttributes();
for(int index = 0; index < attributes.getLength(); index++)
{
processor.process(node,attributes.item(index));
}
}
NodeList children = node.getChildNodes();
for(int index = 0; index < children.getLength(); index++)
{
walkTree(node,children.item(index), processor,false);
}
}
public void detach()
{
if(getParentNode() != null)
{
((CNodeList) getParentNode().getChildNodes()).remove(this);
}
ownerDocument = null;
parentNode = null;
cascadeDOMEvent(prepareEvent(EventType.UPDATE, this));
}
/**
* associate this element with it's defining element from a schema
* @param nodeDefinition
*/
public void setNodeDefinition(CNodeDefinition nodeDefinition)
{
this.nodeDefinition = nodeDefinition;
}
/**
* see setDefinition
* @return
*/
public CNodeDefinition getNodeDefinition()
{
if(nodeDefinition == null && ownerDocument != null && ownerDocument.getNamespaceSchemaMap().containsKey(namespaceURI))
{
nodeDefinition = CNodeDefinition.getDefinitionForNode(this);
}
return nodeDefinition;
}
public boolean isValid() throws CValidationException, Exception
{
return isValid(false, null);
}
public boolean isValid(boolean deep, Vector<CValidationException> exceptionVector) throws CValidationException, Exception
{
if(getNodeDefinition() != null)
{
//return getNodeDefinition().isValid(this,exceptionVector);
if(deep == true)
{
walkTree(null, this, (parentNode,node)->{((CNode) node)._isValid(exceptionVector);}, false);
}
}
else
{
nodeInvalid("X Missing node definition, or undefined node", this, exceptionVector);
return false;
}
return true;
}
private void _isValid(Vector<CValidationException> exceptionVector) throws CValidationException, Exception
{
if(this.getNodeType() == Node.TEXT_NODE && getNodeValue().isEmpty())
{
System.out.println("skipping text node validation");
return;
}
CNodeDefinition definition = getNodeDefinition();
if(definition == null)
{
definition = CNodeDefinition.getDefinitionForNode(this);
setNodeDefinition(definition);
}
if(getNodeDefinition() != null)
{
getNodeDefinition().isValid(this,exceptionVector);
}
else if(getNodeType() == Node.ATTRIBUTE_NODE)
{
nodeInvalid("Attribute @"+getLocalName()+" is undefined", this, exceptionVector);
}
else
{
nodeInvalid("Missing node definition, or undefined node", this, exceptionVector);
}
}
}