245532 HTML Validator Performance Impacted by Model Query Extension
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
index a4a0279..c86a52f 100644
--- a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLAttributeValidator.java
@@ -12,10 +12,8 @@
 
 
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 
 import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
 import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
@@ -26,7 +24,6 @@
 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.CMNode;
-import org.eclipse.wst.xml.core.internal.contentmodel.basic.CMNamedNodeMapImpl;
 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;
@@ -99,38 +96,7 @@
 			return;
 		CMNamedNodeMap declarations = edec.getAttributes();
 
-		CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(declarations) {
-			private Map caseInsensitive;
-			
-			private Map getCaseInsensitiveMap() {
-				if(caseInsensitive == null)
-					caseInsensitive = new HashMap();
-				return caseInsensitive;
-			}
-
-			public CMNode getNamedItem(String name) {
-				CMNode node = super.getNamedItem(name);
-				if (node == null) {
-					node = (CMNode) getCaseInsensitiveMap().get(name.toLowerCase(Locale.US));
-				}
-				return node;
-			}
-
-			public void put(CMNode cmNode) {
-				super.put(cmNode);
-				getCaseInsensitiveMap().put(cmNode.getNodeName().toLowerCase(Locale.US), cmNode);
-			}
-		};
-
-		List nodes = ModelQueryUtil.getModelQuery(target.getOwnerDocument()).getAvailableContent((Element) node, edec, ModelQuery.INCLUDE_ATTRIBUTES);
-		for (int k = 0; k < nodes.size(); k++) {
-			CMNode cmnode = (CMNode) nodes.get(k);
-			if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) {
-				allAttributes.put(cmnode);
-			}
-		}
-		declarations = allAttributes;
-
+		List modelQueryNodes = null;
 		NamedNodeMap attrs = target.getAttributes();
 		for (int i = 0; i < attrs.getLength(); i++) {
 			int rgnType = REGION_NAME;
@@ -150,6 +116,22 @@
 			}
 
 			CMAttributeDeclaration adec = (CMAttributeDeclaration) declarations.getNamedItem(a.getName());
+			
+			/* Check the modelquery if nothing is declared by the element declaration */
+			if (adec == null) {
+				if (modelQueryNodes == null)
+					modelQueryNodes = ModelQueryUtil.getModelQuery(target.getOwnerDocument()).getAvailableContent((Element) node, edec, ModelQuery.INCLUDE_ATTRIBUTES);
+				
+				String attrName = a.getName().toLowerCase(Locale.US);
+				for (int k = 0; k < modelQueryNodes.size(); k++) {
+					CMNode cmnode = (CMNode) modelQueryNodes.get(k);
+					if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION && cmnode.getNodeName().toLowerCase(Locale.US).equals(attrName)) {
+						adec = (CMAttributeDeclaration) cmnode;
+						break;
+					}
+				}
+			}
+			
 			if (adec == null) {
 				// No attr declaration was found. That is, the attr name is
 				// undefined.
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLElementContentValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLElementContentValidator.java
index 9041574..39fe3fe 100644
--- a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLElementContentValidator.java
+++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/internal/validate/HTMLElementContentValidator.java
@@ -59,28 +59,11 @@
 		if(ed == null || ed.getContentType() == CMElementDeclaration.ANY)
 			return;
 		
-		/*
-		 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=218143 - 
-		 * ModelQuery use not pervasive enough
-		 */
-		List availableChildElementDeclarations = ModelQueryUtil.getModelQuery(parent.getOwnerDocument()).getAvailableContent(parent, ed, ModelQuery.INCLUDE_CHILD_NODES);
-		/*
-		 * Retrieve and set aside just the element names for faster checking
-		 * later.
-		 */
-		int availableChildCount = availableChildElementDeclarations.size();
-		List availableChildElementLowercaseNames = new ArrayList(availableChildCount);
-		for (int i = 0; i < availableChildCount; i++) {
-			CMNode cmnode = (CMNode) availableChildElementDeclarations.get(i);
-			if (cmnode.getNodeType() == CMNode.ELEMENT_DECLARATION) {
-				availableChildElementLowercaseNames.add(cmnode.getNodeName().toLowerCase(Locale.US));
-			}
-		}
-		Object[] availableChildElementLowercaseNamesArray = availableChildElementLowercaseNames.toArray();
-
+		List extendedContentHolder = new ArrayList();
+		boolean[] extendedContentRetrieved = new boolean[1];
 		while (child != null) {
 			// perform actual validation
-			validateNode(parent, child, ed, availableChildElementLowercaseNamesArray);
+			validateNode(parent, child, ed, extendedContentHolder, extendedContentRetrieved);
 			child = child.getNextSibling();
 		}
 	}
@@ -109,17 +92,17 @@
 	// return 1;
 	// }
 
-	private boolean containsName(String name, Object[] possible) {
-		if (name != null && possible != null) {
-			for (int i = 0; i < possible.length; i++) {
-				if(name.equals(possible[i]))
-				return true;
-			}
-		}
-		return false;
-	}
+//	private boolean containsName(String name, Object[] possible) {
+//		if (name != null && possible != null) {
+//			for (int i = 0; i < possible.length; i++) {
+//				if(name.equals(possible[i]))
+//				return true;
+//			}
+//		}
+//		return false;
+//	}
 
-	private void validateNode(Element target, Node child, CMElementDeclaration edec, Object[] availableLowercaseChildElementNames) {
+	private void validateNode(Element target, Node child, CMElementDeclaration edec, List extendedContent, boolean[] extendedContentInitialized) {
 		// NOTE: If the target element is 'UNKNOWN', that is, it has no
 		// element declaration, the content type of the element should be
 		// regarded as 'ANY'. -- 9/10/2001
@@ -144,7 +127,7 @@
 				// Defect 186774: If a child is not one of HTML elements,
 				// it should be regarded as a valid child regardless the
 				// type of the parent content model. -- 10/12/2001
-				if (ced == null || CMUtil.isSSI(ced) || (!CMUtil.isHTML(ced)) || containsName(ced.getElementName().toLowerCase(Locale.US), availableLowercaseChildElementNames))
+				if (ced == null || CMUtil.isSSI(ced) || (!CMUtil.isHTML(ced)))
 					return;
 
 				switch (contentType) {
@@ -165,6 +148,30 @@
 							if (CMUtil.isValidInclusion(ced, target))
 								return;
 						}
+						
+						/*
+						 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=218143 - 
+						 * ModelQuery use not pervasive enough
+						 */
+						if (!extendedContentInitialized[0]) {
+							extendedContent.addAll(ModelQueryUtil.getModelQuery(target.getOwnerDocument()).getAvailableContent(target, edec, ModelQuery.INCLUDE_CHILD_NODES));
+							extendedContentInitialized[0] = true;
+						}
+
+						List availableChildElementDeclarations = extendedContent;
+						/*
+						 * Retrieve and set aside just the element names for faster checking
+						 * later.
+						 */
+						int availableChildCount = availableChildElementDeclarations.size();
+						String elementName = ced.getElementName().toLowerCase(Locale.US);
+						for (int i = 0; i < availableChildCount; i++) {
+							CMNode cmnode = (CMNode) availableChildElementDeclarations.get(i);
+							if (cmnode.getNodeType() == CMNode.ELEMENT_DECLARATION && cmnode.getNodeName().toLowerCase(Locale.US).equals(elementName)) {
+								return;
+							}
+						}
+						
 						error = ErrorState.INVALID_CONTENT_ERROR;
 						break;
 					default :