blob: 126c40751d39c380b7fdf70df87170be24d36265 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 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.wst.validation.internal.ui;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewActionDelegate;
import org.eclipse.ui.IViewPart;
import org.eclipse.wst.validation.ValidationFramework;
import org.eclipse.wst.validation.internal.ConfigurationManager;
import org.eclipse.wst.validation.internal.DisabledResourceManager;
import org.eclipse.wst.validation.internal.ValType;
import org.eclipse.wst.validation.internal.ValidationRegistryReader;
import org.eclipse.wst.validation.internal.ValidationSelectionHandlerRegistryReader;
import org.eclipse.wst.validation.internal.plugin.ValidationPlugin;
import org.eclipse.wst.validation.internal.ui.plugin.ValidationUIPlugin;
import org.eclipse.wst.validation.ui.internal.ManualValidationRunner;
/**
* This class implements the pop-up menu item "Run Validation" When the item is selected, this
* action triggers a validation of the project, using all configured, enabled validators.
*/
public class ValidationMenuAction implements IViewActionDelegate {
private ISelection _currentSelection;
protected static final String SEP = "/"; //$NON-NLS-1$
private Display _currentDisplay;
private IResourceVisitor _folderVisitor;
private IResourceVisitor _projectVisitor;
private Map<IProject, Set<IResource>> _selectedResources;
public ValidationMenuAction() {
// cache the display before this action is forked. After the action is forked,
// Display.getCurrent() returns null.
_currentDisplay = Display.getCurrent();
_selectedResources = new HashMap<IProject, Set<IResource>>();
}
private Display getDisplay() {
return (_currentDisplay == null) ? Display.getCurrent() : _currentDisplay;
}
/**
* Return the wizard's shell.
*/
Shell getShell() {
Display display = getDisplay();
Shell shell = (display == null) ? null : display.getActiveShell();
if (shell == null && display != null) {
Shell[] shells = display.getShells();
if (shells.length > 0)
shell = shells[0];
}
return shell;
}
/**
* Return a map of the selected elements. Each key of the map is an IProject, and the value is a
* Set of the selected resources in that project. If a project is selected, and nothing else in
* the project is selected, a full validation (null value) will be done on the project. If a
* project is selected, and some files/folders in the project are also selected, only the
* files/folders will be validated. If a folder is selected, all of its contents are also
* validated.
*
* @return null if there is no selection.
*/
private Map<IProject, Set<IResource>> loadSelected(ValidateAction action) {
// GRK previously this did not do a clear, but I couldn't understand why that would be so I am forcing a clear
// GRK In my testing, not doing a clear caused duplicate validations
_selectedResources.clear();
if ((_currentSelection == null) || _currentSelection.isEmpty() ||
!(_currentSelection instanceof IStructuredSelection))return null;
Object[] elements = ((IStructuredSelection) _currentSelection).toArray();
for (Object element : elements) {
if (element != null)addSelected(action, element);
}
return _selectedResources;
}
private void addSelected(ValidateAction action, Object selected) {
if (selected instanceof IProject) {
addVisitor((IProject) selected);
} else if (selected instanceof IFile) {
addSelected((IFile) selected);
} else if (selected instanceof IFolder) {
addVisitor((IFolder) selected);
} else if (isValidType(getExtendedType(selected))) {
addSelected(action,getExtendedType(selected));
} else {
// Not a valid input type. Must be IProject, IJavaProject, or IResource.
// If this ValidationMenuAction is a delegate of ValidateAction, is
// the input type recognized by the ValidateAction?
boolean valid = false;
if (action != null) {
IResource[] resources = action.getResource(selected);
if (resources != null) {
valid = true;
for (int i = 0; i < resources.length; i++) {
addSelected(action, resources[i]);
}
}
}
if (!valid) {
// Stop processing. This allows the "Run Validation" menu item
// to gray out once an element that can not be validated is selected.
_selectedResources.clear();
}
}
}
private Object getExtendedType(Object selected) {
Object result = ValidationSelectionHandlerRegistryReader.getInstance().getExtendedType(selected);
return result == null ? selected : result;
}
private boolean isValidType(Object object) {
return object instanceof IProject || object instanceof IFile || object instanceof IFolder;
}
void addSelected(IResource selected) {
IProject project = selected.getProject();
boolean added = _selectedResources.containsKey(project);
Set<IResource> changedRes = null;
if (added) {
// If the value is null, the entire project needs to be validated anyway.
changedRes = _selectedResources.get(project);
if (changedRes == null)return;
} else {
changedRes = new HashSet<IResource>();
}
if (changedRes.add(selected)) {
_selectedResources.put(project, changedRes);
}
}
private void addVisitor(IFolder selected) {
// add the folder and its children
try {
selected.accept(getFolderVisitor());
} catch (CoreException exc) {
ValidationUIPlugin.getPlugin().handleException(exc);
return;
}
}
private IResourceVisitor getFolderVisitor() {
if (_folderVisitor == null) {
_folderVisitor = new IResourceVisitor() {
public boolean visit(IResource res) {
if (res instanceof IFile) {
addSelected(res);
} else if (res instanceof IFolder) {
addSelected(res);
}
return true; // visit the resource's children
}
};
}
return _folderVisitor;
}
private void addVisitor(IProject selected) {
// add the folder and its children
if(!selected.isAccessible())return;
try {
selected.accept(getProjectVisitor());
} catch (CoreException exc) {
ValidationUIPlugin.getPlugin().handleException(exc);
return;
}
}
private IResourceVisitor getProjectVisitor() {
if (_projectVisitor == null) {
_projectVisitor = new IResourceVisitor() {
public boolean visit(IResource res) {
if (DisabledResourceManager.getDefault().isDisabled(res))return false;
if (res instanceof IFile)addSelected(res);
else if (res instanceof IFolder)addSelected(res);
else if (res instanceof IProject)addSelected(res);
return true;
}
};
}
return _projectVisitor;
}
/**
* The delegating action has been invoked. This method does the actual work.
*
* @param action
* Action proxy that handles the presentation portion of the plug-in action.
*/
public void run(IAction action) {
ValidateAction vaction = null;
if (action instanceof ValidateAction) {
vaction = (ValidateAction) action;
}
final Map<IProject, Set<IResource>> projects = loadSelected(vaction);
if ((projects == null) || (projects.size() == 0)) {
return;
}
// If the files aren't saved do not run validation.
if(!handleFilesToSave(projects))return;
boolean confirm = org.eclipse.wst.validation.internal.ValManager.getDefault().getGlobalPreferences()
.getConfirmDialog();
ManualValidationRunner.validate(projects, ValType.Manual, confirm);
}
/**
* Selection in the desktop has changed. Plug-in provider can use it to change the availability
* of the action or to modify other presentation properties.
*
* <p>
* Action delegate cannot be notified about selection changes before it is loaded. For that
* reason, control of action's enable state should also be performed through simple XML rules
* defined for the extension point. These rules allow enable state control before the delegate
* has been loaded.
* </p>
*
* @param action
* action proxy that handles presentation portion of the plug-in action
* @param selection
* current selection in the desktop
*/
public void selectionChanged(IAction action, ISelection selection) {
_currentSelection = selection;
boolean enabled = quickCheck(selection);
// Don't force the plug-in to be activated just to check this setting.
if (enabled && ValidationPlugin.isActivated() && ValidationRegistryReader.isActivated()){
enabled = hasManualValidators(selection);
}
action.setEnabled(enabled);
}
/**
* Do a quick check on the selection, so see if we know that we don't want to validate the selection.
*
* @param selection
* @return false if we are sure that we don't want to validate it. Return true if we are still not sure.
*/
private boolean quickCheck(ISelection selection){
if (selection == null || selection.isEmpty())return false;
if (selection instanceof IStructuredSelection){
IStructuredSelection ss = (IStructuredSelection)selection;
Object sel = ss.getFirstElement();
if (sel != null){
if (sel instanceof IProject){
IProject project = (IProject)sel;
if (!project.isOpen())return false;
}
}
}
return true;
}
/**
* Answer true if any of the selected items have manual validators enabled.
* @param selection
*/
private boolean hasManualValidators(ISelection selection){
if (selection instanceof IStructuredSelection){
IStructuredSelection ss = (IStructuredSelection)selection;
for (Iterator it = ss.iterator(); it.hasNext();){
Object sel = it.next();
if (sel instanceof IResource){
IResource resource = (IResource)sel;
if (ValidationFramework.getDefault().hasValidators(resource, true, false))return true;
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart)
*/
public void init(IViewPart view) {
}
/**
* Handle any files that must be saved prior to running
* validation.
*
* @param projects
* The list of projects that will be validated.
* @return
* True if all files have been saved, false otherwise.
*/
protected boolean handleFilesToSave(Map<IProject, Set<IResource>> projects)
{
List fileList = getIFiles(projects);
final IEditorPart[] dirtyEditors = SaveFilesHelper.getDirtyEditors(fileList);
if(dirtyEditors == null || dirtyEditors.length == 0)return true;
boolean saveAutomatically = false;
try
{
saveAutomatically = ConfigurationManager.getManager().getGlobalConfiguration().getSaveAutomatically();
}
catch(InvocationTargetException e)
{
// In this case simply default to false.
}
SaveFilesDialog sfDialog = null;
if(!saveAutomatically)
{
sfDialog = new SaveFilesDialog(ValidationUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow().getShell());
sfDialog.setInput(Arrays.asList(dirtyEditors));
}
if(saveAutomatically || sfDialog.open() == Window.OK){
ProgressMonitorDialog ctx = new ProgressMonitorDialog(getShell());
IRunnableWithProgress runnable = new IRunnableWithProgress(){
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException{
try {
monitor.beginTask(ValidationUIMessages.SaveFilesDialog_saving, dirtyEditors.length);
int numDirtyEditors = dirtyEditors.length;
for(int i = 0; i < numDirtyEditors; i++){
dirtyEditors[i].doSave(new SubProgressMonitor(monitor, 1));
}
} finally {
monitor.done();
}
}
};
try {
ctx.run(false, true, runnable);
return true;
} catch (InvocationTargetException e) {
ValidationUIPlugin.getPlugin().handleException(e);
} catch (InterruptedException e) {
ValidationUIPlugin.getPlugin().handleException(e);
}
}
return false;
}
protected List<IFile> getIFiles(Map<IProject, Set<IResource>> projects) {
List<IFile> fileList = new LinkedList<IFile>();
for(IProject project : projects.keySet()) {
for(IResource resource : projects.get(project)) {
if(resource instanceof IFile)fileList.add((IFile)resource);
}
}
return fileList;
}
}