[197578] [hotbug] JSP validator does not mark the non-existing attribute or the missing required attribute (WTP 1.5.5)
diff --git a/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF
index c029848..e33d3cb 100644
--- a/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.jst.jsp.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jst.jsp.core; singleton:=true
-Bundle-Version: 1.1.104.qualifier
+Bundle-Version: 1.1.105.qualifier
 Bundle-Activator: org.eclipse.jst.jsp.core.internal.JSPCorePlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
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..5f9e0c7
--- /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.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.html.core.internal.HTMLCoreMessages;
+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(HTMLCoreMessages.Undefined_attribute_name___ERROR_, 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(HTMLCoreMessages.Undefined_attribute_name___ERROR_, 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 55eba8d..f6e7a24 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
@@ -1,3 +1,14 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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.io.IOException;
@@ -190,6 +201,7 @@
 	private JSPDirectiveValidator directiveValidator = new JSPDirectiveValidator(this);
 	private JSPELValidator elValidator = new JSPELValidator(this);
 	private JSPJavaValidator jspJavaValidator = new JSPJavaValidator(this);
+	private JSPActionValidator fJSPActionValidator = new JSPActionValidator(this);
 
 	String fAdditionalContentTypesIDs[] = null;
 
@@ -197,6 +209,7 @@
 		directiveValidator.cleanup(reporter);
 		elValidator.cleanup(reporter);
 		jspJavaValidator.cleanup(reporter);
+		fJSPActionValidator.cleanup(reporter);
 	}
 
 	void doValidate(IValidationContext helper, IReporter reporter) throws ValidationException {
@@ -393,9 +406,14 @@
 	}
 
 	private void performValidation(IFile f, IReporter reporter, IStructuredModel model) {
-		directiveValidator.performValidation(f, reporter, model.getStructuredDocument());
-		elValidator.performValidation(f, reporter, model.getStructuredDocument());
-		jspJavaValidator.performValidation(f, reporter, model);
+		if (!reporter.isCancelled())
+			directiveValidator.performValidation(f, reporter, model.getStructuredDocument());
+		if (!reporter.isCancelled())
+			elValidator.performValidation(f, reporter, model.getStructuredDocument());
+		if (!reporter.isCancelled())
+			jspJavaValidator.performValidation(f, reporter, model);
+		if (!reporter.isCancelled())
+			fJSPActionValidator.performValidation(f, reporter, model);
 	}
 
 	/**
diff --git a/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF
index e68d849..686f50f 100644
--- a/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.jst.jsp.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jst.jsp.ui; singleton:=true
-Bundle-Version: 1.1.100.qualifier
+Bundle-Version: 1.1.101.qualifier
 Bundle-Activator: org.eclipse.jst.jsp.ui.internal.JSPUIPlugin
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
@@ -60,5 +60,6 @@
  org.eclipse.ui.views;bundle-version="[3.2.0,3.3.0)",
  org.eclipse.wst.common.project.facet.core;bundle-version="[1.1.0,1.2.0)",
  org.eclipse.wst.common.modulecore;bundle-version="[1.1.0,1.2.0)",
- com.ibm.icu;bundle-version="[3.4.4,3.5.0)"
+ com.ibm.icu;bundle-version="[3.4.4,3.5.0)",
+ org.eclipse.wst.validation;bundle-version="[1.1.0,1.2.0)"
 Eclipse-LazyStart: true
diff --git a/bundles/org.eclipse.jst.jsp.ui/plugin.xml b/bundles/org.eclipse.jst.jsp.ui/plugin.xml
index 83d2e58..3560855 100644
--- a/bundles/org.eclipse.jst.jsp.ui/plugin.xml
+++ b/bundles/org.eclipse.jst.jsp.ui/plugin.xml
@@ -84,8 +84,19 @@
 				<partitionType id="org.eclipse.wst.sse.ST_DEFAULT"/>
 			</contentTypeIdentifier>
 		</validator>
+		<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>
-
 	<extension
 		point="org.eclipse.wst.sse.ui.adapterFactoryDescription">
 		<adapterFactoryDescription
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..8262bda
--- /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.JSPBatchValidator;
+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(JSPBatchValidator.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;
+	}
+}