Index: src/org/jboss/tools/jsf/web/validation/XHTMLValidator.java
===================================================================
--- src/org/jboss/tools/jsf/web/validation/XHTMLValidator.java	(revision 35418)
+++ src/org/jboss/tools/jsf/web/validation/XHTMLValidator.java	(working copy)
@@ -12,11 +12,16 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringReader;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Stack;
 
+import org.apache.xerces.xni.XMLResourceIdentifier;
+import org.apache.xerces.xni.XNIException;
+import org.apache.xerces.xni.parser.XMLEntityResolver;
+import org.apache.xerces.xni.parser.XMLInputSource;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -33,11 +38,15 @@
 import org.eclipse.wst.xml.core.internal.validation.core.ValidationMessage;
 import org.eclipse.wst.xml.core.internal.validation.core.ValidationReport;
 import org.eclipse.wst.xml.core.internal.validation.eclipse.Validator;
+import org.jboss.tools.common.CommonPlugin;
 import org.jboss.tools.common.util.FileUtil;
 import org.jboss.tools.jsf.JSFModelPlugin;
 import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
 import org.xml.sax.Locator;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
 import org.xml.sax.SAXParseException;
 import org.xml.sax.XMLReader;
 import org.xml.sax.ext.LexicalHandler;
@@ -59,21 +68,21 @@
 	IProgressMonitor monitor;
 	IResource resource;
 	
-	public boolean isXHTMLDoctype = false;
-
 	private String[] SAX_PARSER_FEATURES_TO_DISABLE = {
 			"http://xml.org/sax/features/namespaces", 
 	    	"http://xml.org/sax/features/use-entity-resolver2",
 			"http://xml.org/sax/features/validation",
 			"http://apache.org/xml/features/validation/dynamic",
 			"http://apache.org/xml/features/validation/schema",
-			"http://apache.org/xml/features/validation/schema-full-checking",		
+			"http://apache.org/xml/features/validation/schema-full-checking",
 			"http://apache.org/xml/features/nonvalidating/load-external-dtd",
 			"http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
 			"http://apache.org/xml/features/xinclude",
-			"http://xml.org/sax/features/resolve-dtd-uris",
+			"http://xml.org/sax/features/resolve-dtd-uris"
+	};  
+	private String[] SAX_PARSER_FEATURES_TO_ENABLE = {
 			"http://apache.org/xml/features/continue-after-fatal-error"
-		};  
+		};
 	
 	private void setSAXParserFeatures(XMLReader reader, String[] features, boolean set) {
 		for (String feature : features) {
@@ -132,27 +141,44 @@
 			NestedValidatorContext context, ValidationResult result) {
 		displaySubtask(JSFValidationMessage.XHTML_VALIDATION, uri);
 		XMLValidationInfo report = new XMLValidationInfo(uri);
-		XHTMLElementHandler handler = new XHTMLElementHandler(
+		XHTMLElementHandler handler = new XHTMLElementHandler(uri,
 				(resource instanceof IFile ? getDocument((IFile)resource) : null),
 				report);
-		XMLReader xmlReader = new org.apache.xerces.parsers.SAXParser();
+		
+		try {
+			if (!handler.isWellFormedXHTML()) {
+				SAXParseException ex = handler.getException();
+				if (ex != null) {
+					JSFModelPlugin.getDefault().logError(ex);
+					report.addError(ex.getLocalizedMessage(), ex.getLineNumber(), ex.getColumnNumber(), uri);
+				}
+				return report;
+			}
+			
+			XMLReader xmlReader = new org.apache.xerces.parsers.SAXParser();
 
-		setSAXParserFeatures(xmlReader, SAX_PARSER_FEATURES_TO_DISABLE, false);
-	    setSAXParserProperty(xmlReader, "http://xml.org/sax/properties/lexical-handler", handler); 
-	    xmlReader.setContentHandler(handler);
-	    xmlReader.setDTDHandler(handler);
-	    xmlReader.setErrorHandler(handler);
-	    isXHTMLDoctype = false;
+			setSAXParserFeatures(xmlReader, SAX_PARSER_FEATURES_TO_DISABLE, false);
+			setSAXParserFeatures(xmlReader, SAX_PARSER_FEATURES_TO_ENABLE, true);
+    	    setSAXParserProperty(xmlReader, "http://xml.org/sax/properties/lexical-handler", handler); 
+    	    
+			xmlReader.setProperty("http://apache.org/xml/properties/internal/entity-resolver", new NullXMLEntityResolver());
 
-		try {
+    	    xmlReader.setContentHandler(handler);
+    	    xmlReader.setDTDHandler(handler);
+    	    xmlReader.setErrorHandler(handler);
+    	    xmlReader.setEntityResolver(handler);
+		
 			xmlReader.parse(uri);
 		} catch (IOException e) {
 			JSFModelPlugin.getDefault().logError(e);
 			report.addError(e.getLocalizedMessage(), 0, 0, uri);
+		} catch (SAXNotRecognizedException e) {
+			JSFModelPlugin.getDefault().logError(e);
+		} catch (SAXNotSupportedException e) {
+			JSFModelPlugin.getDefault().logError(e);
 		} catch (SAXException e) {
 			report.addError(e.getLocalizedMessage(), 0, 0, uri);
 		}
-
 		List<ElementLocation> locations = handler.getNonPairedOpenElements();
 		if (!locations.isEmpty()) {
 			for (ElementLocation location : locations) {
@@ -171,7 +197,7 @@
 		
 		return report;
 	}
-	
+		
 	@Override
 	protected void addInfoToMessage(ValidationMessage validationMessage,
 			IMessage message) {
@@ -272,17 +298,33 @@
 		}
 	}
 
+	class NullXMLEntityResolver implements XMLEntityResolver {
+
+		public XMLInputSource resolveEntity(XMLResourceIdentifier rid)
+				throws XNIException, IOException {
+			
+			return new XMLInputSource(rid.getPublicId(), 
+					rid.getBaseSystemId()==null?rid.getLiteralSystemId():rid.getExpandedSystemId(), 
+							rid.getBaseSystemId(), new StringReader(""), null);
+		}
+		
+	}
+	
 	class XHTMLElementHandler extends DefaultHandler implements LexicalHandler {
+		private String uri;
 		private Locator locator;
 		private IDocument document;
 		private XMLValidationInfo valinfo;
+		public boolean isXHTMLDoctype = false;
+		private boolean isWellFormed = false;
 		  
 		List<ElementLocation> elements = new ArrayList<ElementLocation>();
 		Stack<ElementLocation> nonPairedOpenElements = new Stack<XHTMLValidator.ElementLocation>();
 		Stack<ElementLocation> nonPairedCloseElements = new Stack<XHTMLValidator.ElementLocation>();
 		 		  
-		public XHTMLElementHandler(IDocument document, XMLValidationInfo valinfo) {
+		public XHTMLElementHandler(String uri, IDocument document, XMLValidationInfo valinfo) {
 			super();
+			this.uri = uri;
 			this.document = document;
 			this.valinfo = valinfo;
 		}
@@ -294,7 +336,7 @@
 		  
 		@Override
 		public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
-			if (!isXHTMLDoctype)
+			if (!isWellFormed || !isXHTMLDoctype)
 				return;
 
 			int end = getCurrentLocation(), start = 0;
@@ -308,7 +350,7 @@
 		ElementLocation currentElementLocation = null;
 		@Override
 		public void endElement(String uri, String localName, String qName) throws SAXException {
-			if (!isXHTMLDoctype)
+			if (!isWellFormed || !isXHTMLDoctype)
 				return;
 
 			int end = getCurrentLocation(), start = 0;
@@ -354,10 +396,21 @@
 			return qName.toString();
 		}
 
+		@Override
+		public void fatalError(SAXParseException e) throws SAXException {
+			// We do not need to throw any exceptions here as super method does!
+		}
+
+		@Override
+		public InputSource resolveEntity(String publicId, String systemId)
+				throws IOException, SAXException {
+			return new InputSource(new StringReader("")); //$NON-NLS-1$
+		}
+
 		List<ElementLocation> getNonPairedOpenElements() {
 			return nonPairedOpenElements;
 		}
-
+		
 		List<ElementLocation> getNonPairedCloseElements() {
 			return nonPairedCloseElements;
 		}
@@ -365,7 +418,7 @@
 		private int getCurrentLocation() {
 			if (locator == null) 
 				return 0;
-
+				
 			int line = locator.getLineNumber() - 1;
 			int lineOffset = locator.getColumnNumber() - 1;
 			try {
@@ -411,12 +464,14 @@
 		@Override
 		public void startDTD(String name, String publicId, String systemId)
 				throws SAXException {
-    		valinfo.setGrammarEncountered(true);
-    		valinfo.setDTDEncountered(true);
-    		if (publicId != null && publicId.indexOf("W3C") != -1 && 
-    				publicId.indexOf("DTD") != -1 && publicId.indexOf("XHTML") != -1) {
-    			isXHTMLDoctype = true;
-    		}
+			if (!isWellFormed) {
+	    		valinfo.setGrammarEncountered(true);
+	    		valinfo.setDTDEncountered(true);
+	    		if (publicId != null && publicId.indexOf("W3C") != -1 && 
+	    				publicId.indexOf("DTD") != -1 && publicId.indexOf("XHTML") != -1) {
+	    			this.isXHTMLDoctype = true;
+	    		}
+			}
 		}
 
 		@Override
@@ -443,5 +498,53 @@
 		public void comment(char[] ch, int start, int length)
 				throws SAXException {
 		}
+		
+		private SAXParseException exception = null;
+		
+		SAXParseException getException() {
+			return exception;
+		}
+		
+		public boolean isWellFormedXHTML() throws IOException, SAXException {
+			exception = null;
+			isXHTMLDoctype = false;
+			isWellFormed = false;
+		    XMLReader reader = new org.apache.xerces.parsers.SAXParser();     
+			try {
+				setSAXParserFeatures(reader, SAX_PARSER_FEATURES_TO_DISABLE, false);
+				setSAXParserFeatures(reader, SAX_PARSER_FEATURES_TO_ENABLE, false); // Disabling this feature 
+																					// due to prevent validation 
+																					// on non-well-formed xhtml files
+	    	    setSAXParserProperty(reader, "http://xml.org/sax/properties/lexical-handler", this); 
+    			reader.setProperty("http://apache.org/xml/properties/internal/entity-resolver", new NullXMLEntityResolver());
+
+    			reader.setContentHandler(this);
+	    	    reader.setDTDHandler(this);
+	    	    reader.setErrorHandler(this);
+	    	    reader.setEntityResolver(this);
+
+	    	    isXHTMLDoctype = false;
+				reader.parse(uri);
+			} catch (SAXParseException e) {
+				// We have to prevent further validation in case we've met the following exception:
+				// "The markup in the document preceding the root element must be well-formed"
+				// If the markup is not well-formed, not doing this may cause the validation to stuck 
+				// in Throwable.fillInStackTrace() method (it may come into an infinite loop) for some reason
+				//
+				if (isNonWellFormedException(e)) {
+					exception = e;
+					isXHTMLDoctype = false;
+					return false;
+				}
+			}
+			isWellFormed = true;
+			return this.isXHTMLDoctype;
+		}
+		
+		private boolean isNonWellFormedException (SAXParseException e) {
+			String message = e.getMessage() == null ? null : e.getMessage().toLowerCase();
+			return (message != null && message.contains("markup") && message.contains("document") &&
+					message.contains("preceding") && message.contains("must") && message.contains("well-formed"));
+		}
 	}
 }
