| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.internal.events; |
| |
| import org.eclipse.core.internal.resources.ICoreConstants; |
| import org.eclipse.core.internal.resources.ResourceInfo; |
| import org.eclipse.core.internal.watson.IElementComparator; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceDelta; |
| |
| /** |
| * Compares two Resources and returns flags describing how |
| * they have changed, for use in computing deltas. |
| * Implementation note: rather than defining a partial order |
| * as specified by IComparator, the compare operation returns |
| * a set of flags instead. The delta computation only cares |
| * whether the comparison is zero (equal) or non-zero (not equal). |
| */ |
| public class ResourceComparator implements IElementComparator, ICoreConstants { |
| /* Singleton instances */ |
| protected static final ResourceComparator notificationSingleton = new ResourceComparator(true, false); |
| protected static final ResourceComparator buildSingleton = new ResourceComparator(false, false); |
| |
| /** |
| * Boolean indicating whether or not this comparator is to be used for |
| * a notification. (as opposed to a build) Notifications include extra information |
| * like marker and sync info changes. |
| */ |
| private boolean notification; |
| |
| /** |
| * Boolean indicating whether or not this comparator is to be used for |
| * snapshot. Snapshots care about extra information such as the used bit. |
| */ |
| private boolean save; |
| |
| /** |
| * Returns a comparator which compares resource infos, suitable for computing |
| * save and snapshot deltas. |
| */ |
| public static ResourceComparator getSaveComparator() { |
| return new ResourceComparator(false, true); |
| } |
| |
| /** |
| * Returns a comparator which compares resource infos, suitable for computing |
| * build deltas. |
| */ |
| public static ResourceComparator getBuildComparator() { |
| return buildSingleton; |
| } |
| |
| /** |
| * Returns a comparator which compares resource infos, suitable for computing |
| * build deltas. |
| */ |
| public static ResourceComparator getNotificationComparator() { |
| return notificationSingleton; |
| } |
| |
| /** |
| * Create a comparator which compares resource infos. |
| * @param notification if true, check for marker deltas. |
| * @param save if true, check for all resource changes that snapshot needs |
| */ |
| private ResourceComparator(boolean notification, boolean save) { |
| this.notification = notification; |
| this.save = save; |
| } |
| |
| /** |
| * Compare the ElementInfos for two resources. |
| */ |
| @Override |
| public int compare(Object o1, Object o2) { |
| // == handles null, null. |
| if (o1 == o2) |
| return IResourceDelta.NO_CHANGE; |
| int result = 0; |
| if (o1 == null) |
| return ((ResourceInfo) o2).isSet(M_PHANTOM) ? IResourceDelta.ADDED_PHANTOM : IResourceDelta.ADDED; |
| if (o2 == null) |
| return ((ResourceInfo) o1).isSet(M_PHANTOM) ? IResourceDelta.REMOVED_PHANTOM : IResourceDelta.REMOVED; |
| if (!(o1 instanceof ResourceInfo && o2 instanceof ResourceInfo)) |
| return IResourceDelta.NO_CHANGE; |
| ResourceInfo oldElement = (ResourceInfo) o1; |
| ResourceInfo newElement = (ResourceInfo) o2; |
| if (!oldElement.isSet(M_PHANTOM) && newElement.isSet(M_PHANTOM)) |
| return IResourceDelta.REMOVED; |
| if (oldElement.isSet(M_PHANTOM) && !newElement.isSet(M_PHANTOM)) |
| return IResourceDelta.ADDED; |
| if (!compareOpen(oldElement, newElement)) |
| result |= IResourceDelta.OPEN; |
| if (!compareContents(oldElement, newElement)) { |
| if (oldElement.getType() == IResource.PROJECT) |
| result |= IResourceDelta.DESCRIPTION; |
| else if (newElement.getType() == IResource.FILE || oldElement.getType() == IResource.FILE) |
| result |= IResourceDelta.CONTENT; |
| } |
| if (!compareType(oldElement, newElement)) |
| result |= IResourceDelta.TYPE; |
| if (!compareNodeIDs(oldElement, newElement)) { |
| result |= IResourceDelta.REPLACED; |
| // if the node was replaced and the old and new were files, this is also a content change. |
| if (oldElement.getType() == IResource.FILE && newElement.getType() == IResource.FILE) |
| result |= IResourceDelta.CONTENT; |
| } |
| if (compareLocal(oldElement, newElement)) |
| result |= IResourceDelta.LOCAL_CHANGED; |
| if (!compareCharsets(oldElement, newElement)) |
| result |= IResourceDelta.ENCODING; |
| if (!compareDerived(oldElement, newElement)) |
| result |= IResourceDelta.DERIVED_CHANGED; |
| if (notification && !compareSync(oldElement, newElement)) |
| result |= IResourceDelta.SYNC; |
| if (notification && !compareMarkers(oldElement, newElement)) |
| result |= IResourceDelta.MARKERS; |
| if (save && !compareUsed(oldElement, newElement)) |
| result |= IResourceDelta.CHANGED; |
| return result == 0 ? 0 : result | IResourceDelta.CHANGED; |
| } |
| |
| private boolean compareDerived(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.isSet(ICoreConstants.M_DERIVED) == newElement.isSet(ICoreConstants.M_DERIVED); |
| } |
| |
| private boolean compareCharsets(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.getCharsetGenerationCount() == newElement.getCharsetGenerationCount(); |
| } |
| |
| /** |
| * Compares the contents of the ResourceInfo. |
| */ |
| private boolean compareContents(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.getContentId() == newElement.getContentId(); |
| } |
| |
| /** |
| * Compares the existence of local files/folders for two linked resources. |
| */ |
| private boolean compareLocal(ResourceInfo oldElement, ResourceInfo newElement) { |
| //only applicable for linked resources |
| if (!oldElement.isSet(ICoreConstants.M_LINK) || !newElement.isSet(ICoreConstants.M_LINK)) |
| return false; |
| long oldStamp = oldElement.getModificationStamp(); |
| long newStamp = newElement.getModificationStamp(); |
| return (oldStamp == -1 || newStamp == -1) && (oldStamp != newStamp); |
| } |
| |
| private boolean compareMarkers(ResourceInfo oldElement, ResourceInfo newElement) { |
| // If both sets of markers are null then perhaps we added some markers |
| // but then deleted them right away before notification. In that case |
| // don't signify a marker change in the delta. |
| boolean bothNull = oldElement.getMarkers(false) == null && newElement.getMarkers(false) == null; |
| return bothNull || oldElement.getMarkerGenerationCount() == newElement.getMarkerGenerationCount(); |
| } |
| |
| /** |
| * Compares the node IDs of the ElementInfos for two resources. |
| */ |
| private boolean compareNodeIDs(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.getNodeId() == newElement.getNodeId(); |
| } |
| |
| /** |
| * Compares the open state of the ElementInfos for two resources. |
| */ |
| private boolean compareOpen(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.isSet(M_OPEN) == newElement.isSet(M_OPEN); |
| } |
| |
| /** |
| * Compares the sync state for two resources. |
| */ |
| private boolean compareSync(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.getSyncInfoGenerationCount() == newElement.getSyncInfoGenerationCount(); |
| } |
| |
| /** |
| * Compares the type of the ResourceInfo. |
| */ |
| private boolean compareType(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.getType() == newElement.getType(); |
| } |
| |
| /** |
| * Compares the used state of the ElementInfos for two resources. |
| */ |
| private boolean compareUsed(ResourceInfo oldElement, ResourceInfo newElement) { |
| return oldElement.isSet(M_USED) == newElement.isSet(M_USED); |
| } |
| } |