/******************************************************************************* * Copyright (c) 2000, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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) */ 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. */ 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 (int i= 0; i < children.length; i++) { AbstractDataTreeNode child= children[i].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); } 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. */ 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 */ public Object getData() { return data; } /** * Returns true if the receiver can carry data, false otherwise. */ boolean hasData() { return true; } /** * Sets the data for the node */ void setData(Object o) { data= o; } /** * Simplifies the given node, and answers its replacement. */ 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); } /* (non-Javadoc * Method declared on IStringPoolParticipant */ public void storeStrings(StringPool set) { super.storeStrings(set); //copy data for thread safety Object o= data; if (o instanceof IStringPoolParticipant) ((IStringPoolParticipant)o).shareStrings(set); } /** * Returns a unicode representation of the node. This method is used for debugging purposes only * (no NLS support needed) */ public String toString() { return "a DataTreeNode(" + this.getName() + ") with " + getChildren().length + " children."; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /** * Returns a constant describing the type of node. */ int type() { return T_COMPLETE_NODE; } }