/*
* Copyright 2007 Guy Van den Broeck
*
* 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.outerj.daisy.diff.html.dom;
import java.util.ArrayList;
import java.util.List;
import org.outerj.daisy.diff.html.dom.helper.LastCommonParentResult;
/**
* Represents any element in the DOM tree of a HTML file.
*/
public abstract class Node {
protected TagNode parent;
/**
* This constructor not only sets the parameter as the parent for the
* created node, but also appends the created node to the collection of the
* parent's children.
*
* @param parent
* - the parent for the new node.
*/
public Node(TagNode parent) {
this.parent = parent;
if (parent != null)
parent.addChild(this);
}
/**
* @return the parent itself (NOT a copy!)
*/
public TagNode getParent() {
return parent;
}
/**
* this recursive method returns list of the ancestors that is ordered
* starting from the root by the depth. Index of an element in that list
* corresponds its depth (if depth of the root is 0)
*
* @return ordered by depth list of the ancestors or an empty
* <code>List<TagNode></code> if the parent is null.
*/
public List<TagNode> getParentTree() {
List<TagNode> parentTree = new ArrayList<TagNode>(5);
if (getParent() != null) {
parentTree.addAll(getParent().getParentTree());
parentTree.add(getParent());
}
return parentTree;
}
// change for correct insertion of the deleted nodes
/**
* "equals" method should work differently for the case where the compared
* nodes are from the same tree, and in that case return true only if it's
* the same object This method returns the root of the tree (which should be
* common ancestor for every node in the tree). If the roots are the same
* object, then the nodes are in the same tree.
*
* @return the "top" ancestor if this node has a parent,<br>
* or<br>
* the node itself if there is no parent, and this is a
* <code>TagNode</code><br>
* or<br>
* null if there is no parents and this node isn't a
* <code>TagNode</code>
*/
public TagNode getRoot() {
TagNode ancestor = getParent();
if (ancestor != null) {
return ancestor.getRoot();
} else if (this instanceof TagNode) {
return (TagNode) this;
} else {
return null;
}
}
public abstract List<Node> getMinimalDeletedSet(long id);
public void detectIgnorableWhiteSpace() {
// no op
}
/**
* Descent the ancestors list for both nodes stopping either at the first
* no-match case or when either of the lists is exhausted.
*
* @param other
* - the node to check for common parent
* @return result that contains last common parent, depth, index in the list
* of children of the common parent of an ancestor(or self) of this
* node that is immediate child of the common parent.
* @throws java.lang.IllegalArgumentException
* if the parameter is null
*/
public LastCommonParentResult getLastCommonParent(Node other) {
if (other == null)
throw new IllegalArgumentException("The given TextNode is null");
LastCommonParentResult result = new LastCommonParentResult();
// note that these lists are never null,
// but sometimes are empty.
List<TagNode> myParents = getParentTree();
List<TagNode> otherParents = other.getParentTree();
int i = 1;
boolean isSame = true;
while (isSame && i < myParents.size() && i < otherParents.size()) {
if (!myParents.get(i).isSameTag(otherParents.get(i))) {
isSame = false;
} else {
// After the while, the index i-1 must be the last common parent
i++;
}
}
result.setLastCommonParentDepth(i - 1);
result.setLastCommonParent(myParents.get(i - 1));
if (!isSame) {// found different parent
result.setIndexInLastCommonParent(myParents.get(i - 1).getIndexOf(
myParents.get(i)));
result.setSplittingNeeded();
} else if (myParents.size() < otherParents.size()) {
// current node is not so deeply nested
result.setIndexInLastCommonParent(myParents.get(i - 1).getIndexOf(
this));
} else if (myParents.size() > otherParents.size()) {
// All tags matched but there are tags left in this tree -
// other node is not so deeply nested
result.setIndexInLastCommonParent(myParents.get(i - 1).getIndexOf(
myParents.get(i)));
result.setSplittingNeeded();
} else {
// All tags matched until the very last one in both trees
// or there were no tags besides the BODY
result.setIndexInLastCommonParent(myParents.get(i - 1).getIndexOf(
this));
}
return result;
}
/**
* changes the parent field of this node. Does NOT append/remove itself from
* the previous or the new parent children collection.
*
* @param parent
* - new parent to assign
*/
public void setParent(TagNode parent) {
this.parent = parent;
}
public abstract Node copyTree();
/**
* @return <code>true</code> only if one of the ancestors is <pre> tag.
* <code>false</code> otherwise (including case where this node is
* <pre> tag)
*/
public boolean inPre() {
for (TagNode ancestor : getParentTree()) {
if (ancestor.isPre()) {
return true;
}
}
return false;
}
private boolean whiteBefore = false;
private boolean whiteAfter = false;
public boolean isWhiteBefore() {
return whiteBefore;
}
public void setWhiteBefore(boolean whiteBefore) {
this.whiteBefore = whiteBefore;
}
public boolean isWhiteAfter() {
return whiteAfter;
}
public void setWhiteAfter(boolean whiteAfter) {
this.whiteAfter = whiteAfter;
}
public abstract Node getLeftMostChild();
public abstract Node getRightMostChild();
}