/*******************************************************************************
* 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;
}
}