[Bug 112037] Should DTD, WSDL, XML, XSD validators have "Validate" action - Fix for saving files before validation runs on a project
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ListContentProvider.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ListContentProvider.java
new file mode 100644
index 0000000..90b6d02
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ListContentProvider.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 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.util.List;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+/** 
+ * A specialized content provider to show a list of editor parts.
+ * This class has been copied from org.eclipse.jdt.internal.ui.viewsupport.ListContentProvider
+ * This class should be removed once a generic solution is made available.
+ */ 
+public class ListContentProvider implements IStructuredContentProvider {
+	List fContents;	
+
+	public ListContentProvider() {
+	}
+	
+	public Object[] getElements(Object input) {
+		if (fContents != null && fContents == input)
+			return fContents.toArray();
+		return new Object[0];
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		if (newInput instanceof List) 
+			fContents= (List)newInput;
+		else
+			fContents= null;
+		// we use a fixed set.
+	}
+
+	public void dispose() {
+	}
+	
+	public boolean isDeleted(Object o) {
+		return fContents != null && !fContents.contains(o);
+	}
+}
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesDialog.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesDialog.java
new file mode 100644
index 0000000..9807bfd
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesDialog.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *   IBM - Initial API and implementation
+ */
+package org.eclipse.wst.validation.internal.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.dialogs.ListDialog;
+import org.eclipse.wst.validation.internal.ConfigurationManager;
+import org.eclipse.wst.validation.internal.GlobalConfiguration;
+import org.eclipse.wst.validation.internal.ui.plugin.ValidationUIPlugin;
+
+/**
+ * A generic save files dialog. The bulk of the code
+ * for this dialog was taken from the JDT refactoring
+ * support in org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper.
+ * This class is a good candidate for reuse amoung components.
+ */
+public class SaveFilesDialog extends ListDialog {
+	
+	public SaveFilesDialog(Shell parent)
+	{
+	  super(parent);
+	  setTitle(ValidationUIMessages.SaveFilesDialog_save_all_resources); 
+	  setAddCancelButton(true);
+	  setLabelProvider(createDialogLabelProvider());
+	  setMessage(ValidationUIMessages.SaveFilesDialog_must_save); 
+	  setContentProvider(new ListContentProvider());
+	}
+
+	protected Control createDialogArea(Composite container) 
+	{
+		Composite result= (Composite) super.createDialogArea(container);
+		boolean fAllowSaveAlways = true;
+		if (fAllowSaveAlways) {
+			final Button check= new Button(result, SWT.CHECK);
+			check.setText(ValidationUIMessages.SaveFilesDialog_always_save); 
+			//check.setSelection(RefactoringSavePreferences.getSaveAllEditors());
+			check.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+				  try
+				  {
+				    GlobalConfiguration config = ConfigurationManager.getManager().getGlobalConfiguration();
+				    config.setSaveAutomatically(check.getSelection());
+				    config.store();
+				  }
+				  catch(InvocationTargetException exc)
+				  {
+					Logger.getLogger(ValidationUIPlugin.getBundleName()).log(Level.WARNING, "Unable to set save automatically preference in save files for validation dialog: " + exc);
+				  }
+				  
+				}
+			});
+			applyDialogFont(result);
+		}
+		return result;
+	}
+	
+	private ILabelProvider createDialogLabelProvider() {
+		return new LabelProvider() {
+			public Image getImage(Object element) {
+				return ((IEditorPart) element).getTitleImage();
+			}
+			public String getText(Object element) {
+				return ((IEditorPart) element).getTitle();
+			}
+		};
+	}	
+}
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesHelper.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesHelper.java
new file mode 100644
index 0000000..4e15e0a
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/SaveFilesHelper.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *   IBM - Initial API and implementation
+ */
+package org.eclipse.wst.validation.internal.ui;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A helper class for the save dirty files dialog.
+ */
+public class SaveFilesHelper {
+
+	/**
+	 * Retreive an array of IEditorParts representing all the dirty
+	 * editors open for the files provided in the list.
+	 * 
+	 * @param files
+	 * 			A list of IFiles.
+	 * @return
+	 * 			An array of IEditorParts containing all the dirty editors for the files in the list.
+	 */
+	public static IEditorPart[] getDirtyEditors(List files) {
+		Set inputs = new HashSet();
+		List result = new ArrayList(0);
+		IWorkbench workbench = PlatformUI.getWorkbench();
+		IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
+		for (int i = 0; i < windows.length; i++) {
+			IWorkbenchPage[] pages = windows[i].getPages();
+			for (int x = 0; x < pages.length; x++) {
+				IEditorPart[] editors = pages[x].getDirtyEditors();
+				for (int z = 0; z < editors.length; z++) {
+					IEditorPart ep = editors[z];
+					IEditorInput input = ep.getEditorInput();
+					if (input instanceof IFileEditorInput) {
+						IFileEditorInput fileInput = (IFileEditorInput) input;
+						if (files.contains(fileInput.getFile())) {
+							if (!inputs.contains(input)) {
+								inputs.add(input);
+								result.add(ep);
+							}
+						}
+					}
+				}
+			}
+		}
+		return (IEditorPart[]) result.toArray(new IEditorPart[result.size()]);
+	}
+}
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationMenuAction.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationMenuAction.java
index 5983cf7..ec61c8f 100644
--- a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationMenuAction.java
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationMenuAction.java
@@ -12,12 +12,15 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.logging.Level;
 
+import org.eclipse.core.internal.resources.Project;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
@@ -34,13 +37,16 @@
 import org.eclipse.jface.action.IAction;
 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.Event;
 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.common.frameworks.internal.ui.WTPUIPlugin;
 import org.eclipse.wst.validation.internal.ConfigurationManager;
+import org.eclipse.wst.validation.internal.GlobalConfiguration;
 import org.eclipse.wst.validation.internal.ProjectConfiguration;
 import org.eclipse.wst.validation.internal.ValidationRegistryReader;
 import org.eclipse.wst.validation.internal.ValidationSelectionHandlerRegistryReader;
@@ -58,6 +64,7 @@
 	protected static final String SEP = "/"; //$NON-NLS-1$
 	private Display _currentDisplay = null;
 	private IResourceVisitor _folderVisitor = null;
+	private IResourceVisitor _projectVisitor = null;
 	private Map _selectedResources = null;
 	//protected IWorkbenchContext workbenchContext;
 
@@ -101,6 +108,7 @@
 	 * files/folders will be validated. If a folder is selected, all of its contents are also
 	 * validated.
 	 */
+	// TODO: Check this method for selected resources.
 	private Map loadSelected(ValidateAction action, boolean refresh) {
 		if (refresh) {
 			// selectionChanged(IAction, ISelection) has been called. Flush the
@@ -125,7 +133,7 @@
 
 	private void addSelected(ValidateAction action, Object selected) {
 		if (selected instanceof IProject) {
-			addSelected((IProject) selected);
+			addVisitor((IProject) selected);
 		} else if (selected instanceof IFile) {
 			addSelected((IFile) selected);
 		} else if (selected instanceof IFolder) {
@@ -165,11 +173,11 @@
 		return object instanceof IProject || object instanceof IFile || object instanceof IFolder;
 	}
 
-	private void addSelected(IProject selected) {
-		_selectedResources.put(selected, null); // whatever the values were
-		// before, the entire project
-		// needs to be revalidated now
-	}
+//	private void addSelected(IProject selected) {
+//		_selectedResources.put(selected, null); // whatever the values were
+//		// before, the entire project
+//		// needs to be revalidated now
+//	}
 
 //	private void addSelected(IJavaProject selected) {
 //		_selectedResources.put(selected.getProject(), null); // whatever the
@@ -232,6 +240,39 @@
 		}
 		return _folderVisitor;
 	}
+	
+	private void addVisitor(IProject selected) {
+		// add the folder and its children
+		try {
+			selected.accept(getProjectVisitor());
+		} catch (CoreException exc) {
+			Logger logger = WTPUIPlugin.getLogger();
+			if (logger.isLoggingLevel(Level.SEVERE)) {
+				LogEntry entry = ValidationUIPlugin.getLogEntry();
+				entry.setSourceIdentifier("ValidationMenuAction.addSelected(IFolder)"); //$NON-NLS-1$
+				entry.setMessageTypeIdentifier(ResourceConstants.VBF_EXC_INTERNAL);
+				entry.setTargetException(exc);
+				logger.write(Level.SEVERE, entry);
+			}
+			return;
+		}
+	}
+
+	private IResourceVisitor getProjectVisitor() {
+		if (_projectVisitor == null) {
+			_projectVisitor = 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 _projectVisitor;
+	}
 
 	/**
 	 * The delegating action has been performed. Implement this method to do the actual work.
@@ -294,6 +335,7 @@
 	
 	
 	public void run(IAction action) {
+		// TODO: Insert dirty file check here.
 		ValidateAction vaction = null;
 		if (action instanceof ValidateAction) {
 			vaction = (ValidateAction) action;
@@ -302,6 +344,12 @@
 		if ((projects == null) || (projects.size() == 0)) {
 			return;
 		}
+		
+		// If the files aren't saved do not run validation.
+		if(!handleFilesToSave(projects))
+		{
+		  return;
+		}
 
 		ValidationJob validationop = new ValidationJob("Running Validation"){ //$NON-NLS-1$
 			protected IStatus run(IProgressMonitor monitor) {
@@ -571,7 +619,7 @@
 			new Status(IStatus.CANCEL, "org.eclipse.wst.validation", 0, "OK", null);
 		
 		ManualValidatorsOperation validOp = null;
-		validOp = new ManualValidatorsOperation(project);
+		validOp = new ManualValidatorsOperation(project, resources);
 //		if (resources == null) {
 //			validOp = new ManualValidatorsOperation(project);
 //		} else {
@@ -758,4 +806,70 @@
 		//init
 		
 	}
+	
+	/**
+	 * 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 projects)
+	{
+	  List fileList = getIFiles(projects);
+      IEditorPart[] dirtyEditors = SaveFilesHelper.getDirtyEditors(fileList);
+      if(dirtyEditors == null || dirtyEditors.length == 0)
+    	return true;
+      boolean saveAutomatically = false;
+      try
+      {
+        saveAutomatically = new GlobalConfiguration(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));
+      }
+	  // Save all open editors.
+	  if(saveAutomatically || sfDialog.open() == Window.OK)
+	  {
+		int numDirtyEditors = dirtyEditors.length;
+		for(int i = 0; i < numDirtyEditors; i++)
+		{
+		  dirtyEditors[i].doSave(null);
+		}
+		return true;
+	  }
+	  return false;
+	}
+	
+	protected List getIFiles(Map projects)
+	{
+		List fileList = new ArrayList();
+		Set projectKeys = projects.keySet();
+		Iterator projectIter = projectKeys.iterator();
+		while(projectIter.hasNext())
+		{
+		  Project project = (Project)projectIter.next();
+		  List resourcesList = (List)projects.get(project);
+		  Iterator resourcesIter = resourcesList.iterator();
+		  while(resourcesIter.hasNext())
+		  {
+			IResource resource = (IResource)resourcesIter.next();
+			if(resource instanceof IFile)
+			{
+				fileList.add(resource);
+			}
+		  }
+		}
+		return fileList;
+	}
+	
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationPreferencePage.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationPreferencePage.java
index 1f9c955..a501078 100644
--- a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationPreferencePage.java
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationPreferencePage.java
@@ -245,6 +245,7 @@
 	private Label emptyRowPlaceholder = null;
 	Button disableAllValidation = null;
 	Button overrideButton = null;
+	Button saveButton = null;
 	private Label listLabel = null;
 	private Table validatorsTable;
 
@@ -522,6 +523,22 @@
 		
 		emptyRowPlaceholder = new Label(validatorGroup, SWT.NONE);
 		emptyRowPlaceholder.setLayoutData(new GridData());
+		
+		GridData saveFileData = new GridData(GridData.FILL_HORIZONTAL);
+		saveFileData.horizontalSpan = 2;
+		saveButton = new Button(validatorGroup, SWT.CHECK);
+		saveButton.setLayoutData(saveFileData);
+		saveButton.setText(ValidationUIMessages.PrefPage_always_save);
+		saveButton.setSelection(pagePreferences.getSaveAutomatically());
+		saveButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				pagePreferences.setSaveAutomatically(saveButton.getSelection());
+				saveButton.setFocus();
+			}
+		});
+		
+		emptyRowPlaceholder = new Label(validatorGroup, SWT.NONE);
+		emptyRowPlaceholder.setLayoutData(new GridData());
 
 		listLabel = new Label(validatorGroup, SWT.NONE);
 		GridData listLabelData = new GridData(GridData.FILL_HORIZONTAL);
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationUIMessages.java b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationUIMessages.java
new file mode 100644
index 0000000..1d06913
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/ValidationUIMessages.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *   IBM - Initial API and implementation
+ */
+package org.eclipse.wst.validation.internal.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Strings used by Validation UI.
+ */
+public class ValidationUIMessages extends NLS {
+	private static final String BUNDLE_NAME = "org.eclipse.wst.validation.internal.ui.validationui";//$NON-NLS-1$
+
+	public static String SaveFilesDialog_saving;
+	public static String SaveFilesDialog_always_save;
+	public static String SaveFilesDialog_save_all_resources;
+	public static String SaveFilesDialog_must_save;
+	public static String PrefPage_always_save;
+
+	static {
+		// load message values from bundle file
+		NLS.initializeMessages(BUNDLE_NAME, ValidationUIMessages.class);
+	}
+
+	private ValidationUIMessages() {
+		// cannot create new instance
+	}
+}
diff --git a/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/validationui.properties b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/validationui.properties
new file mode 100644
index 0000000..28033e1
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation.ui/validateui/org/eclipse/wst/validation/internal/ui/validationui.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2006 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
+###############################################################################
+
+SaveFilesDialog_saving=Saving Resources
+SaveFilesDialog_always_save=&Always save all modified resources automatically prior to validating
+SaveFilesDialog_save_all_resources=Save All Modified Resources
+SaveFilesDialog_must_save=All modified resources must be saved before this operation.
+
+PrefPage_always_save=&Save all modified resources automatically prior to validating
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ConfigurationConstants.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ConfigurationConstants.java
index fcfef17..171596b 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ConfigurationConstants.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ConfigurationConstants.java
@@ -25,6 +25,7 @@
 	public static final String DISABLE_ALL_VALIDATION_SETTING = "disableAllValidation"; //$NON-NLS-1$ // boolean
 	///* package */static final String AUTO_SETTING = "autoValidate"; //$NON-NLS-1$ // boolean
 	///* package */static final String BUILD_SETTING = "runWhenBuild"; //$NON-NLS-1$ // boolean
+	public static final String SAVE_AUTOMATICALLY_SETTING = "saveAutomatically"; //$NON-NLS-1$ // boolean
 
 	// Defaults for the preference and project values
 	
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/GlobalConfiguration.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/GlobalConfiguration.java
index 67aaa41..8c64757 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/GlobalConfiguration.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/GlobalConfiguration.java
@@ -27,8 +27,10 @@
  */
 public class GlobalConfiguration extends ValidationConfiguration {
 	/* package */static final boolean PREF_PROJECTS_CAN_OVERRIDE_DEFAULT = true;
+	static final boolean PREF_SAVE_AUTOMATICALLY_DEFAULT = false;
 
 	private boolean _canProjectsOverride = getCanProjectsOverrideDefault();
+	private boolean _saveAutomatically = getSaveAutomaticallyDefault();
 
 	/**
 	 * This constructor should be used in all cases except for the Preference page's values.
@@ -62,11 +64,20 @@
 	public void setCanProjectsOverride(boolean can) {
 		_canProjectsOverride = can;
 	}
+	
+	public boolean getSaveAutomatically() {
+		return _saveAutomatically;
+	}
+	
+	public void setSaveAutomatically(boolean save) {
+		_saveAutomatically = save;
+	}
 
 	public void resetToDefault()  throws InvocationTargetException {
 		setDisableAllValidation(getDisableValidationDefault());
 		setEnabledValidators(getEnabledValidatorsDefault());
 		setCanProjectsOverride(getCanProjectsOverrideDefault());
+		setSaveAutomatically(getSaveAutomaticallyDefault());
     setDefaultDelegates(getValidators());
 	}
 
@@ -129,7 +140,6 @@
 			if (enabledManualValidators.equals(null) || enabledBuildValidators.equals(null)) 
 				enabledValidators = ConfigurationConstants.DEFAULT_ENABLED_VALIDATORS;
 			setCanProjectsOverride(getValue(rootMarker, ConfigurationConstants.PREF_PROJECTS_CAN_OVERRIDE, PREF_PROJECTS_CAN_OVERRIDE_DEFAULT));
-
 			root.getWorkspace().deleteMarkers(marker);
 		} catch (CoreException exc) {
 			Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
@@ -151,11 +161,16 @@
 		// this field is overwritten. Fields of this class are initialized to the
 		// default after the ValidationConfiguration parent is created.
 		gp.setCanProjectsOverride(canProjectsOverride());
+		gp.setSaveAutomatically(getSaveAutomatically());
 	}
 
 	public static boolean getCanProjectsOverrideDefault() {
 		return PREF_PROJECTS_CAN_OVERRIDE_DEFAULT;
 	}
+	
+	public static boolean getSaveAutomaticallyDefault() {
+		return PREF_SAVE_AUTOMATICALLY_DEFAULT;
+	}
 
 	/**
 	 * @see org.eclipse.wst.validation.internal.operations.internal.attribute.ValidationConfiguration#deserialize(String)
@@ -168,10 +183,16 @@
 			// this instance.
 			int canOverrideIndex = storedConfiguration.indexOf(ConfigurationConstants.PREF_PROJECTS_CAN_OVERRIDE);
 			int disableAllValidationIndex = storedConfiguration.indexOf(ConfigurationConstants.DISABLE_ALL_VALIDATION_SETTING);
+			int saveAutomaticallyIndex = storedConfiguration.indexOf(ConfigurationConstants.SAVE_AUTOMATICALLY_SETTING);
 			if (disableAllValidationIndex != -1) {
-				String canOverride = storedConfiguration.substring(0 + ConfigurationConstants.PREF_PROJECTS_CAN_OVERRIDE.length(), disableAllValidationIndex);
+				String canOverride = storedConfiguration.substring(canOverrideIndex + ConfigurationConstants.PREF_PROJECTS_CAN_OVERRIDE.length(), disableAllValidationIndex);
 				setCanProjectsOverride(Boolean.valueOf(canOverride).booleanValue());
 			}
+			if(saveAutomaticallyIndex != -1)
+			{
+				String saveAutomatically = storedConfiguration.substring(saveAutomaticallyIndex + ConfigurationConstants.SAVE_AUTOMATICALLY_SETTING.length(), canOverrideIndex);
+				setSaveAutomatically(Boolean.valueOf(saveAutomatically).booleanValue());
+			}
 		}
 	}
 
@@ -180,6 +201,8 @@
 	 */
 	public String serialize() throws InvocationTargetException {
 		StringBuffer buffer = new StringBuffer();
+		buffer.append(ConfigurationConstants.SAVE_AUTOMATICALLY_SETTING);
+		buffer.append(String.valueOf(getSaveAutomatically()));
 		buffer.append(ConfigurationConstants.PREF_PROJECTS_CAN_OVERRIDE);
 		buffer.append(String.valueOf(canProjectsOverride()));
 		buffer.append(super.serialize());
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ManualValidatorsOperation.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ManualValidatorsOperation.java
index 242b07d..0dab81d 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ManualValidatorsOperation.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ManualValidatorsOperation.java
@@ -32,4 +32,7 @@
 		super(project, DEFAULT_FORCE, RegistryConstants.ATT_RULE_GROUP_DEFAULT, false);
 		setEnabledValidators(ValidatorManager.getManager().getManualEnabledValidators(project));
 	}	
+	public ManualValidatorsOperation(IProject project, Object[] changedResources) {
+		super(project, DEFAULT_FORCE, RegistryConstants.ATT_RULE_GROUP_DEFAULT, changedResources, false);
+	}	
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ValidatorSubsetOperation.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ValidatorSubsetOperation.java
index f5aee2d..533bb70 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ValidatorSubsetOperation.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/operations/ValidatorSubsetOperation.java
@@ -83,6 +83,25 @@
 	 * validation thread, and all other validators in the main thread. If async is false, all
 	 * validators will run in in the main thread.
 	 */
+	public ValidatorSubsetOperation(IProject project, boolean force, int ruleGroup, Object[] changedResources, boolean async) {
+		super(project, null, null, ruleGroup, force, async);
+		setEnabledValidators(ValidatorManager.getManager().getManualEnabledValidators(project));
+		setFileDeltas(FilterUtil.getFileDeltas(getEnabledValidators(), changedResources, false));
+	}
+	
+	/**
+	 * Create an operation that runs a full validation on the named validators using the
+	 * <code>ruleGroup</code> pass. Use this constructor only if you want to run a validator that
+	 * supports the two passes: FAST and FULL.
+	 * 
+	 * If force is true, validation is run whether or not it needs to.
+	 * 
+	 * IProject must exist and be open.
+	 * 
+	 * If async is true, the validation will run all thread-safe validators in the background
+	 * validation thread, and all other validators in the main thread. If async is false, all
+	 * validators will run in in the main thread.
+	 */
 	public ValidatorSubsetOperation(IProject project, boolean force, int ruleGroup, boolean async) {
 		super(project, null, null, ruleGroup, force, async);
 	}