/******************************************************************************* * Copyright (c) 2000, 2005 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 *******************************************************************************/ 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 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; } }