blob: 89751536aba56aeebbbecb39c3aef2546fd9e4e7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.model;
import java.util.ArrayList;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.core.resources.IResourceDelta;
/**
* @see ICElementDelta
*/
public class CElementDelta implements ICElementDelta {
/**
* The element that this delta describes the change to.
* @see #getElement()
*/
protected ICElement fChangedElement;
/**
* @see #getKind()
*/
private int fKind = 0;
/**
* @see #getFlags()
*/
private int fChangeFlags = 0;
/**
* @see #getMovedFromElement()
*/
protected ICElement fMovedFromHandle = null;
/**
* @see #getMovedToElement()
*/
protected ICElement fMovedToHandle = null;
/**
* Collection of resource deltas that correspond to non c resources deltas.
*/
protected IResourceDelta[] resourceDeltas = null;
/**
* Counter of resource deltas
*/
protected int resourceDeltasCounter;
/**
* Empty array of ICElementDelta
*/
protected static ICElementDelta[] fgEmptyDelta= new ICElementDelta[] {};
/**
* @see #getAffectedChildren()
*/
protected ICElementDelta[] fAffectedChildren = fgEmptyDelta;
/**
* Creates the root delta. To create the nested delta
* hierarchies use the following convenience methods. The root
* delta can be created at any level (i.e. project, folder).
* <ul>
* <li><code>added(ICElement)</code>
* <li><code>changed(ICElement)</code>
* <li><code>moved(ICElement, ICElement)</code>
* <li><code>removed(ICElement)</code>
* <li><code>renamed(ICElement, ICElement)</code>
* </ul>
*/
public CElementDelta(ICElement element) {
super();
fChangedElement = element;
}
/**
* Adds the child delta to the collection of affected children. If the
* child is already in the collection, walk down the hierarchy.
*/
protected void addAffectedChild(CElementDelta child) {
switch (fKind) {
case ADDED:
case REMOVED:
// no need to add a child if this parent is added or removed
return;
case CHANGED:
fChangeFlags |= F_CHILDREN;
break;
default:
fKind = CHANGED;
fChangeFlags |= F_CHILDREN;
}
// if a child delta is added to a translation unit delta or below,
// it's a fine grained delta
if (!(fChangedElement.getElementType() >= ICElement.C_UNIT)) {
fineGrained();
}
if (fAffectedChildren.length == 0) {
fAffectedChildren = new ICElementDelta[] {child};
return;
}
// Check if we already have the delta.
ICElementDelta existingChild = null;
int existingChildIndex = -1;
for (int i = 0; i < fAffectedChildren.length; i++) {
// handle case of two jars that can be equals but not in the same project
if (equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) {
existingChild = fAffectedChildren[i];
existingChildIndex = i;
break;
}
}
if (existingChild == null) { //new affected child
fAffectedChildren= growAndAddToArray(fAffectedChildren, child);
} else {
switch (existingChild.getKind()) {
case ADDED:
switch (child.getKind()) {
// child was added then added -> it is added
case ADDED:
// child was added then changed -> it is added
case CHANGED:
return;
// child was added then removed -> noop
case REMOVED:
fAffectedChildren = removeAndShrinkArray(fAffectedChildren, existingChildIndex);
return;
}
break;
case REMOVED:
switch (child.getKind()) {
// child was removed then added -> it is changed
case ADDED:
child.fKind = CHANGED;
fAffectedChildren[existingChildIndex] = child;
return;
// child was removed then changed -> it is removed
case CHANGED:
// child was removed then removed -> it is removed
case REMOVED:
return;
}
break;
case CHANGED:
switch (child.getKind()) {
// child was changed then added -> it is added
case ADDED:
// child was changed then removed -> it is removed
case REMOVED:
fAffectedChildren[existingChildIndex] = child;
return;
// child was changed then changed -> it is changed
case CHANGED:
((CElementDelta) existingChild).fChangeFlags |= child.fChangeFlags;
ICElementDelta[] children = child.getAffectedChildren();
for (ICElementDelta element : children) {
CElementDelta childsChild = (CElementDelta) element;
((CElementDelta) existingChild).addAffectedChild(childsChild);
}
// add the non-c resource deltas if needed
// note that the child delta always takes
// precedence over this existing child delta
// as non-c resource deltas are always
// created last (by the DeltaProcessor)
IResourceDelta[] resDeltas = child.getResourceDeltas();
if (resDeltas != null) {
((CElementDelta)existingChild).resourceDeltas = resDeltas;
((CElementDelta)existingChild).resourceDeltasCounter = child.resourceDeltasCounter;
}
return;
}
break;
default:
// unknown -> existing child becomes the child with the existing child's flags
int flags = existingChild.getFlags();
fAffectedChildren[existingChildIndex] = child;
child.fChangeFlags |= flags;
}
}
}
/**
* Creates the nested deltas resulting from an add operation.
* Convenience method for creating add deltas.
* The constructor should be used to create the root delta
* and then an add operation should call this method.
*/
public void added(ICElement element) {
CElementDelta addedDelta = new CElementDelta(element);
addedDelta.fKind = ADDED;
insertDeltaTree(element, addedDelta);
}
/**
* Adds the child delta to the collection of affected children. If the
* child is already in the collection, walk down the hierarchy.
*/
protected void addResourceDelta(IResourceDelta child) {
switch (fKind) {
case ADDED:
case REMOVED:
// no need to add a child if this parent is added or removed
return;
case CHANGED:
fChangeFlags |= F_CONTENT;
break;
default:
fKind = CHANGED;
fChangeFlags |= F_CONTENT;
}
if (resourceDeltas == null) {
resourceDeltas = new IResourceDelta[5];
resourceDeltas[resourceDeltasCounter++] = child;
return;
}
if (resourceDeltas.length == resourceDeltasCounter) {
// need a resize
System.arraycopy(resourceDeltas, 0, (resourceDeltas = new IResourceDelta[resourceDeltasCounter * 2]), 0, resourceDeltasCounter);
}
resourceDeltas[resourceDeltasCounter++] = child;
}
/**
* Creates the nested deltas resulting from a change operation.
* Convenience method for creating change deltas.
* The constructor should be used to create the root delta
* and then a change operation should call this method.
*/
public void changed(ICElement element, int changeFlag) {
CElementDelta changedDelta = new CElementDelta(element);
changedDelta.fKind = CHANGED;
changedDelta.fChangeFlags |= changeFlag;
insertDeltaTree(element, changedDelta);
}
/**
* Creates the nested deltas for a closed element.
*/
public void closed(ICElement element) {
CElementDelta delta = new CElementDelta(element);
delta.fKind = CHANGED;
delta.fChangeFlags |= F_CLOSED;
insertDeltaTree(element, delta);
}
/**
* Returns whether the two c elements are equals and have the same parent.
*/
protected boolean equalsAndSameParent(ICElement e1, ICElement e2) {
ICElement parent1;
return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent());
}
/**
* Creates the nested delta deltas based on the affected element
* its delta, and the root of this delta tree. Returns the root
* of the created delta tree.
*/
protected CElementDelta createDeltaTree(ICElement element, CElementDelta delta) {
CElementDelta childDelta = delta;
ArrayList<ICElement> ancestors= getAncestors(element);
if (ancestors == null) {
if (equalsAndSameParent(delta.getElement(), getElement())) {
// handle case of two jars that can be equals but not in the
// same project
// the element being changed is the root element
fKind= delta.fKind;
fChangeFlags = delta.fChangeFlags;
fMovedToHandle = delta.fMovedToHandle;
fMovedFromHandle = delta.fMovedFromHandle;
} else {
// the given delta is not the root or a child - illegal
//Assert.isTrue(false);
}
} else {
for (int i = 0, size = ancestors.size(); i < size; i++) {
ICElement ancestor = ancestors.get(i);
CElementDelta ancestorDelta = new CElementDelta(ancestor);
ancestorDelta.addAffectedChild(childDelta);
childDelta = ancestorDelta;
}
}
return childDelta;
}
/**
* Returns the <code>CElementDelta</code> for the given element
* in the delta tree, or null, if no delta for the given element is found.
*/
protected CElementDelta find(ICElement e) {
if (equalsAndSameParent(fChangedElement, e)) { // handle case of two jars that can be equals but not in the same project
return this;
}
for (ICElementDelta element : fAffectedChildren) {
CElementDelta delta = ((CElementDelta)element).find(e);
if (delta != null) {
return delta;
}
}
return null;
}
/**
* Mark this delta as a fine-grained delta.
*/
public void fineGrained() {
fChangeFlags |= F_FINE_GRAINED;
}
/**
* @see ICElementDelta
*/
@Override
public ICElementDelta[] getAddedChildren() {
return getChildrenOfType(ADDED);
}
/**
* @see ICElementDelta
*/
@Override
public ICElementDelta[] getAffectedChildren() {
return fAffectedChildren;
}
/**
* Returns a collection of all the parents of this element up to (but
* not including) the root of this tree in bottom-up order. If the given
* element is not a descendant of the root of this tree, <code>null</code>
* is returned.
*/
private ArrayList<ICElement> getAncestors(ICElement element) {
ICElement parent = element.getParent();
if (parent == null) {
return null;
}
ArrayList<ICElement> parents = new ArrayList<ICElement>();
while (!parent.equals(fChangedElement)) {
parents.add(parent);
parent = parent.getParent();
if (parent == null) {
return null;
}
}
parents.trimToSize();
return parents;
}
/**
* @see ICElementDelta
*/
@Override
public ICElementDelta[] getChangedChildren() {
return getChildrenOfType(CHANGED);
}
/**
* @see ICElementDelta
*/
protected ICElementDelta[] getChildrenOfType(int type) {
int length = fAffectedChildren.length;
if (length == 0) {
return new ICElementDelta[] {};
}
ArrayList<ICElementDelta> children= new ArrayList<ICElementDelta>(length);
for (int i = 0; i < length; i++) {
if (fAffectedChildren[i].getKind() == type) {
children.add(fAffectedChildren[i]);
}
}
ICElementDelta[] childrenOfType = new ICElementDelta[children.size()];
children.toArray(childrenOfType);
return childrenOfType;
}
/**
* Returns the delta for a given element. Only looks below this
* delta.
*/
protected CElementDelta getDeltaFor(ICElement element) {
if (equalsAndSameParent(getElement(), element)) // handle case of two jars that can be equals but not in the same project
return this;
if (fAffectedChildren.length == 0)
return null;
int childrenCount = fAffectedChildren.length;
for (int i = 0; i < childrenCount; i++) {
CElementDelta delta = (CElementDelta)fAffectedChildren[i];
if (equalsAndSameParent(delta.getElement(), element)) { // handle case of two jars that can be equals but not in the same project
return delta;
}
delta = delta.getDeltaFor(element);
if (delta != null)
return delta;
}
return null;
}
/**
* @see ICElementDelta
*/
@Override
public ICElement getElement() {
return fChangedElement;
}
/**
* @see ICElementDelta
*/
@Override
public int getFlags() {
return fChangeFlags;
}
/**
* @see ICElementDelta
*/
@Override
public int getKind() {
return fKind;
}
/**
* @see ICElementDelta
*/
@Override
public ICElement getMovedFromElement() {
return fMovedFromHandle;
}
/**
* @see ICElementDelta
*/
@Override
public ICElement getMovedToElement() {
return fMovedToHandle;
}
/**
* @see ICElementDelta
*/
@Override
public ICElementDelta[] getRemovedChildren() {
return getChildrenOfType(REMOVED);
}
/**
* Return the collection of resource deltas. Return null if none.
*/
@Override
public IResourceDelta[] getResourceDeltas() {
if (resourceDeltas == null)
return null;
if (resourceDeltas.length != resourceDeltasCounter) {
System.arraycopy(resourceDeltas, 0, resourceDeltas = new IResourceDelta[resourceDeltasCounter], 0, resourceDeltasCounter);
}
return resourceDeltas;
}
/**
* Adds the new element to a new array that contains all of the elements of the old array.
* Returns the new array.
*/
protected ICElementDelta[] growAndAddToArray(ICElementDelta[] array, ICElementDelta addition) {
ICElementDelta[] old = array;
array = new ICElementDelta[old.length + 1];
System.arraycopy(old, 0, array, 0, old.length);
array[old.length] = addition;
return array;
}
/**
* Creates the delta tree for the given element and delta, and then
* inserts the tree as an affected child of this node.
*/
protected void insertDeltaTree(ICElement element, CElementDelta delta) {
CElementDelta childDelta= createDeltaTree(element, delta);
if (!equalsAndSameParent(element, getElement())) {
addAffectedChild(childDelta);
}
}
/**
* Creates the nested deltas resulting from an move operation.
* Convenience method for creating the "move from" delta.
* The constructor should be used to create the root delta
* and then the move operation should call this method.
*/
public void movedFrom(ICElement movedFromElement, ICElement movedToElement) {
CElementDelta removedDelta = new CElementDelta(movedFromElement);
removedDelta.fKind = REMOVED;
removedDelta.fChangeFlags |= F_MOVED_TO;
removedDelta.fMovedToHandle = movedToElement;
insertDeltaTree(movedFromElement, removedDelta);
}
/**
* Creates the nested deltas resulting from an move operation.
* Convenience method for creating the "move to" delta.
* The constructor should be used to create the root delta
* and then the move operation should call this method.
*/
public void movedTo(ICElement movedToElement, ICElement movedFromElement) {
CElementDelta addedDelta = new CElementDelta(movedToElement);
addedDelta.fKind = ADDED;
addedDelta.fChangeFlags |= F_MOVED_FROM;
addedDelta.fMovedFromHandle = movedFromElement;
insertDeltaTree(movedToElement, addedDelta);
}
/**
* Creates the nested deltas for an opened element.
*/
public void opened(ICElement element) {
CElementDelta delta = new CElementDelta(element);
delta.fKind = CHANGED;
delta.fChangeFlags |= F_OPENED;
insertDeltaTree(element, delta);
}
/**
* Removes the child delta from the collection of affected children.
*/
protected void removeAffectedChild(CElementDelta child) {
int index = -1;
if (fAffectedChildren != null) {
for (int i = 0; i < fAffectedChildren.length; i++) {
if (equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project
index = i;
break;
}
}
}
if (index >= 0) {
fAffectedChildren= removeAndShrinkArray(fAffectedChildren, index);
}
}
/**
* Removes the element from the array.
* Returns the a new array which has shrunk.
*/
protected ICElementDelta[] removeAndShrinkArray(ICElementDelta[] old, int index) {
ICElementDelta[] array = new ICElementDelta[old.length - 1];
if (index > 0)
System.arraycopy(old, 0, array, 0, index);
int rest = old.length - index - 1;
if (rest > 0)
System.arraycopy(old, index + 1, array, index, rest);
return array;
}
/**
* Creates the nested deltas resulting from an delete operation.
* Convenience method for creating removed deltas.
* The constructor should be used to create the root delta
* and then the delete operation should call this method.
*/
public void removed(ICElement element) {
CElementDelta removedDelta= new CElementDelta(element);
insertDeltaTree(element, removedDelta);
CElementDelta actualDelta = getDeltaFor(element);
if (actualDelta != null) {
actualDelta.fKind = REMOVED;
actualDelta.fChangeFlags = 0;
actualDelta.fAffectedChildren = fgEmptyDelta;
}
}
/**
* Creates the nested deltas resulting from a change operation.
* Convenience method for creating change deltas.
* The constructor should be used to create the root delta
* and then a change operation should call this method.
*/
public void binaryParserChanged(ICElement element) {
CElementDelta attachedDelta = new CElementDelta(element);
attachedDelta.fKind = CHANGED;
attachedDelta.fChangeFlags |= F_BINARY_PARSER_CHANGED;
insertDeltaTree(element, attachedDelta);
}
/**
* Creates the nested deltas resulting from a change operation.
* Convenience method for creating change deltas.
* The constructor should be used to create the root delta
* and then a change operation should call this method.
*/
public void sourceAttached(ICElement element) {
CElementDelta attachedDelta = new CElementDelta(element);
attachedDelta.fKind = CHANGED;
attachedDelta.fChangeFlags |= F_SOURCEATTACHED;
insertDeltaTree(element, attachedDelta);
}
/**
* Creates the nested deltas resulting from a change operation.
* Convenience method for creating change deltas.
* The constructor should be used to create the root delta
* and then a change operation should call this method.
*/
public void sourceDetached(ICElement element) {
CElementDelta detachedDelta = new CElementDelta(element);
detachedDelta.fKind = CHANGED;
detachedDelta.fChangeFlags |= F_SOURCEDETACHED;
insertDeltaTree(element, detachedDelta);
}
/**
* Returns a string representation of this delta's
* structure suitable for debug purposes.
*
* @see #toString()
*/
public String toDebugString(int depth) {
StringBuilder buffer = new StringBuilder();
for (int i= 0; i < depth; i++) {
buffer.append('\t');
}
buffer.append(((CElement)getElement()).toDebugString());
buffer.append(" ["); //$NON-NLS-1$
switch (getKind()) {
case ICElementDelta.ADDED :
buffer.append('+');
break;
case ICElementDelta.REMOVED :
buffer.append('-');
break;
case ICElementDelta.CHANGED :
buffer.append('*');
break;
default :
buffer.append('?');
break;
}
buffer.append("]: {"); //$NON-NLS-1$
int changeFlags = getFlags();
boolean prev = false;
if ((changeFlags & ICElementDelta.F_CHILDREN) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CHILDREN"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_CONTENT) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CONTENT"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_MOVED_FROM) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
//buffer.append("MOVED_FROM(").append(((CElement)getMovedFromElement()).toStringWithAncestors().append(')'); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_MOVED_TO) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
//buffer.append("MOVED_TO(").append(((CElement)getMovedToElement()).toStringWithAncestors()).append(')'); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_MODIFIERS) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("MODIFIERS CHANGED"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CHANGED TO PATHENTRY INCLUDE"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_CHANGED_PATHENTRY_MACRO) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CHANGED TO PATHENTRY MACRO"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_ADDED_PATHENTRY_LIBRARY) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("ADDED TO PATHENTRY LIBRARY"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_REMOVED_PATHENTRY_LIBRARY) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("ADDED TO PATHENTRY LIBRARY"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_PATHENTRY_REORDER) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("PATHENTRY REORDER"); //$NON-NLS-1$
prev = true;
}
if ((changeFlags & ICElementDelta.F_CONTENT_TYPE) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("CONTENT_TYPE"); //$NON-NLS-1$
prev = true;
}
//if ((changeFlags & ICElementDelta.F_SUPER_TYPES) != 0) {
// if (prev)
// buffer.append(" | "); //$NON-NLS-1$
// buffer.append("SUPER TYPES CHANGED"); //$NON-NLS-1$
// prev = true;
//}
if ((changeFlags & ICElementDelta.F_FINE_GRAINED) != 0) {
if (prev)
buffer.append(" | "); //$NON-NLS-1$
buffer.append("FINE GRAINED"); //$NON-NLS-1$
prev = true;
}
buffer.append("}"); //$NON-NLS-1$
ICElementDelta[] children = getAffectedChildren();
if (children != null) {
for (int i = 0; i < children.length; ++i) {
buffer.append("\n"); //$NON-NLS-1$
buffer.append(((CElementDelta) children[i]).toDebugString(depth + 1));
}
}
for (int i = 0; i < resourceDeltasCounter; i++) {
buffer.append("\n");//$NON-NLS-1$
for (int j = 0; j < depth+1; j++) {
buffer.append('\t');
}
IResourceDelta resourceDelta = resourceDeltas[i];
buffer.append(resourceDelta.toString());
buffer.append("["); //$NON-NLS-1$
switch (resourceDelta.getKind()) {
case IResourceDelta.ADDED :
buffer.append('+');
break;
case IResourceDelta.REMOVED :
buffer.append('-');
break;
case IResourceDelta.CHANGED :
buffer.append('*');
break;
default :
buffer.append('?');
break;
}
buffer.append("]"); //$NON-NLS-1$
}
return buffer.toString();
}
/**
* Returns a string representation of this delta's
* structure suitable for debug purposes.
*/
@Override
public String toString() {
return toDebugString(0);
}
}