blob: 8d8a72a988ceddd237a95d8ea1afecd0cdc820f9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.dtree;
import org.eclipse.core.internal.utils.*;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
/**
* <code>DataTreeNode</code>s are the nodes of a <code>DataTree</code>. Their
* information and their subtrees are complete, and do not represent deltas on
* another node or subtree.
*/
public class DataTreeNode extends AbstractDataTreeNode {
protected Object data;
/**
* Creates a new node
*
* @param name name of node
* @param data data for node
*/
public DataTreeNode(String name, Object data) {
super(name, AbstractDataTreeNode.NO_CHILDREN);
this.data = data;
}
/**
* Creates a new node
*
* @param name name of node
* @param data data for node
* @param children children for new node
*/
public DataTreeNode(String name, Object data, AbstractDataTreeNode[] children) {
super(name, children);
this.data = data;
}
/**
* @see AbstractDataTreeNode#asBackwardDelta(DeltaDataTree, DeltaDataTree, IPath)
*/
@Override
AbstractDataTreeNode asBackwardDelta(DeltaDataTree myTree, DeltaDataTree parentTree, IPath key) {
if (parentTree.includes(key))
return parentTree.copyCompleteSubtree(key);
return new DeletedNode(name);
}
/**
* If this node is a node in a comparison tree, this method reverses
* the comparison for this node and all children. Returns null
* if this node should no longer be included in the comparison tree.
*/
@Override
AbstractDataTreeNode asReverseComparisonNode(IComparator comparator) {
NodeComparison comparison = null;
try {
comparison = ((NodeComparison) data).asReverseComparison(comparator);
} catch (ClassCastException e) {
Assert.isTrue(false, Messages.dtree_reverse);
}
int nextChild = 0;
for (AbstractDataTreeNode c : children) {
AbstractDataTreeNode child = c.asReverseComparisonNode(comparator);
if (child != null) {
children[nextChild++] = child;
}
}
if (nextChild == 0 && comparison.getUserComparison() == 0) {
/* no children and no change */
return null;
}
/* set the new data */
data = comparison;
/* shrink child array as necessary */
if (nextChild < children.length) {
AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[nextChild];
System.arraycopy(children, 0, newChildren, 0, nextChild);
children = newChildren;
}
return this;
}
AbstractDataTreeNode compareWith(DataTreeNode other, IComparator comparator) {
AbstractDataTreeNode[] comparedChildren = compareWith(children, other.children, comparator);
Object oldData = data;
Object newData = other.data;
/* don't allow comparison of implicit root node */
int userComparison = 0;
if (name != null) {
userComparison = comparator.compare(oldData, newData);
}
return new DataTreeNode(name, new NodeComparison(oldData, newData, NodeComparison.K_CHANGED, userComparison), comparedChildren);
}
@Override
AbstractDataTreeNode compareWithParent(IPath key, DeltaDataTree parent, IComparator comparator) {
if (!parent.includes(key))
return convertToAddedComparisonNode(this, NodeComparison.K_ADDED);
DataTreeNode inParent = (DataTreeNode) parent.copyCompleteSubtree(key);
return inParent.compareWith(this, comparator);
}
/**
* Creates and returns a new copy of the receiver.
*/
@Override
AbstractDataTreeNode copy() {
if (children.length > 0) {
AbstractDataTreeNode[] childrenCopy = new AbstractDataTreeNode[children.length];
System.arraycopy(children, 0, childrenCopy, 0, children.length);
return new DataTreeNode(name, data, childrenCopy);
}
return new DataTreeNode(name, data, children);
}
/**
* Returns a new node containing a child with the given local name in
* addition to the receiver's current children and data.
*
* @param localName
* name of new child
* @param childNode
* new child node
*/
DataTreeNode copyWithNewChild(String localName, DataTreeNode childNode) {
AbstractDataTreeNode[] children = this.children;
int left = 0;
int right = children.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
int compare = localName.compareTo(children[mid].name);
if (compare < 0) {
right = mid - 1;
} else if (compare > 0) {
left = mid + 1;
} else {
throw new Error(); // it shouldn't have been here yet
}
}
AbstractDataTreeNode[] newChildren = new AbstractDataTreeNode[children.length + 1];
System.arraycopy(children, 0, newChildren, 0, left);
childNode.setName(localName);
newChildren[left] = childNode;
System.arraycopy(children, left, newChildren, left + 1, children.length - left);
return new DataTreeNode(this.getName(), this.getData(), newChildren);
}
/**
* Returns a new node without the specified child, but with the rest
* of the receiver's current children and its data.
*
* @param localName
* name of child to exclude
*/
DataTreeNode copyWithoutChild(String localName) {
int index, newSize;
DataTreeNode newNode;
AbstractDataTreeNode children[];
index = this.indexOfChild(localName);
if (index == -1) {
newNode = (DataTreeNode) this.copy();
} else {
newSize = this.size() - 1;
children = new AbstractDataTreeNode[newSize];
newNode = new DataTreeNode(this.getName(), this.getData(), children);
newNode.copyChildren(0, index - 1, this, 0); //#from:to:with:startingAt:
newNode.copyChildren(index, newSize - 1, this, index + 1);
}
return newNode;
}
/**
* Returns an array of delta nodes representing the forward delta between
* the given two lists of nodes.
* The given nodes must all be complete nodes.
*/
protected static AbstractDataTreeNode[] forwardDeltaWith(AbstractDataTreeNode[] oldNodes, AbstractDataTreeNode[] newNodes, IComparator comparer) {
if (oldNodes.length == 0 && newNodes.length == 0) {
return NO_CHILDREN;
}
AbstractDataTreeNode[] childDeltas = null;
int numChildDeltas = 0;
int childDeltaMax = 0;
// do a merge
int oldIndex = 0;
int newIndex = 0;
while (oldIndex < oldNodes.length && newIndex < newNodes.length) {
String oldName = oldNodes[oldIndex].name;
String newName = newNodes[newIndex].name;
int compare = oldName.compareTo(newName);
if (compare == 0) {
AbstractDataTreeNode deltaNode = forwardDeltaWithOrNullIfEqual(oldNodes[oldIndex++], newNodes[newIndex++], comparer);
if (deltaNode != null) {
if (numChildDeltas >= childDeltaMax) {
if (childDeltas == null)
childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
else
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
}
childDeltas[numChildDeltas++] = deltaNode;
}
} else if (compare < 0) {
if (numChildDeltas >= childDeltaMax) {
if (childDeltas == null)
childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
else
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
}
childDeltas[numChildDeltas++] = new DeletedNode(oldName);
oldIndex++;
} else {
if (numChildDeltas >= childDeltaMax) {
if (childDeltas == null)
childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
else
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
}
childDeltas[numChildDeltas++] = newNodes[newIndex++];
}
}
while (oldIndex < oldNodes.length) {
if (numChildDeltas >= childDeltaMax) {
if (childDeltas == null)
childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
else
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
}
childDeltas[numChildDeltas++] = new DeletedNode(oldNodes[oldIndex++].name);
}
while (newIndex < newNodes.length) {
if (numChildDeltas >= childDeltaMax) {
if (childDeltas == null)
childDeltas = new AbstractDataTreeNode[childDeltaMax = 5];
else
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[childDeltaMax = childDeltaMax * 2 + 1], 0, numChildDeltas);
}
childDeltas[numChildDeltas++] = newNodes[newIndex++];
}
// trim size of result
if (numChildDeltas == 0) {
return NO_CHILDREN;
}
if (numChildDeltas < childDeltaMax) {
System.arraycopy(childDeltas, 0, childDeltas = new AbstractDataTreeNode[numChildDeltas], 0, numChildDeltas);
}
return childDeltas;
}
/**
* Returns a node representing the forward delta between
* the given two (complete) nodes.
*/
protected AbstractDataTreeNode forwardDeltaWith(DataTreeNode other, IComparator comparer) {
AbstractDataTreeNode deltaNode = forwardDeltaWithOrNullIfEqual(this, other, comparer);
if (deltaNode == null) {
return new NoDataDeltaNode(name, NO_CHILDREN);
}
return deltaNode;
}
/**
* Returns a node representing the forward delta between
* the given two (complete) nodes, or null if the two nodes are equal.
* Although typed as abstract nodes, the given nodes must be complete.
*/
protected static AbstractDataTreeNode forwardDeltaWithOrNullIfEqual(AbstractDataTreeNode oldNode, AbstractDataTreeNode newNode, IComparator comparer) {
AbstractDataTreeNode[] childDeltas = forwardDeltaWith(oldNode.children, newNode.children, comparer);
Object newData = newNode.getData();
if (comparer.compare(oldNode.getData(), newData) == 0) {
if (childDeltas.length == 0) {
return null;
}
return new NoDataDeltaNode(newNode.name, childDeltas);
}
return new DataDeltaNode(newNode.name, newData, childDeltas);
}
/**
* Returns the data for the node
*/
@Override
public Object getData() {
return data;
}
/**
* Returns true if the receiver can carry data, false otherwise.
*/
@Override
boolean hasData() {
return true;
}
/**
* Sets the data for the node
*/
void setData(Object o) {
data = o;
}
/**
* Simplifies the given node, and answers its replacement.
*/
@Override
AbstractDataTreeNode simplifyWithParent(IPath key, DeltaDataTree parent, IComparator comparer) {
/* If not in parent, can't be simplified */
if (!parent.includes(key)) {
return this;
}
/* Can't just call simplify on children since this will miss the case
where a child exists in the parent but does not in this.
See PR 1FH5RYA. */
DataTreeNode parentsNode = (DataTreeNode) parent.copyCompleteSubtree(key);
return parentsNode.forwardDeltaWith(this, comparer);
}
@Override
public void storeStrings(StringPool set) {
super.storeStrings(set);
//copy data for thread safety
Object o = data;
if (o instanceof IStringPoolParticipant)
((IStringPoolParticipant) o).shareStrings(set);
}
@Override
int type() {
return T_COMPLETE_NODE;
}
}