[227834] API Need to have a way, to turn off validators for a certain kind of projects
diff --git a/plugins/org.eclipse.wst.validation/plugin.xml b/plugins/org.eclipse.wst.validation/plugin.xml
index a6c930a..1b17568 100644
--- a/plugins/org.eclipse.wst.validation/plugin.xml
+++ b/plugins/org.eclipse.wst.validation/plugin.xml
@@ -10,6 +10,7 @@
    <extension-point id="validationHelper" name="validationHelper" schema="xsds/validationHelper.exsd"/>
    <extension-point id="validatorDelegates" name="%ValidatorDelegates" schema="xsds/validatorDelegates.exsd"/>   
    <extension-point id="validatorGroup" name="Validator Group" schema="xsds/validatorgroup.exsd"/>   
+   <extension-point id="exclude" name="Validator Exclusions" schema="xsds/exclude.exsd"/>   
    
 <!--============================-->
 <!-- Validation Contributions   -->
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 07a9508..2daabfb 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
@@ -807,6 +807,7 @@
 	}
 
 	public synchronized void add(FilterGroup fg) {
+		assert fg != null;
 		_groupsArray = null;
 		_groups.add(fg);
 		bumpChangeCountGroups();
@@ -1178,6 +1179,7 @@
 		_validatorClassName = v2._validatorClassName;
 		_validatorGroupIds = v2._validatorGroupIds;
 	}
+
 }
 
 public String getSourceId() {
diff --git a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ExtensionConstants.java b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ExtensionConstants.java
index b21e978..9073cdb 100644
--- a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ExtensionConstants.java
+++ b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ExtensionConstants.java
@@ -20,6 +20,9 @@
 	/** validatorV2 - extension point for registering validators that use version 2 of the validation framework. */
 	String validator = "validatorV2"; //$NON-NLS-1$
 	
+	/** exclude - extension point for adding extra exclusion filters to a validator. */ 
+	String excludeExtension = "exclude"; //$NON-NLS-1$
+	
 	/** build - is the validator enabled by default for build based validations? true or false */
 	String build = "build"; //$NON-NLS-1$
 	
@@ -158,6 +161,11 @@
 		String sevIgnore = "ignore";  //$NON-NLS-1$
 	}
 	
+	interface Exclude {
+		/** id - fully qualified id of the validator that is being extended. */
+		String id = "id"; //$NON-NLS-1$
+	}
+	
 	/** true */
 	String True = "true"; //$NON-NLS-1$
 	
diff --git a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ValidatorExtensionReader.java b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ValidatorExtensionReader.java
index a952b11..3a4975e 100644
--- a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ValidatorExtensionReader.java
+++ b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/ValidatorExtensionReader.java
@@ -10,12 +10,14 @@
  *******************************************************************************/
 package org.eclipse.wst.validation.internal;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
 import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IContributor;
 import org.eclipse.core.runtime.IExtension;
@@ -23,9 +25,11 @@
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.wst.validation.MessageSeveritySetting;
 import org.eclipse.wst.validation.Validator;
+import org.eclipse.wst.validation.Validator.V2;
 import org.eclipse.wst.validation.internal.model.FilterGroup;
 import org.eclipse.wst.validation.internal.model.FilterRule;
 import org.eclipse.wst.validation.internal.plugin.ValidationPlugin;
@@ -50,19 +54,70 @@
 	/**
 	 * Read the extensions.
 	 */
-	List<Validator> process() {
-		List<Validator> list = new LinkedList<Validator>();
+	Collection<Validator> process() {
+		Map<String,Validator> map = new HashMap<String, Validator>(100);
 		IExtensionPoint extensionPoint = getExtensionPoint();
-		if (extensionPoint == null)return list;
+		if (extensionPoint == null)return map.values();
 				
 		for (IExtension ext : extensionPoint.getExtensions()){
 			for (IConfigurationElement validator : ext.getConfigurationElements()){
 				Validator v = processValidator(validator, ext.getUniqueIdentifier(), ext.getLabel(), null);
-				if (v != null)list.add(v);
+				if (v != null)map.put(v.getId(),v);
 			}
 		}
-		return list;
 		
+		extensionPoint = getExtensionPointExclude();
+		if (extensionPoint != null){
+			for (IExtension ext : extensionPoint.getExtensions()){
+				for (IConfigurationElement validator : ext.getConfigurationElements()){
+					String id = validator.getAttribute(ExtensionConstants.Exclude.id);
+					Validator v = map.get(id);
+					V2 v2 = null;
+					if (v != null)v2 = v.asV2Validator();
+
+					if (v2 == null){
+						String msg = NLS.bind("Plug-in configuration error, extension {0} references validator id {1} but this id does not exist.",  //$NON-NLS-1$
+							extensionPoint.getUniqueIdentifier(), id);
+						CoreException ex = new CoreException(new Status(IStatus.ERROR, ValidationPlugin.PLUGIN_ID, msg));
+						ValidationPlugin.getPlugin().handleException(ex);
+					}
+					else {
+						for (IConfigurationElement exclude : validator.getChildren()){
+							FilterGroup fg = createFilterGroup(exclude);
+							if (fg != null && fg.isExclude()){
+								mergeExcludeGroup(v2, fg);
+							}
+						}
+					}					
+				}
+			}
+			
+		}
+		return map.values();
+		
+	}
+	
+	/**
+	 * Merge the rules from the filter group into the current exclude group, creating a current exclude
+	 * group if need be.
+	 * @param v2
+	 * @param fg
+	 */
+	private void mergeExcludeGroup(V2 v2, FilterGroup fg){
+		FilterGroup existing = null;
+		for (FilterGroup group : v2.getGroups()){
+			if (group.isExclude()){
+				existing = group;
+				break;
+			}
+		}
+		if (existing == null)v2.add(fg);
+		else {
+			for (FilterRule rule : fg.getRules()){
+				existing.add(rule);
+			}
+			v2.bumpChangeCountGroups();
+		}
 	}
 	
 	/**
@@ -95,7 +150,7 @@
 			v.setVersion(getAttribute(validator, ExtensionConstants.version, 1));
 			v.setSourceId(validator.getAttribute(ExtensionConstants.sourceId));
 			IConfigurationElement[] children = validator.getChildren();
-			for (int i=0; i<children.length; i++)processValidatorChildren(v, children[i]);
+			for (IConfigurationElement child : children)processIncludeAndExcludeElement(v, child);
 		}
 		catch (Exception e){
 			ValidationPlugin.getPlugin().handleException(e);
@@ -136,6 +191,18 @@
 		IExtensionRegistry registry = Platform.getExtensionRegistry();
 		return registry.getExtensionPoint(ValidationPlugin.PLUGIN_ID, ExtensionConstants.validator);
 	}
+
+	/**
+	 * Answer the extension point for adding exclusion filters.
+	 * 
+	 * @return null if there is a problem or no extensions.
+	 */
+	private IExtensionPoint getExtensionPointExclude() {
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+		return registry.getExtensionPoint(ValidationPlugin.PLUGIN_ID, ExtensionConstants.excludeExtension);
+	}
+	
+	
 	
 
 	/**
@@ -155,14 +222,31 @@
 	}
 
 	/** 
-	 * Process the children of the validator tag, i.e. include and exclude groups.
+	 * Process the include and exclude elements.
 	 * 
-	 *  @param v the validator that we are building up
-	 *  @param group the include and exclude elements
+	 *  @param v The validator that we are building up.
+	 *  @param group The children of the validator tag. This may included include and exclude elements.
+	 *  Other elements are ignored. 
 	 */
-	private void processValidatorChildren(Validator.V2 v, IConfigurationElement group) {
+	private void processIncludeAndExcludeElement(Validator.V2 v, IConfigurationElement group) {
+		FilterGroup fg = createFilterGroup(group);
+		if (fg != null)v.add(fg);
+	}
+	
+	/**
+	 * Process an include or exclude element, returning a filter group for it.
+	 * 
+	 * @param group
+	 *            An include, exclude or some other element. Only include and
+	 *            exclude elements are processed, other types are ignored.
+	 *            
+	 * @return a filter group that corresponds to the include or exclude
+	 *         element, or null if the element was not an include or exclude
+	 *         element.
+	 */
+	private FilterGroup createFilterGroup(IConfigurationElement group){
 		FilterGroup fg = FilterGroup.create(group.getName());
-		if (fg == null)return;			
+		if (fg == null)return null;			
 		
 		IConfigurationElement[] rules = group.getChildren(ExtensionConstants.rules);
 		// there should only be one
@@ -172,7 +256,7 @@
 				processRule(fg, r[j]);
 			}
 		}
-		v.add(fg);
+		return fg;
 	}
 
 	/**
diff --git a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/model/FilterGroup.java b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/model/FilterGroup.java
index 3fc39df..f544156 100644
--- a/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/model/FilterGroup.java
+++ b/plugins/org.eclipse.wst.validation/vf2/org/eclipse/wst/validation/internal/model/FilterGroup.java
@@ -123,7 +123,7 @@
 			return ValMessages.GroupInclude;
 		}
 		
-		protected boolean isInclude() {
+		public boolean isInclude() {
 			return true;
 		}
 		
@@ -147,7 +147,7 @@
 			return ValMessages.GroupExclude;
 		}
 		
-		protected boolean isExclude() {
+		public boolean isExclude() {
 			return true;
 		}		
 	}
@@ -202,7 +202,7 @@
 	 * Answer true if this is an inclusion filter, that is at least one of the rules must
 	 * match in order to validate the resource.
 	 */
-	protected boolean isInclude() {
+	public boolean isInclude() {
 		return false;
 	}
 
@@ -210,7 +210,7 @@
 	 * Answer true if this is an exclusion filter, that is if any of the rules match the 
 	 * resource is not validated.
 	 */
-	protected boolean isExclude() {
+	public boolean isExclude() {
 		return false;
 	}
 	
diff --git a/plugins/org.eclipse.wst.validation/xsds/exclude.exsd b/plugins/org.eclipse.wst.validation/xsds/exclude.exsd
new file mode 100644
index 0000000..3ed9a96
--- /dev/null
+++ b/plugins/org.eclipse.wst.validation/xsds/exclude.exsd
@@ -0,0 +1,256 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.wst.validation" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appinfo>
+         <meta.schema plugin="org.eclipse.wst.validation" id="exclude" name="Validator Exclusion"/>
+      </appinfo>
+      <documentation>
+         A way to add additional exclusion filters to a V2 validator.
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="validator" minOccurs="1" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  A short id that is unique within the plug-in, so that this set of extra exclusion filters can be identified.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  The short user friendly name for the exclusion.
+               </documentation>
+               <appinfo>
+                  <meta.attribute translatable="true"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="validator">
+      <annotation>
+         <documentation>
+            Identifies the validator that is being extended.
+         </documentation>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="exclude"/>
+         </sequence>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The fully qualifed validator id that is being extended.
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="identifier"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="exclude">
+      <annotation>
+         <documentation>
+            This contains a set of exclusion rules, used in filtering which resources get validated. If any of these rules are matched, then the validator is not called on this resource.
+         </documentation>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="rules"/>
+         </sequence>
+      </complexType>
+   </element>
+
+   <element name="fileext">
+      <annotation>
+         <documentation>
+            A file extension, for example html.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="ext" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The file extension, for example html.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="caseSensitive" type="boolean">
+            <annotation>
+               <documentation>
+                  If the file extension is case sensitive, then this needs to be set to true.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="projectNature">
+      <annotation>
+         <documentation>
+            Used to filter on project natures.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The fully qualified project nature id, for example &quot;org.eclipse.jdt.core.javanature&quot;.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="file">
+      <complexType>
+         <attribute name="name" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The file or folder name. The file name can be fully qualified, that is it starts with a leading slash (&quot;/&quot;), or it can be a relative name. Folder names are separated with the forward slash (&quot;/&quot;) character.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="caseSensitive" type="boolean">
+            <annotation>
+               <documentation>
+                  If the file name is not case senstitive, then this needs to be set to false.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="type" use="required">
+            <annotation>
+               <documentation>
+                  Type of name, there are three choices. &lt;ul&gt;&lt;li&gt;folder - specifies a project relative folder name.&lt;/li&gt;&lt;li&gt;file - a simple file name (i.e. no path information).&lt;/li&gt;&lt;li&gt;full - a fully qualified (project relative) file name.&lt;/li&gt;&lt;/ul&gt;
+               </documentation>
+            </annotation>
+            <simpleType>
+               <restriction base="string">
+                  <enumeration value="folder">
+                  </enumeration>
+                  <enumeration value="file">
+                  </enumeration>
+                  <enumeration value="full">
+                  </enumeration>
+               </restriction>
+            </simpleType>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="rules">
+      <annotation>
+         <documentation>
+            A set of rules for determining if a resource is passed to this validator or not. The rules are OR&apos;ed together.
+         </documentation>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="fileext" minOccurs="0" maxOccurs="unbounded"/>
+            <element ref="file" minOccurs="0" maxOccurs="unbounded"/>
+            <element ref="projectNature" minOccurs="0" maxOccurs="unbounded"/>
+            <element ref="facet" minOccurs="0" maxOccurs="unbounded"/>
+            <element ref="contentType" minOccurs="0" maxOccurs="unbounded"/>
+         </sequence>
+      </complexType>
+   </element>
+
+   <element name="facet">
+      <complexType>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The facet id.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="contentType">
+      <annotation>
+         <documentation>
+            Used to filter based on content types.
+         </documentation>
+      </annotation>
+      <complexType>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  The content type id.
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="exactMatch" type="boolean">
+            <annotation>
+               <documentation>
+                  Set to true if the content type needs to match exactly. Set to false if sub types are considered matches as well.
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="since"/>
+      </appinfo>
+      <documentation>
+         WebTools 3.0
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="examples"/>
+      </appinfo>
+      <documentation>
+         &lt;!-- A plug-in is turning off the T1A validator for projects that have the ModuleCoreNature --&gt;
+ &lt;extension
+       id=&quot;exclude1&quot;
+       point=&quot;org.eclipse.wst.validation.exclude&quot;&gt;
+    &lt;validator
+          id=&quot;org.eclipse.wst.common.tests.validation.T1A&quot;&gt;
+       &lt;exclude&gt;
+          &lt;rules&gt;
+             &lt;projectNature
+                   id=&quot;org.eclipse.jst.j2ee.ModuleCoreNature&quot;&gt;
+             &lt;/projectNature&gt;
+          &lt;/rules&gt;
+       &lt;/exclude&gt;
+    &lt;/validator&gt;
+ &lt;/extension&gt;
+
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="apiInfo"/>
+      </appinfo>
+      <documentation>
+         Provisional API.
+      </documentation>
+   </annotation>
+
+
+
+
+</schema>