[160548] JSP validator does not mark the non-existing attribute or the missing required attribute
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
index 03fcf13..3d74cb7 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
@@ -25,6 +25,8 @@
 	public static String JSPDirectiveValidator_2;
 	public static String JSPDirectiveValidator_3;
 	public static String JSPDirectiveValidator_4;
+	public static String JSPDirectiveValidator_5;
+	public static String JSPDirectiveValidator_6;
 	public static String JSPIndexManager_0;
 	public static String JSPIndexManager_2;
 	public static String JSP_Search;
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
index d40799b..1129ea1 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
@@ -22,4 +22,6 @@
 JSPDirectiveValidator_2=The prefix {0} is used more than once
 JSPDirectiveValidator_3=A {0} value is required in this directive
 JSPDirectiveValidator_4=Fragment {0} was not be found at expected path {1}
+JSPDirectiveValidator_5=Missing required attribute "{0}"
+JSPDirectiveValidator_6=Undefined attribute name ({0})
 JSPBatchValidator_0=Gathering files in {0}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java
new file mode 100644
index 0000000..04450e3
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.jst.jsp.core.internal.validation;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
+import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager;
+import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TaglibTracker;
+import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.validation.internal.core.ValidationException;
+import org.eclipse.wst.validation.internal.provisional.core.IMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
+import org.eclipse.wst.validation.internal.provisional.core.IValidator;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
+import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
+import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ * Checks for: - missing required attributes & undefined attributes in jsp
+ * action tags such as jsp directives and jsp custom tags
+ */
+public class JSPActionValidator extends JSPValidator {
+	private int fSeverityMissingRequiredAttribute = IMessage.HIGH_SEVERITY;
+	private int fSeverityUnknownAttribute = IMessage.NORMAL_SEVERITY;
+	private IValidator fMessageOriginator;
+	private HashSet fTaglibPrefixes = new HashSet();
+
+	public JSPActionValidator() {
+		this.fMessageOriginator = this;
+	}
+
+	public JSPActionValidator(IValidator validator) {
+		this.fMessageOriginator = validator;
+	}
+
+	private void checkRequiredAttributes(IDOMElement element, CMNamedNodeMap attrMap, IReporter reporter, IFile file, IStructuredDocument document, IStructuredDocumentRegion documentRegion) {
+		Iterator it = attrMap.iterator();
+		CMAttributeDeclaration attr = null;
+		while (it.hasNext()) {
+			attr = (CMAttributeDeclaration) it.next();
+			if (attr.getUsage() == CMAttributeDeclaration.REQUIRED) {
+				Attr a = element.getAttributeNode(attr.getAttrName());
+				if (a == null) {
+					String msgText = NLS.bind(JSPCoreMessages.JSPDirectiveValidator_5, attr.getAttrName());
+					LocalizedMessage message = new LocalizedMessage(fSeverityMissingRequiredAttribute, msgText, file);
+					int start = element.getStartOffset();
+					int length = element.getStartEndOffset() - start;
+					int lineNo = document.getLineOfOffset(start);
+					message.setLineNo(lineNo);
+					message.setOffset(start);
+					message.setLength(length);
+
+					reporter.addMessage(fMessageOriginator, message);
+				}
+			}
+		}
+	}
+
+	private boolean checkUnknownAttributes(IDOMElement element, CMNamedNodeMap cmAttrs, IReporter reporter, IFile file, IStructuredDocument document, IStructuredDocumentRegion documentRegion) {
+		boolean foundjspattribute = false;
+
+		NamedNodeMap attrs = element.getAttributes();
+		for (int i = 0; i < attrs.getLength(); i++) {
+			Attr a = (Attr) attrs.item(i);
+			CMAttributeDeclaration adec = (CMAttributeDeclaration) cmAttrs.getNamedItem(a.getName());
+			if (adec == null) {
+				// No attr declaration was found. That is, the attr name is
+				// undefined.
+				// but not regard it as undefined name if it includes JSP
+				if (!hasJSPRegion(((IDOMNode) a).getNameRegion())) {
+					String msgText = NLS.bind(JSPCoreMessages.JSPDirectiveValidator_6, a.getName());
+					LocalizedMessage message = new LocalizedMessage(fSeverityUnknownAttribute, msgText, file);
+					int start = ((IDOMAttr) a).getNameRegionStartOffset();
+					int length = ((IDOMAttr) a).getNameRegionEndOffset() - start;
+					int lineNo = document.getLineOfOffset(start);
+					message.setLineNo(lineNo);
+					message.setOffset(start);
+					message.setLength(length);
+
+					reporter.addMessage(fMessageOriginator, message);
+				}
+				else {
+					foundjspattribute = true;
+				}
+			}
+		}
+		return foundjspattribute;
+	}
+
+	public void cleanup(IReporter reporter) {
+		super.cleanup(reporter);
+		fTaglibPrefixes.clear();
+	}
+
+	private String getStartTagName(IStructuredDocumentRegion sdr) {
+		String name = new String();
+		ITextRegionList subRegions = sdr.getRegions();
+		if (subRegions.size() > 2) {
+			ITextRegion subRegion = subRegions.get(0);
+			if (subRegion.getType() == DOMRegionContext.XML_TAG_OPEN) {
+				subRegion = subRegions.get(1);
+				if (subRegion.getType() == DOMRegionContext.XML_TAG_NAME) {
+					name = sdr.getText(subRegion);
+				}
+			}
+		}
+		return name;
+	}
+
+	private HashSet getTaglibPrefixes(IStructuredDocument document) {
+		if (fTaglibPrefixes.isEmpty()) {
+			// add all reserved prefixes
+			fTaglibPrefixes.add("jsp"); //$NON-NLS-1$
+			fTaglibPrefixes.add("jspx"); //$NON-NLS-1$
+			fTaglibPrefixes.add("java"); //$NON-NLS-1$
+			fTaglibPrefixes.add("javax"); //$NON-NLS-1$ 
+			fTaglibPrefixes.add("servlet"); //$NON-NLS-1$ 
+			fTaglibPrefixes.add("sun"); //$NON-NLS-1$ 
+			fTaglibPrefixes.add("sunw"); //$NON-NLS-1$
+
+			// add all taglib prefixes
+			TLDCMDocumentManager manager = TaglibController.getTLDCMDocumentManager(document);
+			List trackers = manager.getTaglibTrackers();
+			for (Iterator it = trackers.iterator(); it.hasNext();) {
+				TaglibTracker tracker = (TaglibTracker) it.next();
+				String prefix = tracker.getPrefix();
+				fTaglibPrefixes.add(prefix);
+			}
+		}
+		return fTaglibPrefixes;
+	}
+
+	private boolean hasJSPRegion(ITextRegion container) {
+		if (!(container instanceof ITextRegionContainer))
+			return false;
+		ITextRegionList regions = ((ITextRegionContainer) container).getRegions();
+		if (regions == null)
+			return false;
+		Iterator e = regions.iterator();
+		while (e.hasNext()) {
+			ITextRegion region = (ITextRegion) e.next();
+			if (region == null)
+				continue;
+			String regionType = region.getType();
+			if (regionType == DOMRegionContext.XML_TAG_OPEN || (isNestedTagName(regionType)))
+				return true;
+		}
+		return false;
+	}
+
+	private boolean isNestedTagName(String regionType) {
+		boolean result = regionType.equals(DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_DECLARATION_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN);
+		return result;
+	}
+
+	void performValidation(IFile f, IReporter reporter, IStructuredModel model) {
+		fTaglibPrefixes.clear();
+		int length = model.getStructuredDocument().getLength();
+		performValidation(f, reporter, model, new Region(0, length));
+	}
+
+	protected void performValidation(IFile f, IReporter reporter, IStructuredModel model, IRegion validateRegion) {
+		IStructuredDocument sDoc = model.getStructuredDocument();
+
+		// iterate all document regions
+		IStructuredDocumentRegion region = sDoc.getRegionAtCharacterOffset(validateRegion.getOffset());
+		while (region != null && !reporter.isCancelled() && (region.getStartOffset() <= (validateRegion.getOffset() + validateRegion.getLength()))) {
+			if (region.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) {
+				// only checking directives
+				processDirective(reporter, f, model, region);
+				fTaglibPrefixes.clear();
+			}
+			else if (region.getType() == DOMRegionContext.XML_TAG_NAME) {
+				// and jsp tags
+				String tagName = getStartTagName(region);
+				int colonPosition = tagName.indexOf(':');
+				if (colonPosition > -1) {
+					// get tag's prefix and check if it's really a jsp action
+					// tag
+					String prefix = tagName.substring(0, colonPosition);
+					if (getTaglibPrefixes(sDoc).contains(prefix))
+						processDirective(reporter, f, model, region);
+				}
+			}
+			region = region.getNext();
+		}
+	}
+
+	private void processDirective(IReporter reporter, IFile file, IStructuredModel model, IStructuredDocumentRegion documentRegion) {
+		IndexedRegion ir = model.getIndexedRegion(documentRegion.getStartOffset());
+		if (ir instanceof IDOMElement) {
+			IDOMElement element = (IDOMElement) ir;
+			ModelQuery query = ModelQueryUtil.getModelQuery(model);
+			if (query != null) {
+				CMElementDeclaration cmElement = query.getCMElementDeclaration(element);
+				if (cmElement != null) {
+					CMNamedNodeMap cmAttributes = cmElement.getAttributes();
+
+					boolean foundjspattribute = checkUnknownAttributes(element, cmAttributes, reporter, file, model.getStructuredDocument(), documentRegion);
+					// required attributes could be hidden in jsp regions in
+					// tags, so if jsp regions were detected, do not check for
+					// missing required attributes
+					if (!foundjspattribute)
+						checkRequiredAttributes(element, cmAttributes, reporter, file, model.getStructuredDocument(), documentRegion);
+				}
+			}
+		}
+	}
+
+	public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
+		reporter.removeAllMessages(this);
+		super.validate(helper, reporter);
+	}
+
+	protected void validateFile(IFile f, IReporter reporter) {
+		if (DEBUG) {
+			Logger.log(Logger.INFO, getClass().getName() + " validating: " + f); //$NON-NLS-1$
+		}
+
+		IStructuredModel sModel = null;
+		try {
+			sModel = StructuredModelManager.getModelManager().getModelForRead(f);
+			if (sModel != null && !reporter.isCancelled()) {
+				performValidation(f, reporter, sModel);
+			}
+		}
+		catch (Exception e) {
+			Logger.logException(e);
+		}
+		finally {
+			if (sModel != null)
+				sModel.releaseFromRead();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java
index f4b5ba5..63357a3 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java
@@ -163,11 +163,14 @@
 
 	private JSPJavaValidator fJSPJavaValidator = new JSPJavaValidator(this);
 
+	private JSPActionValidator fJSPActionValidator = new JSPActionValidator(this);
+
 
 	public void cleanup(IReporter reporter) {
 		fJSPDirectiveValidator.cleanup(reporter);
 		fJSPELValidator.cleanup(reporter);
 		fJSPJavaValidator.cleanup(reporter);
+		fJSPActionValidator.cleanup(reporter);
 	}
 
 
@@ -182,7 +185,7 @@
 				currentFile = wsRoot.getFile(new Path(uris[i]));
 				if (currentFile != null && currentFile.exists()) {
 					if (shouldValidate(currentFile) && fragmentCheck(currentFile)) {
-						Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i+1) + "/" + uris.length + " - " + currentFile.getFullPath().toString().substring(1));
+						Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i + 1) + "/" + uris.length + " - " + currentFile.getFullPath().toString().substring(1));
 						reporter.displaySubtask(this, message);
 						validateFile(currentFile, reporter);
 					}
@@ -195,7 +198,7 @@
 			// if uris[] length 0 -> validate() gets called for each project
 			if (helper instanceof IWorkbenchContext) {
 				IProject project = ((IWorkbenchContext) helper).getProject();
-				
+
 				Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, NLS.bind(JSPCoreMessages.JSPBatchValidator_0, project.getFullPath()));
 				reporter.displaySubtask(this, message);
 
@@ -212,7 +215,7 @@
 				for (int i = 0; i < files.length && !reporter.isCancelled(); i++) {
 					if (shouldValidate(files[i]) && fragmentCheck(files[i])) {
 
-						message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i+1) + "/" + files.length + " - " + files[i].getFullPath().toString().substring(1));
+						message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i + 1) + "/" + files.length + " - " + files[i].getFullPath().toString().substring(1));
 						reporter.displaySubtask(this, message);
 
 						validateFile(files[i], reporter);
@@ -370,6 +373,8 @@
 			fJSPDirectiveValidator.performValidation(f, reporter, model.getStructuredDocument());
 		if (!reporter.isCancelled())
 			fJSPELValidator.performValidation(f, reporter, model.getStructuredDocument());
+		if (!reporter.isCancelled())
+			fJSPActionValidator.performValidation(f, reporter, model);
 	}
 
 	/**
diff --git a/bundles/org.eclipse.jst.jsp.ui/plugin.xml b/bundles/org.eclipse.jst.jsp.ui/plugin.xml
index 782dc7f..eca333b 100644
--- a/bundles/org.eclipse.jst.jsp.ui/plugin.xml
+++ b/bundles/org.eclipse.jst.jsp.ui/plugin.xml
@@ -118,6 +118,23 @@
 		</validator>
 	</extension>
 	<!--======================================================================================-->
+	<!-- source (as you type) validation for JSP action tags			   					  -->
+	<!--======================================================================================-->
+	<extension point="org.eclipse.wst.sse.ui.sourcevalidation">
+		<validator
+			scope="partial"
+			class="org.eclipse.jst.jsp.ui.internal.validation.JSPActionSourceValidator"
+			id="org.eclipse.jst.jsp.ui.internal.validation.jspactionvalidator">
+			<contentTypeIdentifier
+				id="org.eclipse.jst.jsp.core.jspsource">
+				<partitionType id="org.eclipse.jst.jsp.DEFAULT_JSP">
+				</partitionType>
+				<partitionType id="org.eclipse.jst.jsp.JSP_DIRECTIVE">
+				</partitionType>
+			</contentTypeIdentifier>
+		</validator>
+	</extension>
+	<!--======================================================================================-->
 	<!-- custom XML source (as you type) validation					   						  -->
 	<!--======================================================================================-->
 	<extension point="org.eclipse.wst.sse.ui.sourcevalidation">
diff --git a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/validation/JSPActionSourceValidator.java b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/validation/JSPActionSourceValidator.java
new file mode 100644
index 0000000..0ab20f9
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/validation/JSPActionSourceValidator.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.jst.jsp.ui.internal.validation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentProperties;
+import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
+import org.eclipse.jst.jsp.core.internal.validation.JSPActionValidator;
+import org.eclipse.jst.jsp.core.internal.validation.JSPContentValidator;
+import org.eclipse.jst.jsp.ui.internal.Logger;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.ui.internal.reconcile.validator.ISourceValidator;
+import org.eclipse.wst.validation.internal.ConfigurationManager;
+import org.eclipse.wst.validation.internal.ProjectConfiguration;
+import org.eclipse.wst.validation.internal.ValidationConfiguration;
+import org.eclipse.wst.validation.internal.ValidationRegistryReader;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
+
+/**
+ * Source validator (able to check partial document) that checks for: -
+ * missing required attributes & undefined attributes in jsp action tags such
+ * as jsp directives and jsp custom tags
+ */
+public class JSPActionSourceValidator extends JSPActionValidator implements ISourceValidator {
+	private IDocument fDocument;
+	private boolean fEnableSourceValidation;
+	private IContentType fJSPFContentType = null;
+
+	public void connect(IDocument document) {
+		fDocument = document;
+
+		// special checks to see source validation should really execute
+		IFile file = null;
+		IStructuredModel model = null;
+		try {
+			model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
+			if (model != null) {
+				String baseLocation = model.getBaseLocation();
+				// The baseLocation may be a path on disk or relative to the
+				// workspace root. Don't translate on-disk paths to
+				// in-workspace resources.
+				IPath basePath = new Path(baseLocation);
+				if (basePath.segmentCount() > 1) {
+					file = ResourcesPlugin.getWorkspace().getRoot().getFile(basePath);
+					/*
+					 * If the IFile doesn't exist, make sure it's not returned
+					 */
+					if (!file.exists())
+						file = null;
+				}
+			}
+		}
+		finally {
+			if (model != null) {
+				model.releaseFromRead();
+			}
+		}
+		fEnableSourceValidation = (file != null && isBatchValidatorPreferenceEnabled(file) && shouldValidate(file) && fragmentCheck(file));
+	}
+
+	public void disconnect(IDocument document) {
+		fDocument = null;
+	}
+
+	public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
+		if (helper == null || fDocument == null || !fEnableSourceValidation)
+			return;
+
+		if ((reporter != null) && (reporter.isCancelled() == true)) {
+			throw new OperationCanceledException();
+		}
+
+		IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(fDocument);
+		if (model == null)
+			return;
+
+		try {
+			ITextFileBuffer fb = FileBufferModelManager.getInstance().getBuffer(fDocument);
+			if (fb == null)
+				return;
+			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fb.getLocation());
+			if (file == null || !file.exists())
+				return;
+			performValidation(file, reporter, model, dirtyRegion);
+		}
+		finally {
+			if (model != null)
+				model.releaseFromRead();
+		}
+	}
+
+	/**
+	 * Gets current validation configuration based on current project (which
+	 * is based on current document) or global configuration if project does
+	 * not override
+	 * 
+	 * @return ValidationConfiguration
+	 */
+	private ValidationConfiguration getValidationConfiguration(IFile file) {
+		ValidationConfiguration configuration = null;
+		if (file != null) {
+			IProject project = file.getProject();
+			if (project != null) {
+				try {
+					ProjectConfiguration projectConfiguration = ConfigurationManager.getManager().getProjectConfiguration(project);
+					configuration = projectConfiguration;
+					if (projectConfiguration == null || projectConfiguration.useGlobalPreference()) {
+						configuration = ConfigurationManager.getManager().getGlobalConfiguration();
+					}
+				}
+				catch (InvocationTargetException e) {
+					Logger.log(Logger.WARNING_DEBUG, e.getMessage(), e);
+				}
+			}
+		}
+
+		return configuration;
+	}
+
+	/**
+	 * Checks if validator is enabled according in Validation preferences
+	 * 
+	 * @param vmd
+	 * @return
+	 */
+	private boolean isBatchValidatorPreferenceEnabled(IFile file) {
+		if (file == null) {
+			return true;
+		}
+
+		boolean enabled = true;
+		ValidationConfiguration configuration = getValidationConfiguration(file);
+		if (configuration != null) {
+			org.eclipse.wst.validation.internal.ValidatorMetaData metadata = ValidationRegistryReader.getReader().getValidatorMetaData(JSPContentValidator.class.getName());
+			if (metadata != null) {
+				if (!configuration.isBuildEnabled(metadata) && !configuration.isManualEnabled(metadata))
+					enabled = false;
+			}
+		}
+		return enabled;
+	}
+
+	/**
+	 * Checks if file is a jsp fragment or not. If so, check if the fragment
+	 * should be validated or not.
+	 * 
+	 * @param file
+	 *            Assumes shouldValidate was already called on file so it
+	 *            should not be null and does exist
+	 * @return false if file is a fragment and it should not be validated,
+	 *         true otherwise
+	 */
+	private boolean fragmentCheck(IFile file) {
+		// copied from JSPValidator
+		boolean shouldValidate = true;
+		// quick check to see if this is possibly a jsp fragment
+		if (getJSPFContentType().isAssociatedWith(file.getName())) {
+			// get preference for validate jsp fragments
+			boolean shouldValidateFragments = Boolean.valueOf(JSPFContentProperties.getProperty(JSPFContentProperties.VALIDATE_FRAGMENTS, file, true)).booleanValue();
+			/*
+			 * if jsp fragments should not be validated, check if file is
+			 * really jsp fragment
+			 */
+			if (!shouldValidateFragments) {
+				boolean isFragment = isFragment(file);
+				shouldValidate = !isFragment;
+			}
+		}
+		return shouldValidate;
+	}
+
+	/**
+	 * Determines if file is jsp fragment or not (does a deep, indepth check,
+	 * looking into contents of file)
+	 * 
+	 * @param file
+	 *            assumes file is not null and exists
+	 * @return true if file is jsp fragment, false otherwise
+	 */
+	private boolean isFragment(IFile file) {
+		// copied from JSPValidator
+		boolean isFragment = false;
+		InputStream is = null;
+		try {
+			IContentDescription contentDescription = file.getContentDescription();
+			// it can be null
+			if (contentDescription == null) {
+				is = file.getContents();
+				contentDescription = Platform.getContentTypeManager().getDescriptionFor(is, file.getName(), new QualifiedName[]{IContentDescription.CHARSET});
+			}
+			if (contentDescription != null) {
+				String fileCtId = contentDescription.getContentType().getId();
+				isFragment = (fileCtId != null && ContentTypeIdForJSP.ContentTypeID_JSPFRAGMENT.equals(fileCtId));
+			}
+		}
+		catch (IOException e) {
+			// ignore, assume it's invalid JSP
+		}
+		catch (CoreException e) {
+			// ignore, assume it's invalid JSP
+		}
+		finally {
+			// must close input stream in case others need it
+			if (is != null)
+				try {
+					is.close();
+				}
+				catch (Exception e) {
+					// not sure how to recover at this point
+				}
+		}
+		return isFragment;
+	}
+
+	private boolean shouldValidate(IFile file) {
+		// copied from JSPValidator
+		IResource resource = file;
+		do {
+			if (resource.isDerived() || resource.isTeamPrivateMember() || !resource.isAccessible() || resource.getName().charAt(0) == '.') {
+				return false;
+			}
+			resource = resource.getParent();
+		}
+		while ((resource.getType() & IResource.PROJECT) == 0);
+		return true;
+	}
+
+	/**
+	 * Returns JSP fragment content type
+	 * 
+	 * @return jspf content type
+	 */
+	private IContentType getJSPFContentType() {
+		// copied from JSPValidator
+		if (fJSPFContentType == null) {
+			fJSPFContentType = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSPFRAGMENT);
+		}
+		return fJSPFContentType;
+	}
+}