[313384] Validation Framework invokes the V1 validators too often
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/RegistryConstants.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/RegistryConstants.java
index 81a78b4..140240a 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/RegistryConstants.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/RegistryConstants.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2007 IBM Corporation and others.
+ * Copyright (c) 2001, 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
@@ -54,6 +54,9 @@
 	 * only one validator may use an aggregate of that type. */
 	String TAG_AGGREGATE_VALIDATORS = "aggregateValidator"; //$NON-NLS-1$ 
 
+	/** runStrategy - identifies the run strategy of  Validator*/
+	String TAG_RUN_STRATEGY = "runStrategy"; //$NON-NLS-1$
+	
 	/** objectClass - identifies a type */ 
 	String ATT_OBJECT_CLASS = "objectClass"; //$NON-NLS-1$
 	
@@ -119,6 +122,16 @@
 	/** false - The "can validator run asynchronously" default. In the future this may be changed to true. */
 	boolean ATT_ASYNC_DEFAULT = false;
 
+	/** 
+	 * project - identifies whether or not the validator is called per project. 
+	 * Default is false (i.e. the validator is called per resource). 
+	 */
+	String ATT_PROJECT = "project"; //$NON-NLS-1$
+	
+	/** false - The project default. */
+	boolean ATT_PROJECT_DEFAULT = false;
+	
+	
 	/** migrate - the "migrate" section of the validator */
 	String TAG_MIGRATE = "migrate"; //$NON-NLS-1$
 	
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidationRegistryReader.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidationRegistryReader.java
index 9766048..7a4d5f1 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidationRegistryReader.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidationRegistryReader.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2009 IBM Corporation and others.
+ * Copyright (c) 2001, 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
@@ -1221,7 +1221,7 @@
 				getIncremental(element), getFullBuild(element), element, helperImplName, getMigrationMetaData(element),
 				pluginId, getRuleGroup(element), runChildren[0], validatorName.intern(), validatorImplName.intern(),
 				getContentTypeBindings(element), getDependentValidatorValue(element), getEnablementElement(element),
-				getFacetIds(element), getFilters(element), getProjectNatureFilters(element), markerIds);
+				getFacetIds(element), getFilters(element), getProjectNatureFilters(element), markerIds, getRunStragety(element));
 		
 		
 		if (Tracing.isTraceV1()) {
@@ -1349,5 +1349,22 @@
 			return vmd.getValidator();
 		return null;
 	}
+	/**
+	 * Given an IConfigurationElement from plugin.xml, return whether or not the validator is called by project
+	 * 
+	 * <p>If no project attribute is specified, the default is false (validator is called by resource)
+	 */
+	private boolean getRunStragety(IConfigurationElement element) {
+		
+		IConfigurationElement[] runChildren = element.getChildren(TAG_RUN_STRATEGY);
+		
+		if (runChildren == null || runChildren.length < 1) return RegistryConstants.ATT_PROJECT_DEFAULT;
+		String project = runChildren[0].getAttribute(ATT_PROJECT);
+		if (project == null)return RegistryConstants.ATT_PROJECT_DEFAULT;
+
+		return Boolean.valueOf(project.trim()); 
+	
+	}
+	
 
 }
diff --git a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidatorMetaData.java b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidatorMetaData.java
index 6b26bf6..4fef8a4 100644
--- a/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidatorMetaData.java
+++ b/plugins/org.eclipse.wst.validation/validate/org/eclipse/wst/validation/internal/ValidatorMetaData.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2008 IBM Corporation and others.
+ * Copyright (c) 2001, 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
@@ -78,13 +78,14 @@
 	private final Map<IValidatorJob, IWorkbenchContext> _helpers = 
 		Collections.synchronizedMap( new HashMap<IValidatorJob, IWorkbenchContext>() );
 	private final Expression 		_enablementExpression;
+	private boolean _validateByProject = true;
 
 	ValidatorMetaData(boolean async, String[] aggregatedValidators, boolean isEnabledByDefault, boolean supportsIncremental,
 			boolean supportsFullBuild, IConfigurationElement helperClassElement, String helperClassName, 
 			MigrationMetaData migrationMetaData, String pluginId, int ruleGroup, IConfigurationElement validatorClassElement,
 			String validatorDisplayName, String validatorUniqueName, String[] contentTypeIds, boolean dependentValidator,
 			Expression enablementExpression, String[] facetFilters, ValidatorFilter[] filters,
-			ValidatorNameFilter[] projectNatureFilters, String[] markerIds) {
+			ValidatorNameFilter[] projectNatureFilters, String[] markerIds, boolean validateByProject) {
 		_async = async;
 		_aggregatedValidators = aggregatedValidators;
 		_isEnabledByDefault = isEnabledByDefault;
@@ -106,6 +107,7 @@
 		_projectNatureFilters = projectNatureFilters;
 		_markerIds = markerIds;
 		_validatorNames = buildValidatorNames();
+		_validateByProject = validateByProject;
 	}
 		
 	protected String[] getFacetFilters() {
@@ -544,5 +546,9 @@
 	}
 	return false;
 }
+
+public boolean isValidateByProject() {
+	return _validateByProject;
+}
    
 }
diff --git a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/Validator.java b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/Validator.java
index 7a9da1c..88f5ec5 100644
--- a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/Validator.java
+++ b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/Validator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * Copyright (c) 2007, 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
@@ -13,10 +13,12 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 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 java.util.concurrent.atomic.AtomicBoolean;
 
 import org.eclipse.core.resources.IMarker;
@@ -748,6 +750,8 @@
 		IValidator v = asIValidator();
 		if (v == null)return null;
 		
+		if (shouldSkipValidator(resource, operation))return null;
+		
 		try {
 			IProject project = resource.getProject();
 			SummaryReporter reporter = new SummaryReporter(project, monitor);
@@ -783,6 +787,29 @@
 		return vr;
 	}
 	
+
+	private static final String VALIDATE_PROJECT_ONCE = "ValidateProjectOnce"; //$NON-NLS-1$
+
+		@SuppressWarnings("unchecked")	
+		private boolean shouldSkipValidator(IResource resource, ValOperation operation) {
+
+			if (_vmd.isValidateByProject())
+			{
+				ValidationState validationState = operation.getState();
+				Set<String> projectsNameSet = (Set<String>)validationState.get(VALIDATE_PROJECT_ONCE);
+				String projectName = resource.getProject().getName();
+
+				if (projectsNameSet == null) {
+					projectsNameSet = new HashSet<String>();
+					validationState.put(VALIDATE_PROJECT_ONCE, projectsNameSet);
+				}
+
+				if (projectsNameSet.contains(projectName))return true;
+				else projectsNameSet.add(projectName);
+			}
+			return false;
+		}
+
 	/*
 	 * GRK - Because I didn't want to try to make a true copy of the V1 validator, (because I didn't
 	 * want to copy the vmd object), I came up with this approach to only copy the fields that
diff --git a/plugins/org.eclipse.wst.validation/xsds/validatorExtSchema.exsd b/plugins/org.eclipse.wst.validation/xsds/validatorExtSchema.exsd
index 25d2602..0e9667e 100644
--- a/plugins/org.eclipse.wst.validation/xsds/validatorExtSchema.exsd
+++ b/plugins/org.eclipse.wst.validation/xsds/validatorExtSchema.exsd
@@ -1,6 +1,6 @@
 <?xml version='1.0' encoding='UTF-8'?>

 <!-- Schema file written by PDE -->

-<schema targetNamespace="org.eclipse.wst.validation">

+<schema targetNamespace="org.eclipse.wst.validation" xmlns="http://www.w3.org/2001/XMLSchema">

 <annotation>

       <appInfo>

          <meta.schema plugin="org.eclipse.wst.validation" id="validator" name="Validator"/>

@@ -13,6 +13,11 @@
    <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>

 

    <element name="extension">

+      <annotation>

+         <appInfo>

+            <meta.element />

+         </appInfo>

+      </annotation>

       <complexType>

          <sequence>

             <element ref="validator"/>

@@ -49,13 +54,14 @@
          <sequence>

             <element ref="projectNature" minOccurs="0" maxOccurs="unbounded"/>

             <element ref="filter" minOccurs="0" maxOccurs="unbounded"/>

-            <element ref="enablement" minOccurs="0" maxOccurs="unbounded"/>/>

+            <element ref="enablement" minOccurs="0" maxOccurs="unbounded"/>

             <element ref="helper"/>

             <element ref="dependentValidator" minOccurs="0" maxOccurs="1"/>

             <element ref="run"/>

             <element ref="markerId" minOccurs="0" maxOccurs="1"/>

             <element ref="facet" minOccurs="0" maxOccurs="unbounded"/>

             <element ref="contentTypeBinding" minOccurs="0" maxOccurs="unbounded"/>

+            <element ref="runStrategy"/>

          </sequence>

          <attribute name="to" type="string">

             <annotation>

@@ -94,15 +100,14 @@
          </attribute>

       </complexType>

    </element>

-   

-   

+

    <element name="contentTypeBinding">

       <annotation>

          <appInfo>

             <meta.element labelAttribute="contentTypeId"/>

          </appInfo>

          <documentation>

-                        Associates a particular content type with the current validator, and enables the validator to be run on resources of the specified content type.

+            Associates a particular content type with the current validator, and enables the validator to be run on resources of the specified content type.

          </documentation>

       </annotation>

       <complexType>

@@ -115,9 +120,6 @@
          </attribute>

       </complexType>

    </element>

-   

-   

-   

 

    <element name="filter">

       <complexType>

@@ -284,6 +286,19 @@
       </complexType>

    </element>

 

+   <element name="runStrategy">

+      <complexType>

+         <attribute name="project" type="boolean">

+            <annotation>

+               <documentation>

+                  If true then the validator is only called once per project. If false (or missing) then the validator could be called once per resource (based on the other filter rules).

+               </documentation>

+            </annotation>

+         </attribute>

+      </complexType>

+   </element>

+

+

    <annotation>

       <appInfo>

          <meta.section type="since"/>

@@ -293,39 +308,14 @@
       </documentation>

    </annotation>

 

-   <annotation>

-      <appInfo>

-         <meta.section type="examples"/>

-      </appInfo>

-      <documentation>

-         

-      </documentation>

-   </annotation>

 

-   <annotation>

-      <appInfo>

-         <meta.section type="apiInfo"/>

-      </appInfo>

-      <documentation>

-         

-      </documentation>

-   </annotation>

-

-   <annotation>

-      <appInfo>

-         <meta.section type="implementation"/>

-      </appInfo>

-      <documentation>

-         

-      </documentation>

-   </annotation>

 

    <annotation>

       <appInfo>

          <meta.section type="copyright"/>

       </appInfo>

       <documentation>

-         Copyright (c) 2005 IBM Corporation and others.&lt;br&gt;

+         Copyright (c) 2005, 2010 IBM Corporation and others.&lt;br&gt;

 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 &lt;a