blob: bb54ba75d5150b1db82516a934f618f90396450b [file] [log] [blame]
package org.eclipse.jdt.internal.core.builder.impl;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.core.Assert;
import org.eclipse.jdt.internal.core.Util;
import org.eclipse.jdt.internal.core.builder.IType;
import org.eclipse.jdt.internal.core.util.Dumper;
import org.eclipse.jdt.internal.core.util.IDumpable;
/**
* A node in the image builder dependency graph.
* @see IDependencyGraph
*/
public abstract class AbstractNode implements INode, IDumpable, Cloneable {
/**
* The "order number" of the node.
* Nodes with lower order numbers are earlier
* in the topological order.
* An order of -1 indicates that the order has not yet been calculated.
*/
protected int fOrder = -1;
/**
* Nodes that this node depends on
*/
protected INode[] fDependencies = fgEmptyNodeList;
/**
* Nodes that depend on this node
*/
protected INode[] fDependents = fgEmptyNodeList;
protected int fNumDependents = 0;
/**
* Default list of dependencies or dependents
*/
protected static final INode[] fgEmptyNodeList = new INode[0];
/**
* Hide the default constructor to force specialized instantiation of subclasses
*/
protected AbstractNode() {
}
/**
* Adds a node that depends on this node.
* IMPORTANT: This should only be called by addDependency.
*/
public void addDependent(INode nodeThatDependsOnMe) {
/* check if I already have this dependent */
/*
for (int i = fNumDependents; --i >= 0;) {
if (fDependents[i] == nodeThatDependsOnMe) {
System.out.println("Attempt to add duplicate dependent " + nodeThatDependsOnMe.getElement() + " to " + getElement());
return;
}
}
*/
if (fNumDependents >= fDependents.length) {
/* grow array */
INode[] newDependents = new INode[fNumDependents == 0 ? 5 : fNumDependents*2+1];
System.arraycopy(fDependents, 0, newDependents, 0, fNumDependents);
fDependents = newDependents;
}
fDependents[fNumDependents++] = nodeThatDependsOnMe;
}
/**
* Clears all outgoing dependency links for this node
*/
public void clearDependencies() {
/* do for all my dependencies (Nodes that I depend on) */
INode[] dependencies = getDependencies();
for (int i = 0; i < dependencies.length; i++) {
dependencies[i].removeDependent(this);
}
fDependencies = fgEmptyNodeList;
invalidateOrder();
}
/**
* Returns a copy of this node, without copying dependencies. Used
* by DependencyGraph.copy().
*/
public abstract AbstractNode copy();
/**
* Adds a node that depends on this node.
* IMPORTANT: This should only be called by dependency graph copy methods.
* The dependents array at this point contains either new dependents or null
* values. This method must find the first null entry in the dependents array
* and add the given value.
*/
public void copyAddDependent(INode nodeThatDependsOnMe) {
/* find first null entry */
int min = 0;
int max = fNumDependents - 1;
int mid = (max + min) / 2;
boolean found = false;
/* binary search until on or next to first non-null entry */
try {
while (max > min) {
if (fDependents[mid] == null) {
/* look in lower half */
max = mid - 1;
} else {
/* look in higher half */
min = mid + 1;
}
mid = (max + min) / 2;
}
/* linear search for first non-null entry */
while (fDependents[min] != null) {
min++;
}
/* add the dependent */
fDependents[min] = nodeThatDependsOnMe;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException in AbstractNode.copyAddDependent()"/*nonNLS*/);
}
}
/**
* Returns a copy of this node, but with the dependents and dependencies
* left unchanged. They are later replaced using replaceDeps().
* Be careful that this implementation applies to any information in subclasses.
* Right now, all information except the dependents are non-state-specific
* and can be shared.
*/
INode copyWithoutReplacingDeps() {
AbstractNode newNode = this.copy();
/* copy dependencies into new array */
int depCount = fDependencies.length;
newNode.fDependencies = new INode[depCount];
for (int i = depCount; --i >= 0;) {
newNode.fDependencies[i] = fDependencies[i];
}
newNode.fNumDependents = fNumDependents;
newNode.fDependents = new INode[fNumDependents];
newNode.fOrder = fOrder;
return newNode;
}
/**
* For debugging only.
*/
public void dump(Dumper dumper) {
dumper.dump("element"/*nonNLS*/, getElement());
if (getKind() == JCU_NODE)
dumper.dump("types"/*nonNLS*/, getTypes());
Object[] dependencies = new Object[fDependencies.length];
for (int i = 0; i < fDependencies.length; ++i) {
dependencies[i] = fDependencies[i].getElement();
}
dumper.dump("dependencies"/*nonNLS*/, dependencies);
Object[] dependents = new Object[fNumDependents];
for (int i = 0; i < fDependents.length; ++i) {
dependents[i] = fDependents[i].getElement();
}
dumper.dump("dependents"/*nonNLS*/, dependents);
}
/**
* Make sure equality tests are never carried out on nodes
*/
public boolean equals(Object o) {
Assert.isTrue(false, "Internal Error - Equality not defined for dependency graph nodes"/*nonNLS*/);
return false;
}
/**
* Returns the nodes that this node depends on. A change to the principal structure
* of any of these nodes may affect the principal structure of this node.
*/
public INode[] getDependencies() {
return fDependencies;
}
/**
* Returns the nodes that depend on this node. A change to the principal structure
* of this node may affect the principal structure of the returned dependents.
*/
public INode[] getDependents() {
if (fNumDependents < fDependents.length) {
System.arraycopy(fDependents, 0, fDependents = new INode[fNumDependents], 0, fNumDependents);
}
return fDependents;
}
/**
* Returns the number of bytes that this node uses.
* For debugging and profiling purposes only.
*/
int getFootprint() {
/* 5 slots plus 8 bytes for object header */
int size = 28;
/* size of dep arrays */
if (fDependencies != null) {
size += fDependencies.length * 4;
}
if (fDependents != null) {
size += fDependents.length * 4;
}
return size;
}
/**
* @see INode
*/
public int getOrder() {
return getOrder(0);
}
/**
* Returns the order number, calculating it if not already done.
* seen is a set of previously visited nodes.
*/
private int getOrder(int recursionLevel) {
if (fOrder != -1) {
return fOrder;
}
// trace(recursionLevel, -1);
fOrder = 0; // Mark as non-invalid to break cycles
int max = -1;
for (int i = 0, len = fDependencies.length; i < len; ++i) {
int order = ((AbstractNode) fDependencies[i]).getOrder(recursionLevel + 1);
if (order > max) {
max = order;
}
}
fOrder = max + 1;
// trace(recursionLevel, fOrder);
return fOrder;
}
/**
* Returns the types that belong to this node
*/
public abstract IType[] getTypes();
/**
* Invalidates the order number, for this node and all dependents.
*/
public void invalidateOrder() {
if (fOrder != -1) {
fOrder = -1;
/* do for each node that depends on me */
for (int i = 0, len = fNumDependents; i < len; ++i) {
((AbstractNode) fDependents[i]).invalidateOrder();
}
}
}
/**
* Removes a node on which this node depends.
* This -does- remove the backwards link from the other node.
*/
public void removeDependency(INode nodeThatIDependOn) {
nodeThatIDependOn.removeDependent(this);
int size = fDependencies.length;
for (int i = size; --i >= 0;) {
if (fDependencies[i] == nodeThatIDependOn) {
/* shrink array */
if (--size == 0) {
fDependencies = fgEmptyNodeList;
} else {
INode[] newDependencies = new INode[size];
System.arraycopy(fDependencies, 0, newDependencies, 0, i);
System.arraycopy(fDependencies, i + 1, newDependencies, i, size - i);
fDependencies = newDependencies;
}
invalidateOrder();
return;
}
}
}
/**
* Removes a node that depends on this node.
* This does not remove the backwards link.
*/
public void removeDependent(INode nodeThatDependsOnMe) {
int size = fNumDependents;
for (int i = size; --i >= 0;) {
if (fDependents[i] == nodeThatDependsOnMe) {
/* shrink array */
if (--size == 0) {
fDependents = fgEmptyNodeList;
} else {
INode[] newDependents = new INode[size];
System.arraycopy(fDependents, 0, newDependents, 0, i);
System.arraycopy(fDependents, i + 1, newDependents, i, size - i);
fDependents = newDependents;
}
fNumDependents = size;
return;
}
}
}
/**
* This node has previously been copied without updating its
* dependency references. Now replace the dependency references
* with the corresponding nodes in the given graph.
* Note that we only need to replace the dependencies, because the dependents
* are added as backward links from other nodes.
*/
void replaceDeps(DependencyGraph graph) {
int depCount = fDependencies.length;
INode newNode;
for (int i = 0; i < depCount; i++) {
/* get new node for old node */
newNode = graph.getNodeFor(fDependencies[i]);
newNode.copyAddDependent(this);
fDependencies[i] = newNode;
}
}
/**
* Sets the nodes that this node depends on.
* Backwards links are added automatically.
*/
public void setDependencies(INode[] nodesThatIDependOn) {
/* clear old dependencies before nuking array */
clearDependencies();
fDependencies = nodesThatIDependOn;
/* add backwards links */
for (int i = nodesThatIDependOn.length; --i >= 0;) {
nodesThatIDependOn[i].addDependent(this);
}
invalidateOrder();
}
public void trace(int recursionLevel, int refCount) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < recursionLevel; ++i)
sb.append(' ');
sb.append(this);
sb.append(": "/*nonNLS*/);
sb.append(refCount);
System.out.println(sb.toString());
}
}