/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation 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: * IBM Corporation - initial API and implementation * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.events; import java.util.Map; import org.eclipse.core.internal.dtree.DeltaDataTree; import org.eclipse.core.internal.dtree.NodeComparison; import org.eclipse.core.internal.resources.*; import org.eclipse.core.internal.watson.ElementTree; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * This class is used for calculating and building resource delta trees for notification * and build purposes. */ public class ResourceDeltaFactory { /** * Singleton indicating no delta children */ protected static final ResourceDelta[] NO_CHILDREN = new ResourceDelta[0]; /** * Returns the resource delta representing the changes made between the given old and new trees, * starting from the given root element. * @param markerGeneration the start generation for which deltas should be computed, or -1 * if marker deltas should not be provided. */ public static ResourceDelta computeDelta(Workspace workspace, ElementTree oldTree, ElementTree newTree, IPath root, long markerGeneration) { //compute the underlying delta tree. ResourceComparator comparator = markerGeneration >= 0 ? ResourceComparator.getNotificationComparator() : ResourceComparator.getBuildComparator(); newTree.immutable(); DeltaDataTree delta = null; if (Path.ROOT.equals(root)) delta = newTree.getDataTree().compareWith(oldTree.getDataTree(), comparator); else delta = newTree.getDataTree().compareWith(oldTree.getDataTree(), comparator, root); delta = delta.asReverseComparisonTree(comparator); IPath pathInTree = root.isRoot() ? Path.ROOT : root; IPath pathInDelta = Path.ROOT; // get the marker deltas for the delta info object....if needed Map<IPath, MarkerSet> allMarkerDeltas = null; if (markerGeneration >= 0) allMarkerDeltas = workspace.getMarkerManager().getMarkerDeltas(markerGeneration); //recursively walk the delta and create a tree of ResourceDelta objects. ResourceDeltaInfo deltaInfo = new ResourceDeltaInfo(workspace, allMarkerDeltas, comparator); ResourceDelta result = createDelta(workspace, delta, deltaInfo, pathInTree, pathInDelta); //compute node ID map and fix up moves deltaInfo.setNodeIDMap(computeNodeIDMap(result, new NodeIDMap())); result.fixMovesAndMarkers(oldTree); // check all the projects and if they were added and opened then tweek the flags // so the delta reports both. int segmentCount = result.getFullPath().segmentCount(); if (segmentCount <= 1) checkForOpen(result, segmentCount); return result; } /** * Checks to see if added projects were also opens and tweaks the flags * accordingly. Should only be called for root and projects. Pass the segment count * in since we've already calculated it before. */ protected static void checkForOpen(ResourceDelta delta, int segmentCount) { if (delta.getKind() == IResourceDelta.ADDED) if (delta.newInfo.isSet(ICoreConstants.M_OPEN)) delta.status |= IResourceDelta.OPEN; // return for PROJECT if (segmentCount == 1) return; // recurse for ROOT IResourceDelta[] children = delta.children; for (int i = 0; i < children.length; i++) checkForOpen((ResourceDelta) children[i], 1); } /** * Creates the map from node id to element id for the old and new states. * Used for recognizing moves. Returns the map. */ protected static NodeIDMap computeNodeIDMap(ResourceDelta delta, NodeIDMap nodeIDMap) { IResourceDelta[] children = delta.children; for (int i = 0; i < children.length; i++) { ResourceDelta child = (ResourceDelta) children[i]; IPath path = child.getFullPath(); switch (child.getKind()) { case IResourceDelta.ADDED : nodeIDMap.putNewPath(child.newInfo.getNodeId(), path); break; case IResourceDelta.REMOVED : nodeIDMap.putOldPath(child.oldInfo.getNodeId(), path); break; case IResourceDelta.CHANGED : long oldID = child.oldInfo.getNodeId(); long newID = child.newInfo.getNodeId(); //don't add entries to the map if nothing has changed. if (oldID != newID) { nodeIDMap.putOldPath(oldID, path); nodeIDMap.putNewPath(newID, path); } break; } //recurse computeNodeIDMap(child, nodeIDMap); } return nodeIDMap; } /** * Recursively creates the tree of ResourceDelta objects rooted at * the given path. */ protected static ResourceDelta createDelta(Workspace workspace, DeltaDataTree delta, ResourceDeltaInfo deltaInfo, IPath pathInTree, IPath pathInDelta) { // create the delta and fill it with information ResourceDelta result = new ResourceDelta(pathInTree, deltaInfo); // fill the result with information NodeComparison compare = (NodeComparison) delta.getData(pathInDelta); int comparison = compare.getUserComparison(); result.setStatus(comparison); if (comparison == IResourceDelta.NO_CHANGE || Path.ROOT.equals(pathInTree)) { ResourceInfo info = workspace.getResourceInfo(pathInTree, true, false); result.setOldInfo(info); result.setNewInfo(info); } else { result.setOldInfo((ResourceInfo) compare.getOldData()); result.setNewInfo((ResourceInfo) compare.getNewData()); } // recurse over the children IPath[] childKeys = delta.getChildren(pathInDelta); int numChildren = childKeys.length; if (numChildren == 0) { result.setChildren(NO_CHILDREN); } else { ResourceDelta[] children = new ResourceDelta[numChildren]; for (int i = 0; i < numChildren; i++) { //reuse the delta path if tree-relative and delta-relative are the same IPath newTreePath = pathInTree == pathInDelta ? childKeys[i] : pathInTree.append(childKeys[i].lastSegment()); children[i] = createDelta(workspace, delta, deltaInfo, newTreePath, childKeys[i]); } result.setChildren(children); } // if this delta has children but no other changes, mark it as changed int status = result.status; if ((status & IResourceDelta.ALL_WITH_PHANTOMS) == 0 && numChildren != 0) result.setStatus(status |= IResourceDelta.CHANGED); // return the delta return result; } /** * Returns an empty build delta describing the fact that no * changes occurred in the given project. The returned delta * is not appropriate for use as a notification delta because * it is rooted at a project, and does not contain marker deltas. */ public static IResourceDelta newEmptyDelta(IProject project) { ResourceDelta result = new ResourceDelta(project.getFullPath(), new ResourceDeltaInfo(((Workspace) project.getWorkspace()), null, ResourceComparator.getBuildComparator())); result.setStatus(0); result.setChildren(NO_CHILDREN); ResourceInfo info = ((Project) project).getResourceInfo(true, false); result.setOldInfo(info); result.setNewInfo(info); return result; } }