package org.erlide.engine.internal.model.root; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IResourceDelta; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.IParent; import org.erlide.engine.model.root.IErlElementDelta; import com.google.common.collect.Lists; public class ErlElementDelta implements IErlElementDelta { private int fKind; private int fFlags; private final IErlElement fElement; private final List<ErlElementDelta> fChildren; private List<IResourceDelta> fResourceDeltas; /** * @see #getMovedFromElement() */ protected IErlElement fMovedFromElement = null; /** * @see #getMovedToElement() */ protected IErlElement fMovedToElement = null; /** * @param kind * @param flags * @param element */ public ErlElementDelta(final int kind, final int flags, final IErlElement element) { this(kind, flags, element, new ArrayList<ErlElementDelta>(0), new ArrayList<IResourceDelta>(0)); } /** * @param kind * @param flags * @param element * @param children */ public ErlElementDelta(final int kind, final int flags, final IErlElement element, final List<ErlElementDelta> children) { this(kind, flags, element, children, new ArrayList<IResourceDelta>(0)); } /** * @param kind * @param flags * @param element * @param children * @param resourceDeltas */ public ErlElementDelta(final int kind, final int flags, final IErlElement element, final List<ErlElementDelta> children, final List<IResourceDelta> resourceDeltas) { super(); fKind = kind; fFlags = flags; fElement = element; fChildren = children; fResourceDeltas = resourceDeltas; } @Override public IErlElementDelta[] getChildren(final int kind) { final ArrayList<IErlElementDelta> children = new ArrayList<>(0); for (int i = 0; i < fChildren.size(); ++i) { final IErlElementDelta c = fChildren.get(i); if (c.getKind() == kind || kind == ALL) { children.add(c); } } return children.toArray(new IErlElementDelta[children.size()]); } @Override public IErlElement getElement() { return fElement; } @Override public int getFlags() { return fFlags; } @Override public int getKind() { return fKind; } @Override public IResourceDelta[] getResourceDeltas() { return fResourceDeltas.toArray(new IResourceDelta[fResourceDeltas.size()]); } /** * Creates the delta tree for the given element and delta, and then inserts * the tree as an affected child of this node. */ public void insertDeltaTree(final IErlElement element, final ErlElementDelta delta) { final ErlElementDelta childDelta = createDeltaTree(element, delta); if (!equalsAndSameParent(element, getElement())) { 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 ErlElementDelta createDeltaTree(final IErlElement element, final ErlElementDelta delta) { ErlElementDelta childDelta = delta; final List<IParent> 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; fFlags = delta.fFlags; fMovedToElement = delta.fMovedToElement; fMovedFromElement = delta.fMovedFromElement; } else { // the given delta is not the root or a child - illegal // Assert.isTrue(false); } } else { for (final IParent ancestor : ancestors) { final IErlElement element2 = (IErlElement) ancestor; final ErlElementDelta ancestorDelta = new ErlElementDelta(0, 0, element2); ancestorDelta.addAffectedChild(childDelta); childDelta = ancestorDelta; } } return childDelta; } @Override public IErlElement getMovedFromElement() { return fMovedFromElement; } @Override public IErlElement getMovedToElement() { return fMovedToElement; } /** * Adds the child delta to the collection of affected children. If the child * is already in the collection, walk down the hierarchy. * * JC: this sucks a little bit, too much code, as always... (from C model, * not my fault) */ protected void addAffectedChild(final ErlElementDelta child) { switch (fKind) { case ADDED: case REMOVED: // no need to add a child if this parent is added or removed return; case CHANGED: fFlags |= F_CHILDREN; break; default: fKind = CHANGED; fFlags |= F_CHILDREN; break; } // Check if we already have the delta. IErlElementDelta existingChild = null; int existingChildIndex = -1; for (int i = 0; i < fChildren.size(); i++) { // handle case of two jars that can be equals but not in the same // project if (equalsAndSameParent(fChildren.get(i).getElement(), child.getElement())) { existingChild = fChildren.get(i); existingChildIndex = i; break; } } if (existingChild == null) { // new affected child fChildren.add(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: fChildren.remove(existingChildIndex); return; } break; case REMOVED: switch (child.getKind()) { // child was removed then added -> it is changed case ADDED: child.fKind = CHANGED; fChildren.set(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: fChildren.set(existingChildIndex, child); return; // child was changed then changed -> it is changed case CHANGED: final IErlElementDelta[] children = child.getChildren(ALL); for (final IErlElementDelta element : children) { final ErlElementDelta childsChild = (ErlElementDelta) element; ((ErlElementDelta) existingChild).addAffectedChild(childsChild); } // add the non-erlang resource deltas if needed // note that the child delta always takes // precedence over this existing child delta // as non-erlang resource deltas are always // created last (by the DeltaProcessor) ((ErlElementDelta) existingChild).fResourceDeltas = child.fResourceDeltas; return; } break; default: // unknown -> existing child becomes the child with the existing // child's // flags final int flags = existingChild.getFlags(); fChildren.set(existingChildIndex, child); child.fFlags |= flags; } } } private List<IParent> getAncestors(final IErlElement element0) { IErlElement element = element0; IParent parent = element.getParent(); if (parent == null) { return null; } final ArrayList<IParent> parents = Lists.newArrayList(); while (!parent.equals(fElement)) { parents.add(parent); if (parent instanceof IErlElement) { element = (IErlElement) parent; parent = element.getParent(); } else { break; } if (parent == null) { break; } } parents.trimToSize(); return parents; } /** * Returns whether the two elements are equals and have the same parent. */ protected boolean equalsAndSameParent(final IErlElement e1, final IErlElement e2) { final IErlElement parent1 = (IErlElement) e1.getParent(); return e1.equals(e2) && parent1 != null && parent1.equals(e2.getParent()); } /** * Adds the child delta to the collection of affected children. If the child * is already in the collection, walk down the hierarchy. */ public void addResourceDelta(final IResourceDelta child) { switch (fKind) { case ADDED: case REMOVED: // no need to add a child if this parent is added or removed return; case CHANGED: fFlags |= F_CONTENT; break; default: fKind = CHANGED; fFlags |= F_CONTENT; } fResourceDeltas.add(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(final IErlElement element, final int flag) { final ErlElementDelta changedDelta = new ErlElementDelta(0, 0, element); changedDelta.fKind = CHANGED; changedDelta.fFlags |= flag; insertDeltaTree(element, changedDelta); } @Override public IErlElementDelta findElement(final IErlElement element) { if (fElement.equals(element)) { return this; } for (int i = 0; i < fChildren.size(); ++i) { final IErlElementDelta d = fChildren.get(i).findElement(element); if (d != null) { return d; } } return null; } }