/*******************************************************************************
* Copyright (c) 2000, 2010 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.localstore;
import org.eclipse.core.internal.resources.*;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
//
/**
* Visits a unified tree, and synchronizes the file system with the resource tree. After the visit
* is complete, the file system will be synchronized with the workspace tree with respect to
* resource existence, gender, and timestamp.
*
* @author Stas Negara - Changed methods createResource, deleteResource, and resourceChanged to send
* the appropriate notifications.
*/
public class RefreshLocalVisitor implements IUnifiedTreeVisitor, ILocalStoreConstants {
/** control constants */
protected static final int RL_UNKNOWN= 0;
protected static final int RL_IN_SYNC= 1;
protected static final int RL_NOT_IN_SYNC= 2;
/*
* Fields for progress monitoring algorithm.
* Initially, give progress for every 4 resources, double
* this value at halfway point, then reset halfway point
* to be half of remaining work. (this gives an infinite
* series that converges at total work after an infinite
* number of resources).
*/
public static final int TOTAL_WORK= 250;
private int currentIncrement= 4;
private int halfWay= TOTAL_WORK / 2;
private int nextProgress= currentIncrement;
private int worked= 0;
protected MultiStatus errors;
protected IProgressMonitor monitor;
protected boolean resourceChanged;
protected Workspace workspace;
public RefreshLocalVisitor(IProgressMonitor monitor) {
this.monitor= monitor;
workspace= (Workspace)ResourcesPlugin.getWorkspace();
resourceChanged= false;
String msg= Messages.resources_errorMultiRefresh;
errors= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_LOCAL, msg, null);
}
/**
* This method has the same implementation as resourceChanged but as they are different cases,
* we prefer to use different methods.
*/
protected void contentAdded(UnifiedTreeNode node, Resource target) {
resourceChanged(node, target);
}
protected void createResource(UnifiedTreeNode node, Resource target) throws CoreException {
ResourceInfo info= target.getResourceInfo(false, false);
int flags= target.getFlags(info);
if (target.exists(flags, false))
return;
/* make sure target's parent exists */
if (node.getLevel() == 0) {
IContainer parent= target.getParent();
if (parent.getType() == IResource.FOLDER)
((Folder)target.getParent()).ensureExists(monitor);
}
/* Use the basic file creation protocol since we don't want to create any content on disk. */
info= workspace.createResource(target, false);
/* Mark this resource as having unknown children */
info.set(ICoreConstants.M_CHILDREN_UNKNOWN);
target.getLocalManager().updateLocalSync(info, node.getLastModified());
//CODINGSPECTATOR
Resource.resourceListener.externallyCreatedResource(target);
}
protected void deleteResource(UnifiedTreeNode node, Resource target) throws CoreException {
ResourceInfo info= target.getResourceInfo(false, false);
int flags= target.getFlags(info);
//don't delete linked resources
if (ResourceInfo.isSet(flags, ICoreConstants.M_LINK)) {
//just clear local sync info
info= target.getResourceInfo(false, true);
//handle concurrent deletion
if (info != null) {
info.clearModificationStamp();
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}
return;
}
if (target.exists(flags, false))
target.deleteResource(true, errors);
node.setExistsWorkspace(false);
//CODINGSPECTATOR
Resource.resourceListener.externallyModifiedResource(target, true);
}
protected void fileToFolder(UnifiedTreeNode node, Resource target) throws CoreException {
ResourceInfo info= target.getResourceInfo(false, false);
int flags= target.getFlags(info);
if (target.exists(flags, true)) {
target= (Folder)((File)target).changeToFolder();
} else {
if (!target.exists(flags, false)) {
target= (Resource)workspace.getRoot().getFolder(target.getFullPath());
// Use the basic file creation protocol since we don't want to create any content on disk.
workspace.createResource(target, false);
}
}
node.setResource(target);
info= target.getResourceInfo(false, true);
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}
protected void folderToFile(UnifiedTreeNode node, Resource target) throws CoreException {
ResourceInfo info= target.getResourceInfo(false, false);
int flags= target.getFlags(info);
if (target.exists(flags, true))
target= (File)((Folder)target).changeToFile();
else {
if (!target.exists(flags, false)) {
target= (Resource)workspace.getRoot().getFile(target.getFullPath());
// Use the basic file creation protocol since we don't want to
// create any content on disk.
workspace.createResource(target, false);
}
}
node.setResource(target);
info= target.getResourceInfo(false, true);
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}
/**
* Returns the status of the nodes visited so far. This will be a multi-status that describes
* all problems that have occurred, or an OK status if everything went smoothly.
*/
public IStatus getErrorStatus() {
return errors;
}
protected void makeLocal(UnifiedTreeNode node, Resource target) {
ResourceInfo info= target.getResourceInfo(false, true);
if (info != null)
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}
/**
* Refreshes the parent of a resource currently being synchronized.
*/
protected void refresh(Container parent) throws CoreException {
parent.getLocalManager().refresh(parent, IResource.DEPTH_ZERO, false, null);
}
protected void resourceChanged(UnifiedTreeNode node, Resource target) {
ResourceInfo info= target.getResourceInfo(false, true);
if (info == null)
return;
target.getLocalManager().updateLocalSync(info, node.getLastModified());
info.incrementContentId();
// forget content-related caching flags
info.clear(ICoreConstants.M_CONTENT_CACHE);
workspace.updateModificationStamp(info);
//CODINGSPECTATOR
Resource.resourceListener.externallyModifiedResource(target, false);
}
public boolean resourcesChanged() {
return resourceChanged;
}
/**
* deletion or creation -- Returns: - RL_IN_SYNC - the resource is in-sync with the file system
* - RL_NOT_IN_SYNC - the resource is not in-sync with file system - RL_UNKNOWN - couldn't
* determine the sync status for this resource
*/
protected int synchronizeExistence(UnifiedTreeNode node, Resource target) throws CoreException {
if (node.existsInWorkspace()) {
if (!node.existsInFileSystem()) {
// 1. non-local files are always in sync
// 2. links to non-existent locations with the modification stamp of IResource.NULL_STAMP are in sync
if (target.isLocal(IResource.DEPTH_ZERO) && target.getModificationStamp() != IResource.NULL_STAMP) {
deleteResource(node, target);
resourceChanged= true;
return RL_NOT_IN_SYNC;
}
return RL_IN_SYNC;
}
} else {
// do we have a gender variant in the workspace?
IResource genderVariant= workspace.getRoot().findMember(target.getFullPath());
if (genderVariant != null)
return RL_UNKNOWN;
if (node.existsInFileSystem()) {
Container parent= (Container)target.getParent();
if (!parent.exists()) {
refresh(parent);
if (!parent.exists())
return RL_NOT_IN_SYNC;
}
if (!target.getName().equals(node.getLocalName()))
return RL_IN_SYNC;
if (!Workspace.caseSensitive && node.getLevel() == 0) {
// do we have any alphabetic variants in the workspace?
IResource variant= target.findExistingResourceVariant(target.getFullPath());
if (variant != null) {
deleteResource(node, ((Resource)variant));
createResource(node, target);
resourceChanged= true;
return RL_NOT_IN_SYNC;
}
}
createResource(node, target);
resourceChanged= true;
return RL_NOT_IN_SYNC;
}
}
return RL_UNKNOWN;
}
/**
* gender change -- Returns true if gender was in sync.
*/
protected boolean synchronizeGender(UnifiedTreeNode node, Resource target) throws CoreException {
if (!node.existsInWorkspace()) {
//may be an existing resource in the workspace of different gender
IResource genderVariant= workspace.getRoot().findMember(target.getFullPath());
if (genderVariant != null)
target= (Resource)genderVariant;
}
if (target.getType() == IResource.FILE) {
if (node.isFolder()) {
fileToFolder(node, target);
resourceChanged= true;
return false;
}
} else {
if (!node.isFolder()) {
folderToFile(node, target);
resourceChanged= true;
return false;
}
}
return true;
}
/**
* lastModified
*/
protected void synchronizeLastModified(UnifiedTreeNode node, Resource target) {
if (target.isLocal(IResource.DEPTH_ZERO))
resourceChanged(node, target);
else
contentAdded(node, target);
resourceChanged= true;
}
public boolean visit(UnifiedTreeNode node) throws CoreException {
Policy.checkCanceled(monitor);
try {
Resource target= (Resource)node.getResource();
int targetType= target.getType();
if (targetType == IResource.PROJECT)
return true;
if (node.existsInWorkspace() && node.existsInFileSystem()) {
/* for folders we only care about updating local status */
if (targetType == IResource.FOLDER && node.isFolder()) {
// if not local, mark as local
if (!target.isLocal(IResource.DEPTH_ZERO))
makeLocal(node, target);
ResourceInfo info= target.getResourceInfo(false, false);
if (info != null && info.getModificationStamp() != IResource.NULL_STAMP)
return true;
}
/* compare file last modified */
if (targetType == IResource.FILE && !node.isFolder()) {
ResourceInfo info= target.getResourceInfo(false, false);
if (info != null && info.getModificationStamp() != IResource.NULL_STAMP && info.getLocalSyncInfo() == node.getLastModified())
return true;
}
} else {
if (node.existsInFileSystem() && !Path.EMPTY.isValidSegment(node.getLocalName())) {
String message= NLS.bind(Messages.resources_invalidResourceName, node.getLocalName());
errors.merge(new ResourceStatus(IResourceStatus.INVALID_RESOURCE_NAME, message));
return false;
}
int state= synchronizeExistence(node, target);
if (state == RL_IN_SYNC || state == RL_NOT_IN_SYNC) {
if (targetType == IResource.FILE) {
try {
((File)target).updateMetadataFiles();
} catch (CoreException e) {
errors.merge(e.getStatus());
}
}
return true;
}
}
if (synchronizeGender(node, target))
synchronizeLastModified(node, target);
if (targetType == IResource.FILE) {
try {
((File)target).updateMetadataFiles();
} catch (CoreException e) {
errors.merge(e.getStatus());
}
}
return true;
} finally {
if (--nextProgress <= 0) {
//we have exhausted the current increment, so report progress
monitor.worked(1);
worked++;
if (worked >= halfWay) {
//we have passed the current halfway point, so double the
//increment and reset the halfway point.
currentIncrement*= 2;
halfWay+= (TOTAL_WORK - halfWay) / 2;
}
//reset the progress counter to another full increment
nextProgress= currentIncrement;
}
}
}
}