/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.core.workspace; import java.util.ArrayList; import org.eclipse.core.resources.IResourceDelta; /** * ModelWorkspaceDeltaImpl * * @since 8.0 */ public class ModelWorkspaceDeltaImpl implements ModelWorkspaceDelta { /** * The element that this delta describes the change to. * @see #getElement() */ protected ModelWorkspaceItem fChangedElement; /** * @see #getKind() */ private int fKind = 0; /** * @see #getFlags() */ private int fChangeFlags = 0; /** * @see #getAffectedChildren() */ protected ModelWorkspaceDelta[] fAffectedChildren = fgEmptyDelta; /** * Collection of resource deltas that correspond to non java resources deltas. */ protected IResourceDelta[] resourceDeltas = null; /** * Counter of resource deltas */ protected int resourceDeltasCounter; /** * @see #getMovedFromHandle() */ protected ModelWorkspaceItem fMovedFromHandle = null; /** * @see #getMovedToHandle() */ protected ModelWorkspaceItem fMovedToHandle = null; /** * Empty array of ModelWorkspaceDelta */ protected static ModelWorkspaceDelta[] fgEmptyDelta= new ModelWorkspaceDelta[] {}; /** * Construct an instance of ModelWorkspaceDeltaImpl. * */ public ModelWorkspaceDeltaImpl( final ModelWorkspaceItem changedItem ) { super(); this.fChangedElement = changedItem; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getAddedChildren() */ @Override public ModelWorkspaceDelta[] getAddedChildren() { return getChildrenOfType(ADDED); } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getAffectedChildren() */ @Override public ModelWorkspaceDelta[] getAffectedChildren() { return fAffectedChildren; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getChangedChildren() */ @Override public ModelWorkspaceDelta[] getChangedChildren() { return getChildrenOfType(CHANGED); } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getElement() */ @Override public ModelWorkspaceItem getElement() { return this.fChangedElement; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getFlags() */ @Override public int getFlags() { return this.fChangeFlags; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getKind() */ @Override public int getKind() { return fKind; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getMovedFromElement() */ @Override public ModelWorkspaceItem getMovedFromElement() { return fMovedFromHandle; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getMovedToElement() */ @Override public ModelWorkspaceItem getMovedToElement() { return fMovedToHandle; } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getRemovedChildren() */ @Override public ModelWorkspaceDelta[] getRemovedChildren() { return getChildrenOfType(REMOVED); } /* (non-Javadoc) * @See org.teiid.designer.core.workspace.ModelWorkspaceDelta#getResourceDeltas() */ @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; } /** * @see IJavaElementDelta */ protected ModelWorkspaceDelta[] getChildrenOfType(int type) { int length = fAffectedChildren.length; if (length == 0) { return new ModelWorkspaceDelta[] {}; } ArrayList children= new ArrayList(length); for (int i = 0; i < length; i++) { if (fAffectedChildren[i].getKind() == type) { children.add(fAffectedChildren[i]); } } ModelWorkspaceDelta[] childrenOfType = new ModelWorkspaceDelta[children.size()]; children.toArray(childrenOfType); return childrenOfType; } /** * 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 delta tree for the given element and delta, and then * inserts the tree as an affected child of this node. */ protected void insertDeltaTree( final ModelWorkspaceItem element, final ModelWorkspaceDeltaImpl delta) { ModelWorkspaceDeltaImpl childDelta = createDeltaTree(element, delta); if (!this.equalsAndSameParent(element, getElement())) { // handle case of two jars that can be equals but not in the same project addAffectedChild(childDelta); } } /** * 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 ModelWorkspaceDeltaImpl createDeltaTree(ModelWorkspaceItem element, ModelWorkspaceDeltaImpl delta) { ModelWorkspaceDeltaImpl childDelta = delta; ArrayList ancestors= getAncestors(element); if (ancestors == null) { if (this.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 { for (int i = 0, size = ancestors.size(); i < size; i++) { ModelWorkspaceItem ancestor = (ModelWorkspaceItem) ancestors.get(i); ModelWorkspaceDeltaImpl ancestorDelta = new ModelWorkspaceDeltaImpl(ancestor); ancestorDelta.addAffectedChild(childDelta); childDelta = ancestorDelta; } } return 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(ModelWorkspaceItem movedFromElement, ModelWorkspaceItem movedToElement) { ModelWorkspaceDeltaImpl removedDelta = new ModelWorkspaceDeltaImpl(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(ModelWorkspaceItem movedToElement, ModelWorkspaceItem movedFromElement) { ModelWorkspaceDeltaImpl addedDelta = new ModelWorkspaceDeltaImpl(movedToElement); addedDelta.fKind = ADDED; addedDelta.fChangeFlags |= F_MOVED_FROM; addedDelta.fMovedFromHandle = movedFromElement; insertDeltaTree(movedToElement, addedDelta); } /** * 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 getAncestors(ModelWorkspaceItem element) { ModelWorkspaceItem parent = element.getParent(); if (parent == null) { return null; } ArrayList parents = new ArrayList(); while (!parent.equals(fChangedElement)) { parents.add(parent); parent = parent.getParent(); if (parent == null) { return null; } } parents.trimToSize(); return parents; } /** * Mark this delta as a fine-grained delta. */ public void fineGrained() { if (fKind == 0) { // if not set yet fKind = CHANGED; } // fChangeFlags |= F_FINE_GRAINED; } /** * Removes the element from the array. * Returns the a new array which has shrunk. */ protected ModelWorkspaceDelta[] removeAndShrinkArray( final ModelWorkspaceDelta[] old, final int index) { ModelWorkspaceDelta[] array = new ModelWorkspaceDelta[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; } /** * Adds the new element to a new array that contains all of the elements of the old array. * Returns the new array. */ protected ModelWorkspaceDelta[] growAndAddToArray( ModelWorkspaceDelta[] array, final ModelWorkspaceDelta addition) { ModelWorkspaceDelta[] old = array; array = new ModelWorkspaceDelta[old.length + 1]; System.arraycopy(old, 0, array, 0, old.length); array[old.length] = addition; return array; } /** * 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(ModelWorkspaceDeltaImpl 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 compilation unit delta or below, // it's a fine grained delta if (fChangedElement.getItemType() >= ModelWorkspaceItem.MODEL_RESOURCE) { this.fineGrained(); } if (fAffectedChildren.length == 0) { fAffectedChildren = new ModelWorkspaceDelta[] {child}; return; } ModelWorkspaceDelta existingChild = null; int existingChildIndex = -1; if (fAffectedChildren != null) { for (int i = 0; i < fAffectedChildren.length; i++) { if (this.equalsAndSameParent(fAffectedChildren[i].getElement(), child.getElement())) { // handle case of two jars that can be equals but not in the same project 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()) { case ADDED: // child was added then added -> it is added case CHANGED: // child was added then changed -> it is added return; case REMOVED: // child was added then removed -> noop fAffectedChildren = this.removeAndShrinkArray(fAffectedChildren, existingChildIndex); return; } break; case REMOVED: switch (child.getKind()) { case ADDED: // child was removed then added -> it is changed child.fKind = CHANGED; fAffectedChildren[existingChildIndex] = child; return; case CHANGED: // child was removed then changed -> it is removed case REMOVED: // child was removed then removed -> it is removed return; } break; case CHANGED: switch (child.getKind()) { case ADDED: // child was changed then added -> it is added case REMOVED: // child was changed then removed -> it is removed fAffectedChildren[existingChildIndex] = child; return; case CHANGED: // child was changed then changed -> it is changed ModelWorkspaceDelta[] children = child.getAffectedChildren(); for (int i = 0; i < children.length; i++) { ModelWorkspaceDeltaImpl childsChild = (ModelWorkspaceDeltaImpl) children[i]; ((ModelWorkspaceDeltaImpl) existingChild).addAffectedChild(childsChild); } // update flags if needed // switch (((ModelWorkspaceDeltaImpl) existingChild).fChangeFlags) { // case F_ADDED_TO_CLASSPATH: // case F_REMOVED_FROM_CLASSPATH: // case F_SOURCEATTACHED: // case F_SOURCEDETACHED: // ((ModelWorkspaceDeltaImpl) existingChild).fChangeFlags |= ((ModelWorkspaceDeltaImpl) child).fChangeFlags; // break; // } // add the non-java resource deltas if needed // note that the child delta always takes precedence over this existing child delta // as non-java resource deltas are always created last (by the DeltaProcessor) IResourceDelta[] resDeltas = child.getResourceDeltas(); if (resDeltas != null) { ((ModelWorkspaceDeltaImpl)existingChild).resourceDeltas = resDeltas; ((ModelWorkspaceDeltaImpl)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( final ModelWorkspaceItem element) { ModelWorkspaceDeltaImpl addedDelta = new ModelWorkspaceDeltaImpl(element); addedDelta.fKind = ADDED; insertDeltaTree(element, addedDelta); } /** * 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( final ModelWorkspaceItem element, int changeFlag) { ModelWorkspaceDeltaImpl changedDelta = new ModelWorkspaceDeltaImpl(element); changedDelta.fKind = CHANGED; changedDelta.fChangeFlags |= changeFlag; insertDeltaTree(element, changedDelta); } /** * Returns whether the two model workspace items are equals and have the same parent. */ protected boolean equalsAndSameParent( final ModelWorkspaceItem e1, final ModelWorkspaceItem e2) { ModelWorkspaceItem parent1; return e1.equals(e2) && ((parent1 = e1.getParent()) != null) && parent1.equals(e2.getParent()); } /** * Returns the ModelWorkspaceDelta for the given element * in the delta tree, or null, if no delta for the given element is found. */ protected ModelWorkspaceDeltaImpl find( final ModelWorkspaceItem e) { if (this.equalsAndSameParent(fChangedElement, e)) { // handle case of two jars that can be equals but not in the same project return this; } for (int i = 0; i < fAffectedChildren.length; i++) { ModelWorkspaceDeltaImpl delta = ((ModelWorkspaceDeltaImpl)fAffectedChildren[i]).find(e); if (delta != null) { return delta; } } return null; } }