*
* @return null if the document is consistent, an error report otherwise.
*/
public ErrorReport checkXMLTree( final DocumentImpl doc )
{
final DOMFile domDb = ( (NativeBroker)broker ).getDOMFile();
return( (ErrorReport)new DOMTransaction( this, domDb, Lock.WRITE_LOCK, doc ) {
public Object start() {
EmbeddedXMLStreamReader reader = null;
try {
final ElementImpl root = (ElementImpl)doc.getDocumentElement();
reader = broker.getXMLStreamReader( root, true );
NodeId nodeId;
boolean attribsAllowed = false;
int expectedAttribs = 0;
int attributeCount = 0;
while( reader.hasNext() ) {
final int status = reader.next();
nodeId = (NodeId)reader.getProperty( EmbeddedXMLStreamReader.PROPERTY_NODE_ID );
ElementNode parent = null;
if( ( status != XMLStreamReader.END_ELEMENT ) && !elementStack.isEmpty() ) {
parent = elementStack.peek();
parent.childCount++;
// test parent-child relation
if( !nodeId.isChildOf( parent.elem.getNodeId() ) ) {
return( new ErrorReport.ResourceError( ErrorReport.NODE_HIERARCHY, "Node " + nodeId + " is not a child of " + parent.elem.getNodeId() ) );
}
// test sibling relation
if( ( parent.prevSibling != null ) && !( nodeId.isSiblingOf( parent.prevSibling ) && ( nodeId.compareTo( parent.prevSibling ) > 0 ) ) ) {
return( new ErrorReport.ResourceError( ErrorReport.INCORRECT_NODE_ID, "Node " + nodeId + " is not a sibling of " + parent.prevSibling ) );
}
parent.prevSibling = nodeId;
}
switch( status ) {
case XMLStreamReader.ATTRIBUTE: {
attributeCount++;
break;
}
case XMLStreamReader.END_ELEMENT: {
if( elementStack.isEmpty() ) {
return( new org.exist.backup.ErrorReport.ResourceError( ErrorReport.NODE_HIERARCHY, "Error in node hierarchy: received END_ELEMENT event " + "but stack was empty!" ) );
}
final ElementNode lastElem = elementStack.pop();
if( lastElem.childCount != lastElem.elem.getChildCount() ) {
return( new ErrorReport.ResourceError( org.exist.backup.ErrorReport.NODE_HIERARCHY, "Element reports incorrect child count: expected " + lastElem.elem.getChildCount() + " but found " + lastElem.childCount ) );
}
break;
}
case XMLStreamReader.START_ELEMENT: {
if( nodeId.getTreeLevel() <= defaultIndexDepth ) {
// check dom.dbx btree, which maps the node
// id to the node's storage address
// look up the node id and check if the
// returned storage address is correct
final NativeBroker.NodeRef nodeRef = new NativeBroker.NodeRef( doc.getDocId(), nodeId );
try {
final long p = domDb.findValue( nodeRef );
if( p != reader.getCurrentPosition() ) {
final Value v = domDb.get( p );
if( v == null ) {
return( new ErrorReport.IndexError( ErrorReport.DOM_INDEX, "Failed to access node " + nodeId + " through dom.dbx index. Wrong storage address. Expected: " + p + "; got: " + reader.getCurrentPosition() + " - ", doc.getDocId() ) );
}
}