blob: 5407ba1f240e73413aabfd1a34e7457d76c36db1 [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.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.core.builder.*;
import org.eclipse.jdt.internal.core.Util;
import java.util.*;
class DeltaImpl implements IDelta {
protected IDelta fParent;
protected IDelta[] fChildren;
/* Non state specific object */
protected Object fObject;
protected String fName;
protected DeltaKey fKey;
protected StateImpl fNewState;
protected StateImpl fOldState;
protected int fStatus;
protected IImageContext fContext;
/* names for checklist items */
public static final String fgImage = "Image"/*nonNLS*/;
public static final String fgPackageCategory = "Packages"/*nonNLS*/;
public static final String fgTypeCategory = "Types"/*nonNLS*/;
public static final String fgBinaryCategory = "Binaries"/*nonNLS*/;
public static final String fgMethodCategory = "Methods"/*nonNLS*/;
public static final String fgPackage = "Package"/*nonNLS*/;
public static final String fgType = "Type"/*nonNLS*/;
public static final String fgBinary = "Binary"/*nonNLS*/;
public static final String fgMethod = "Method"/*nonNLS*/;
/* convenience structure for delta calculation process */
class DeltaInfo {
PackageInfo[] addedPkgs;
PackageInfo[] removedPkgs;
PackageInfo[] changedPkgs;
};
class PackageInfo {
IPackage pkg;
IType[] addedTypes;
IType[] removedTypes;
IType[] changedTypes;
IType[] changedBinaries;
};
/* shared empty array */
protected static final IDelta[] fgEmptyArray = new IDelta[0];
protected IncrementalImageBuilder fIncrementalBuilder;
static final int PS_CHANGED = 1; // has the principal structure changed?
static final int BIN_CHANGED = 2; // has the binary changed?
/**
* Creates a new root image delta
*/
DeltaImpl(IncrementalImageBuilder builder, IImageContext context) {
this(builder.getOldState(), builder.getNewState(), context, builder);
}
/**
* Creates a new root image delta
*/
protected DeltaImpl(StateImpl oldState, StateImpl newState, String name,
Object o, DeltaKey key, IDelta parent, int status) {
fNewState = (StateImpl)newState;
fOldState = (StateImpl)oldState;
fName = name;
fObject = o;
fParent = parent;
fKey = key;
fStatus = status;
fChildren = fgEmptyArray;
fContext = parent.getImageContext();
}
/**
* Creates a new root image delta
*/
DeltaImpl(IState oldState, IState newState, IImageContext context) {
this(oldState, newState, context, null);
}
/**
* Creates a new root image delta
*/
DeltaImpl(IState oldState, IState newState, IImageContext context, IncrementalImageBuilder builder) {
try {
fNewState = (StateImpl)newState;
fOldState = (StateImpl)oldState;
} catch (ClassCastException cce) {
Assert.isTrue(false, "Internal Error - Invalid states"/*nonNLS*/);
}
fStatus = CHANGED;
fName = fgImage;
fObject = newState.getDevelopmentContext().getImage();
fParent = null;
fKey = DeltaKey.getRoot();
fContext = context;
fChildren = fgEmptyArray;
fIncrementalBuilder = builder;
computeImageDelta();
}
/**
* Creates and returns a child with the given name and object
*/
protected DeltaImpl add(String name, Object object, int status) {
return add(name, object, getKey().add(object), status);
}
/**
* Creates and returns a child with the given name and object
*/
protected DeltaImpl add(String name, Object object, DeltaKey key, int status) {
return new DeltaImpl(
fOldState, fNewState, //same states as me
name, object, key, //given name, object and key
this, //parent
status); //added, removed, or changed
}
/**
* Returns the child of this delta with the given object
*/
protected IDelta at(Object o) {
if (fChildren != null){
for (int i = 0; i < fChildren.length; i++) {
if (o instanceof String) {
if (o.equals(fChildren[i].getName())) {
return fChildren[i];
}
}
else {
if (o.equals(fChildren[i].getObject())) {
return fChildren[i];
}
}
}
}
/* not found */
return null;
}
/**
* Compares the type in the old and new states.
* Returns a bit mask indicating whether the principal structure or binary have changed.
*/
protected int compareBuilderType(BuilderType type) {
if (!type.isAffected()) {
return 0;
}
TypeStructureEntry oldEntry = type.getOldTypeStructureEntry();
TypeStructureEntry newEntry = type.getNewTypeStructureEntry();
if (oldEntry == null || newEntry == null) {
return PS_CHANGED | BIN_CHANGED;
}
if (oldEntry == newEntry) {
return 0;
}
int result = 0;
IBinaryType oldType = type.getOldBinaryType();
IBinaryType newType = type.getNewBinaryType();
if (!BinaryStructure.compare(oldType, newType)) {
result |= PS_CHANGED;
}
/*
* If the source entry has changed, consider the binary to have changed
* even if the binary is actually the same. Some clients, such as the
* target manager, need to know when the source changes.
*/
if (!oldEntry.getSourceEntry().equals(newEntry.getSourceEntry())) {
result |= BIN_CHANGED;
} else
if (oldEntry.getCRC32() != newEntry.getCRC32()) {
result |= BIN_CHANGED;
} else {
boolean oldIsBinary = oldEntry.isBinary();
boolean newIsBinary = newEntry.isBinary();
if (oldIsBinary != newIsBinary) {
result |= BIN_CHANGED;
}
}
return result;
}
/**
* Returns an integer indicating whether anotherDelta is less than, equal to,
* or greater than this delta.
*/
public int compareTo(IDelta anotherDelta) {
/* is this used? */
Assert.isTrue(false, "TBD"/*nonNLS*/);
return 0;
}
/**
* Compares the type in the old and new states.
* Returns a bit mask indicating whether the principal structure or binary have changed.
*/
protected int compareTypes(IType handle) {
if (fIncrementalBuilder != null) {
return compareBuilderType(fIncrementalBuilder.getBuilderType(handle));
}
TypeStructureEntry oldEntry = fOldState.getTypeStructureEntry(handle, true);
TypeStructureEntry newEntry = fNewState.getTypeStructureEntry(handle, true);
if (oldEntry == null || newEntry == null) {
return PS_CHANGED | BIN_CHANGED;
}
if (oldEntry == newEntry) {
return 0;
}
int result = 0;
IBinaryType oldType = fOldState.getBinaryType(oldEntry);
IBinaryType newType = fNewState.getBinaryType(newEntry);
if (!BinaryStructure.compare(oldType, newType)) {
result |= PS_CHANGED;
}
/*
* If the source entry has changed, consider the binary to have changed
* even if the binary is actually the same. Some clients, such as the
* target manager, need to know when the source changes.
*/
if (!oldEntry.getSourceEntry().equals(newEntry.getSourceEntry())) {
result |= BIN_CHANGED;
} else
if (oldEntry.getCRC32() != newEntry.getCRC32()) {
result |= BIN_CHANGED;
} else {
boolean oldIsBinary = oldEntry.isBinary();
boolean newIsBinary = newEntry.isBinary();
if (oldIsBinary != newIsBinary) {
result |= BIN_CHANGED;
}
}
return result;
}
/**
* Computes the added classes for each added package.
* Returns a DeltaInfo object for each package describing the affected
* classes.
*/
protected PackageInfo[] computeAddedClasses(Vector pkgs) {
PackageInfo[] infos = new PackageInfo[pkgs.size()];
int i = 0;
for (Enumeration pkgEnum = pkgs.elements(); pkgEnum.hasMoreElements(); i++) {
IPackage pkg = (IPackage) pkgEnum.nextElement();
infos[i] = new PackageInfo();
infos[i].pkg = pkg;
IType[] types = ((IPackage) pkg.inState(fNewState)).getAllClasses();
for (int j = 0; j < types.length; ++j) {
types[j] = (IType) types[j].nonStateSpecific();
}
infos[i].addedTypes = types;
}//next package
return infos;
}//end function
/**
* Computes added, removed, and changed classes for a set of packages.
* Stores vectors of IType handles.
*/
protected void computeAllClasses(IPackage pkg,
Vector added, Vector removed, Vector changedTypes, Vector changedBinaries) {
/* collect a set of all classes in old package */
/* we have to make a copy here to avoid destroying the state */
Hashtable oldTypeTable = new Hashtable();
IType[] oldTypes = ((IPackage) pkg.inState(fOldState)).getAllClasses();
for (int i = 0; i < oldTypes.length; i++) {
IType handle = (IType)oldTypes[i].nonStateSpecific();
oldTypeTable.put(handle, handle);
}
/* iterate through new classes */
IType[] newTypes = ((IPackage)pkg.inState(fNewState)).getAllClasses();
for (int i = 0; i < newTypes.length; i++) {
IType handle = (IType)newTypes[i].nonStateSpecific();
if (oldTypeTable.remove(handle) != null) {
/* class is in both packages */
int code = compareTypes(handle);
if ((code & PS_CHANGED) != 0) {
changedTypes.addElement(handle);
}
if ((code & BIN_CHANGED) != 0) {
changedBinaries.addElement(handle);
}
} else {
/* its only in the new package */
added.addElement(handle);
}
}
/* remaining classes are removed */
for (Enumeration e = oldTypeTable.keys(); e.hasMoreElements();) {
removed.addElement(e.nextElement());
}
}
/**
* Computes added, removed, and changed packages. At this
* stage, if a package hasn't been added or removed, it is
* considered to be changed. Later, when the classes are
* calculated, the packages that don't have changed classes
* will be removed from the list. Stores vectors of non-state
* specific package handles.
*/
protected void computeAllPackages(
Vector added, Vector removed, Vector changed) {
/* do for each affected package */
for (Enumeration e = getAffectedPackages();e.hasMoreElements();) {
IPackage pkg = (IPackage)e.nextElement();
boolean inOld = pkg.inState(fOldState).isPresent();
boolean inNew = pkg.inState(fNewState).isPresent();
if (inOld) {
if (inNew) {
changed.addElement(pkg);
}
else {
// Package was removed.
removed.addElement(pkg);
}
}
else {
if (inNew) {
// Package was added.
added.addElement(pkg);
}
else {
// Package was in image context but was not in either state.
// Ignore it.
}
}
}
}
/**
* Computes added, removed, and changed classes for a set of packages.
* Returns a DeltaInfo object for each package describing the affected
* classes.
*/
protected PackageInfo[] computeChangedClasses(Vector pkgs) {
Vector vInfo = new Vector();
Vector toRemove = new Vector();
for (Enumeration pkgEnum = pkgs.elements(); pkgEnum.hasMoreElements();) {
IPackage pkg = (IPackage)pkgEnum.nextElement();
Vector vAdded = new Vector();
Vector vRemoved = new Vector();
Vector vChanged = new Vector();
Vector vBinaries = new Vector();
computeAllClasses(pkg, vAdded, vRemoved, vChanged, vBinaries);
/* if there are any affected classes */
if (vAdded.size() + vRemoved.size() + vChanged.size() + vBinaries.size() > 0) {
/* fill out package information */
PackageInfo pkgInfo = new PackageInfo();
pkgInfo.pkg = pkg;
pkgInfo.addedTypes = new IType[vAdded.size()];
vAdded.copyInto(pkgInfo.addedTypes);
pkgInfo.removedTypes = new IType[vRemoved.size()];
vRemoved.copyInto(pkgInfo.removedTypes);
pkgInfo.changedTypes = new IType[vChanged.size()];
vChanged.copyInto(pkgInfo.changedTypes);
pkgInfo.changedBinaries = new IType[vBinaries.size()];
vBinaries.copyInto(pkgInfo.changedBinaries);
vInfo.addElement(pkgInfo);
} else {
/* remove the element -- can't delete while enumerating */
toRemove.addElement(pkg);
}
}//next package
/* remove packages with no changes */
for (Enumeration e = toRemove.elements(); e.hasMoreElements();) {
pkgs.removeElement(e.nextElement());
}
/* convert info vector to array */
PackageInfo[] infos = new PackageInfo[vInfo.size()];
vInfo.copyInto(infos);
return infos;
}//end function
/**
* Calculates the added, removed, and changed packages, types and methods
* that consitute an image delta. The results are stored in the supplied
* DeltaInfo object
*/
protected DeltaInfo computeDeltaInfo() {
DeltaInfo info = new DeltaInfo();
/* compute packages */
Vector vAddedPkgs = new Vector();
Vector vRemovedPkgs = new Vector();
Vector vChangedPkgs = new Vector();
computeAllPackages(vAddedPkgs, vRemovedPkgs, vChangedPkgs);
/* compute classes */
info.addedPkgs = computeAddedClasses(vAddedPkgs);
info.removedPkgs = computeRemovedClasses(vRemovedPkgs);
info.changedPkgs = computeChangedClasses(vChangedPkgs);
return info;
}
/**
* Computes the image delta.
*/
protected void computeImageDelta() {
/* compute the delta info */
DeltaInfo info = computeDeltaInfo();
/* create categories, any or all of them may be null */
IDelta pkgCategory = createPackageCategory(this, info);
IDelta typeCategory = createCategory(this, info, fgTypeCategory);
IDelta binaryCategory = createCategory(this, info, fgBinaryCategory);
/* Create top level children */
int childCount = 0;
if (pkgCategory != null) childCount++;
if (typeCategory != null) childCount++;
if (binaryCategory != null) childCount++;
fChildren = new IDelta[childCount];
childCount = 0;
if (pkgCategory != null)
fChildren[childCount++] = pkgCategory;
if (typeCategory != null)
fChildren[childCount++] = typeCategory;
if (binaryCategory != null)
fChildren[childCount++] = binaryCategory;
}
/**
* Computes the removed classes for each removed package.
* Returns a DeltaInfo object for each package describing the affected
* classes.
*/
protected PackageInfo[] computeRemovedClasses(Vector pkgs) {
PackageInfo[] infos = new PackageInfo[pkgs.size()];
int i = 0;
for (Enumeration pkgEnum = pkgs.elements(); pkgEnum.hasMoreElements(); i++) {
IPackage pkg = (IPackage) pkgEnum.nextElement();
infos[i] = new PackageInfo();
infos[i].pkg = pkg;
IType[] types = ((IPackage) pkg.inState(fOldState)).getAllClasses();
for (int j = 0; j < types.length; ++j) {
types[j] = (IType) types[j].nonStateSpecific();
}
infos[i].removedTypes = types;
}//next package
return infos;
}//end function
/**
* Converts a vector of handles to an array of delta info objects of the given
* type.
*/
protected IDelta[] convertHandlesToDeltaArray(Vector vHandles, String type, int status) {
int i = 0;
IDelta[] results = new IDelta[vHandles.size()];
for (Enumeration e = vHandles.elements(); e.hasMoreElements(); i++) {
results[i] = add(type, e.nextElement(), status);
}
return results;
}
/**
* Copies information from a PackageInfo to the children of a DeltaImpl.
* If types is true, copy the type information, otherwise copy the binary information
*/
protected void copyInfoToChildren(DeltaImpl delta, PackageInfo info, boolean types) {
/* are we looking at binaries or types ? */
IType[] changed = types ? info.changedTypes : info.changedBinaries;
String name = types ? fgType : fgBinary;
int count = info.addedTypes.length + info.removedTypes.length + changed.length;
if (count == 0) {
delta.fStatus = SAME;
delta.setChildren(new IDelta[0]);
return;
}
IDelta[] children = new IDelta[count];
int i = 0;
for (; i < info.addedTypes.length; i++) {
children[i] = delta.add(name, info.addedTypes[i], ADDED);
}
for (int j = 0; j < info.removedTypes.length; j++, i++) {
children[i] = delta.add(name, info.removedTypes[j], REMOVED);
}
for (int j = 0; j < changed.length; j++, i++) {
children[i] = delta.add(name, changed[j], CHANGED);
}
delta.setChildren(children);
}
/**
* Creates the types category of the image delta. Returns
* the Delta object for the category. If there are no added,
* removed, or changed types, returns null.
*/
protected IDelta createCategory(DeltaImpl parent, DeltaInfo info, String categoryName) {
int size = info.changedPkgs.length + info.addedPkgs.length + info.removedPkgs.length;
if (size == 0) return null;
/* collection of packages that are children of the type category */
IDelta[] children = new IDelta[size];
/* create Type category node */
DeltaImpl category = parent.add(categoryName, parent.getObject(), parent.getKey().add(categoryName), CHANGED);
/* create new deltas for changed packages */
int count = 0;
for (int i = 0; i < info.changedPkgs.length; i++) {
PackageInfo pkgInfo = info.changedPkgs[i];
DeltaImpl child = (DeltaImpl)category.add(fgPackage, pkgInfo.pkg, CHANGED);
copyInfoToChildren(child, pkgInfo, categoryName == fgTypeCategory);
children[count++] = child;
}
/* create deltas for added and removed packages */
for (int i = 0; i < info.addedPkgs.length; i++) {
children[count++] = category.packageForTypeCategory(ADDED, info.addedPkgs[i]);
}
for (int i = 0; i < info.removedPkgs.length; i++) {
children[count++] = category.packageForTypeCategory(REMOVED, info.removedPkgs[i]);
}
category.setChildren(children);
return category;
}
/**
* Creates the package category of the image delta. Returns
* the Delta object for the category. If there are no added,
* removed, or changed packages, returns null. A changed package
* is a package that has had a package fragment added or removed.
*/
protected IDelta createPackageCategory(DeltaImpl parent, DeltaInfo info) {
/* number of children of this category */
int size = info.addedPkgs.length + info.changedPkgs.length + info.removedPkgs.length;
if (size == 0) return null;
/* create the category */
DeltaImpl category =
parent.add(
fgPackageCategory,
parent.getObject(),
parent.getKey().add(fgPackageCategory),
CHANGED);
IDelta[] children = new IDelta[size];
int count = 0;
for (int i = 0; i < info.addedPkgs.length; i++) {
children[count++] = category.add(fgPackage, info.addedPkgs[i].pkg, ADDED);
}
for (int i = 0; i < info.removedPkgs.length; i++) {
children[count++] = category.add(fgPackage, info.removedPkgs[i].pkg, REMOVED);
}
for (int i = 0; i < info.changedPkgs.length; i++) {
IPackage pkg = info.changedPkgs[i].pkg;
if (!Util.equalArraysOrNull(
fOldState.getPackageMap().getFragments(pkg),
fNewState.getPackageMap().getFragments(pkg))) {
children[count++] = category.add(fgPackage, pkg, CHANGED);
}
}
/* there could be no changed packages */
if (count == 0) {
return null;
}
/* compact if some changed packages did not actually change */
if (count < children.length) {
System.arraycopy(children, 0, children = new IDelta[count], 0, count);
}
category.setChildren(children);
return category;
}
public void dump() {
dump(this, 0);
}
protected static void dump(IDelta delta, int depth) {
for (int i = 0; i < depth; ++i)
System.out.print(" "/*nonNLS*/);
System.out.println(delta);
IDelta[] children = delta.getAffectedSubdeltas();
for (int i = 0; i < children.length; ++i) {
dump(children[i], depth+1);
}
}
/**
* Returns the delta reached by navigating the given
* relative path from this object.
* It is an error if the delta could never have such a descendent.
* <pre>
* - navigation off a checklist with the wrong child name
* is always bad
* - navigation off a batch delta is common when you don't
* yet know whether the object is present; this is allowed
* - navigation off a leaf delta is always bad
* </pre>
*
* @param path the path to follow
* @exception InvalidKeyException if an invalid key is given.
*/
public IDelta follow(IDeltaKey path) throws InvalidKeyException {
IDelta delta = this;
for (int i = 0, size = path.size(); i < size; ++i) {
delta = ((DeltaImpl) delta).at(path.at(i));
if (delta == null) {
throw new InvalidKeyException();
}
}
return delta;
}
/**
* Returns the immediate subdeltas of this delta that are additions
* (i.e. their status is Added).
*/
public IDelta[] getAddedSubdeltas() {
if (fChildren == null) return fgEmptyArray;
Vector vAdded = new Vector();
for (int i = 0; i < fChildren.length; i++) {
if (fChildren[i].getStatus() == IDelta.ADDED) {
vAdded.addElement(fChildren[i]);
}
}
IDelta[] added = new IDelta[vAdded.size()];
vAdded.copyInto(added);
return added;
}
/**
* Returns an enumeration of packages which are possibly affected.
*/
public Enumeration getAffectedPackages() {
/* Non-naive case - the image builder knows. */
if (fIncrementalBuilder != null) {
return fIncrementalBuilder.getAffectedPackages();
}
/* Naive case - take union of packages in old and new state. */
Hashtable affected = new Hashtable(21);
for (Enumeration e = fOldState.getPackageMap().getAllPackages(); e.hasMoreElements();) {
IPackage pkg = (IPackage) e.nextElement();
affected.put(pkg, pkg);
}
for (Enumeration e = fNewState.getPackageMap().getAllPackages(); e.hasMoreElements();) {
IPackage pkg = (IPackage) e.nextElement();
affected.put(pkg, pkg);
}
return affected.keys();
}
/**
* Returns the immediate subdeltas of this delta that are
* not the same (i.e. their status is either Added, Removed, or Changed).
*/
public IDelta[] getAffectedSubdeltas() {
if (fChildren == null) return fgEmptyArray;
Vector vAffected = new Vector();
for (int i = 0; i < fChildren.length; i++) {
if (fChildren[i].getStatus() != IDelta.SAME) {
vAffected.addElement(fChildren[i]);
}
}
IDelta[] affected = new IDelta[vAffected.size()];
vAffected.copyInto(affected);
return affected;
}
/**
* Returns the immediate subdeltas of this delta that are true
* changes, not additions or removals (i.e. their status is Changed).
*/
public IDelta[] getChangedSubdeltas() {
if (fChildren == null) return fgEmptyArray;
Vector vChanged = new Vector();
for (int i = 0; i < fChildren.length; i++) {
if (fChildren[i].getStatus() == IDelta.CHANGED) {
vChanged.addElement(fChildren[i]);
}
}
IDelta[] changed = new IDelta[vChanged.size()];
vChanged.copyInto(changed);
return changed;
}
/**
* Returns the ImageContext that the delta is restricted to.
*
* @see IImageBuilder#getImageDelta
*/
public IImageContext getImageContext() {
return fContext;
}
/**
* Returns the delta key for this delta.
* Delta keys often contain non-state-specific handles, but
* never state-specific ones.
*
* @see DeltaKey
*/
public IDeltaKey getKey() {
return fKey;
}
/**
* Returns the name of the aspect being compared in this delta.
*/
public String getName() {
return fName;
}
/**
* Returns the object in the new state that is the focus of this delta.
* It only make sense to talk about the 'new object' if the object
* that is the focus of this delta is a Handle. If it is not, this
* returns null.
*
* @return the state-specific handle of the object in the new state.
*/
public IHandle getNewObject() {
IHandle handle;
try {
handle = (IHandle)fObject;
} catch (ClassCastException cce) {
//not a handle
return null;
}
return handle.inState(fNewState);
}
/**
* Returns the new state to which this delta pertains.
*/
public IState getNewState() {
return fNewState;
}
/**
* Returns the object that is the focus of this delta.
* The result is often a Handle, but in
* some cases it is another type of object.
* When it is a Handle, it is non-state-specific.
*/
public Object getObject() {
return fObject;
}
/**
* Returns the object in the old state that is the focus of this delta.
* It only make sense to talk about the 'old object' if the object
* that is the focus of this delta is a Handle. If it is not, this
* returns null.
*
* @return the state-specific handle of the object in the old state.
*/
public IHandle getOldObject() {
IHandle handle;
try {
handle = (IHandle)fObject;
} catch (ClassCastException cce) {
//not a handle
return null;
}
return handle.inState(fOldState);
}
/**
* Returns the old state to which this delta pertains.
*/
public org.eclipse.jdt.internal.core.builder.IState getOldState() {
return fOldState;
}
/**
* Returns the parent delta of this delta, or null if it has no parent.
*/
public IDelta getParent() {
return fParent;
}
/**
* Returns the immediate subdeltas of this delta that are removals
* (i.e. their status is Removed).
*/
public IDelta[] getRemovedSubdeltas() {
if (fChildren == null) return fgEmptyArray;
Vector vRemoved = new Vector();
for (int i = 0; i < fChildren.length; i++) {
if (fChildren[i].getStatus() == IDelta.REMOVED) {
vRemoved.addElement(fChildren[i]);
}
}
IDelta[] removed = new IDelta[vRemoved.size()];
vRemoved.copyInto(removed);
return removed;
}
/**
* Returns the root delta of the tree containing this delta.
*/
public IDelta getRoot() {
/* don't bother caching the root at each branch for now */
if (fKey == DeltaKey.getRoot()) {
return this;
} else {
return getParent().getRoot();
}
}
/**
* Returns the status of this delta. If this delta
* is not applicable, it always returns SAME.
* If the status is not currently known, it is computed
* (UNKNOWN is never returned).
*/
public int getStatus() {
return fStatus;
}
/**
* Returns the status of this delta if it is known.
* Returns UNKNOWN if it is not known whether the object has changed.
*
*/
public int getStatusIfKnown() {
return fStatus;
}
/**
* Returns an array of Delta objects that are children of this delta.
* Returns an array of length 0 if this delta has no children,
* or if it is not composite.
*/
public IDelta[] getSubdeltas() {
return fChildren;
}
/**
* Returns whether this delta is a composite delta that is further
* broken down into subdeltas.
*/
public boolean hasSubdeltas() {
return fChildren != null && fChildren.length > 0;
}
/**
* Creates a package delta for the types or binaries category.
* The package delta can be for either an added or removed package,
* according to the constant given. The package delta will have
* children for each type in the package.
*/
protected IDelta packageForTypeCategory(int status, PackageInfo info) {
/* create the package delta */
DeltaImpl pkgDelta = (DeltaImpl)this.add(fgPackage, info.pkg, status);
IType[] types = (status == ADDED ? info.addedTypes : info.removedTypes);
IDelta[] children = new IDelta[types.length];
for (int i = 0; i < types.length; i++) {
children[i] = pkgDelta.add(fgType, types[i], status);
}
pkgDelta.setChildren(children);
return pkgDelta;
}
/**
* Sets the children of this delta
*/
protected void setChildren(IDelta[] children) {
fChildren = children;
}
/**
* Return a string of the form:
* status key
*
* status will be one of the following:
* + if status is ADDED
* - if status is REMOVED
* * if status is CHANGED
* = if status is SAME
* ? if status is UNKNOWN
* The string returned is only for debugging purposes,
* and the contents of the string may change in the future.
* @return java.lang.String
*/
public String toString() {
StringBuffer sb = new StringBuffer("DeltaImpl("/*nonNLS*/);
switch (fStatus) {
case ADDED: sb.append("+"/*nonNLS*/);
break;
case REMOVED: sb.append("-"/*nonNLS*/);
break;
case CHANGED: sb.append("*"/*nonNLS*/);
break;
case SAME: sb.append("="/*nonNLS*/);
break;
case UNKNOWN: sb.append("?"/*nonNLS*/);
break;
default: sb.append("(ERROR)"/*nonNLS*/);
}
if (fKey.isRoot()) {
sb.append(fOldState);
sb.append("-->"/*nonNLS*/);
sb.append(fNewState);
}
else {
for (int i = 0; i < fKey.size(); ++i) {
if (i != 0) {
sb.append('/');
}
sb.append(fKey.at(i));
}
}
sb.append(')');
return sb.toString();
}
}