/******************************************************************************* * Copyright (c) 2000, 2008 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.team.internal.ccvs.ui; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.HashSet; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.team.FileModificationValidationContext; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.team.core.TeamException; import org.eclipse.team.core.synchronize.SyncInfo; import org.eclipse.team.internal.ccvs.core.*; import org.eclipse.team.internal.ccvs.core.client.Command; import org.eclipse.team.internal.ccvs.ui.actions.EditorsAction; import org.eclipse.team.internal.ccvs.ui.operations.UpdateOperation; import org.eclipse.team.internal.ui.Utils; import org.eclipse.ui.progress.IProgressConstants; /** * IFileModificationValidator that is plugged into the CVS Repository Provider */ public class FileModificationValidator extends CVSCoreFileModificationValidator { public FileModificationValidator() { } /* (non-Javadoc) * @see org.eclipse.team.internal.ccvs.core.CVSCoreFileModificationValidator#edit(org.eclipse.core.resources.IFile[], org.eclipse.core.resources.team.FileModificationValidationContext) */ protected IStatus edit(IFile[] readOnlyFiles, FileModificationValidationContext context) { return edit(readOnlyFiles, getShell(context)); } private Shell getShell(FileModificationValidationContext context) { if (context == null) return null; if (context.getShell() != null) return (Shell)context.getShell(); return Utils.getShell(null, true); } private IStatus getStatus(InvocationTargetException e) { Throwable target = e.getTargetException(); if (target instanceof TeamException) { return ((TeamException) target).getStatus(); } else if (target instanceof CoreException) { return ((CoreException) target).getStatus(); } return new Status(IStatus.ERROR, CVSUIPlugin.ID, 0, CVSUIMessages.internal, target); } private IStatus edit(final IFile[] files, final Shell shell) { if (isPerformEdit()) { try { if (shell != null && !promptToEditFiles(files, shell)) { // The user didn't want to edit. // OK is returned but the file remains read-only throw new InterruptedException(); } // see if the file is up to date if (shell != null && promptToUpdateFiles(files, shell)) { // The user wants to update the file // Run the update in a runnable in order to get a busy cursor. // This runnable is syncExeced in order to get a busy cursor IRunnableWithProgress updateRunnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { performUpdate(files, monitor); } }; if (isRunningInUIThread()) { // Only show a busy cursor if validate edit is blocking the UI CVSUIPlugin.runWithProgress(shell, false, updateRunnable); } else { // We can't show a busy cursor (i.e., run in the UI thread) // since this thread may hold locks and // running an edit in the UI thread could try to obtain the // same locks, resulting in a deadlock. updateRunnable.run(new NullProgressMonitor()); } } // Run the edit in a runnable in order to get a busy cursor. // This runnable is syncExeced in order to get a busy cursor IRunnableWithProgress editRunnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { try { performEdit(files, monitor); } catch (CVSException e) { throw new InvocationTargetException(e); } } }; if (isRunningInUIThread()) { // Only show a busy cursor if validate edit is blocking the UI CVSUIPlugin.runWithProgress(shell, false, editRunnable); } else { // We can't show a busy cursor (i.e., run in the UI thread) // since this thread may hold locks and // running an edit in the UI thread could try to obtain the // same locks, resulting in a deadlock. editRunnable.run(new NullProgressMonitor()); } } catch (InvocationTargetException e) { return getStatus(e); } catch (InterruptedException e) { // Must return an error to indicate that it is not OK to edit the files return new Status(IStatus.CANCEL, CVSUIPlugin.ID, 0, CVSUIMessages.FileModificationValidator_vetoMessage, null); //; } } else if (isPerformEditInBackground()) { IStatus status = setWritable(files); if (status.isOK()) performEdit(files); return status; } else { // Allow the files to be edited without notifying the server return setWritable(files); } return Status.OK_STATUS; } protected void scheduleEditJob(Job job) { job.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE); job.setProperty(IProgressConstants.ICON_PROPERTY, getOperationIcon()); super.scheduleEditJob(job); } private URL getOperationIcon() { return FileLocator.find(CVSUIPlugin.getPlugin().getBundle(), new Path(ICVSUIConstants.ICON_PATH + ICVSUIConstants.IMG_CVS_PERSPECTIVE), null); } private boolean isRunningInUIThread() { return Display.getCurrent() != null; } private boolean promptToEditFiles(IFile[] files, Shell shell) throws InvocationTargetException, InterruptedException { if (files.length == 0) return true; if(isNeverPrompt()) return true; // Contact the server to see if anyone else is editing the files EditorsAction editors = fetchEditors(files, shell); if (editors.isEmpty()) { if (isAlwaysPrompt()) return (promptEdit(shell)); return true; } else { return (editors.promptToEdit(shell)); } } private boolean promptToUpdateFiles(IFile[] files, Shell shell) throws InvocationTargetException, InterruptedException { if (files.length == 0) return false; if (isNeverUpdate()) return false; // Contact the server to see if the files are up-to-date if (needsUpdate(files, new NullProgressMonitor())) { if (isPromptUpdate()) return (promptUpdate(shell)); return true; // auto update } return false; } private boolean promptEdit(Shell shell) { // Open the dialog using a sync exec (there are no guarantees that we // were called from the UI thread final boolean[] result = new boolean[] { false }; int flags = isRunningInUIThread() ? 0 : CVSUIPlugin.PERFORM_SYNC_EXEC; CVSUIPlugin.openDialog(shell, new CVSUIPlugin.IOpenableInShell() { public void open(Shell shell) { result[0] = MessageDialog.openQuestion(shell,CVSUIMessages.FileModificationValidator_3,CVSUIMessages.FileModificationValidator_4); // } }, flags); return result[0]; } private boolean promptUpdate(Shell shell) { // Open the dialog using a sync exec (there are no guarantees that we // were called from the UI thread final boolean[] result = new boolean[] { false }; int flags = isRunningInUIThread() ? 0 : CVSUIPlugin.PERFORM_SYNC_EXEC; CVSUIPlugin.openDialog(shell, new CVSUIPlugin.IOpenableInShell() { public void open(Shell shell) { result[0] = MessageDialog.openQuestion(shell,CVSUIMessages.FileModificationValidator_5,CVSUIMessages.FileModificationValidator_6); } }, flags); return result[0]; } private boolean isPerformEdit() { return ICVSUIConstants.PREF_EDIT_PROMPT_EDIT.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_EDIT_ACTION)); } private boolean isPerformEditInBackground() { return ICVSUIConstants.PREF_EDIT_IN_BACKGROUND.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_EDIT_ACTION)); } private EditorsAction fetchEditors(IFile[] files, Shell shell) throws InvocationTargetException, InterruptedException { final EditorsAction editors = new EditorsAction(getProvider(files), files); IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { editors.run(monitor); } }; if (isRunningInUIThread()) { // Show a busy cursor if we are running in the UI thread CVSUIPlugin.runWithProgress(shell, false, runnable); } else { // We can't show a busy cursor (i.e., run in the UI thread) // since this thread may hold locks and // running a CVS operation in the UI thread could try to obtain the // same locks, resulting in a deadlock. runnable.run(new NullProgressMonitor()); } return editors; } private boolean isNeverPrompt() { return ICVSUIConstants.PREF_EDIT_PROMPT_NEVER.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_EDIT_PROMPT)); } private boolean isAlwaysPrompt() { return ICVSUIConstants.PREF_EDIT_PROMPT_ALWAYS.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_EDIT_PROMPT)); } private boolean needsUpdate(IFile[] files, IProgressMonitor monitor) { try { CVSWorkspaceSubscriber subscriber = CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber(); subscriber.refresh(files, IResource.DEPTH_ZERO, monitor); for (int i = 0; i < files.length; i++) { IFile file = files[i]; SyncInfo info = subscriber.getSyncInfo(file); int direction = info.getKind() & SyncInfo.DIRECTION_MASK; if (direction == SyncInfo.CONFLICTING || direction == SyncInfo.INCOMING) { return true; } } } catch (TeamException e) { // Log the exception and assume we don't need to update it CVSProviderPlugin.log(e); } return false; } private void performUpdate(IFile[] files, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { // TODO: This obtains the project rule which can cause a rule violation new UpdateOperation(null /* no target part */, files, Command.NO_LOCAL_OPTIONS, null /* no tag */).run(monitor); } private boolean isPromptUpdate() { return ICVSUIConstants.PREF_UPDATE_PROMPT_IF_OUTDATED.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_UPDATE_PROMPT)); } private boolean isNeverUpdate() { return ICVSUIConstants.PREF_UPDATE_PROMPT_NEVER.equals(CVSUIPlugin.getPlugin().getPreferenceStore().getString(ICVSUIConstants.PREF_UPDATE_PROMPT)); } public ISchedulingRule validateEditRule(CVSResourceRuleFactory factory, IResource[] resources) { if (!isNeverUpdate()) { // We may need to perform an update so we need to obtain the lock on each project Set projects = new HashSet(); for (int i = 0; i < resources.length; i++) { IResource resource = resources[i]; if (isReadOnly(resource)) projects.add(resource.getProject()); } return createSchedulingRule(projects); } return internalValidateEditRule(factory, resources); } }