blob: 78b707b037158b3f770a1801a2468f91d219ccd8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2009 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.wst.html.core.internal.validate;
import java.util.List;
import java.util.Locale;
import org.eclipse.wst.html.core.internal.provisional.HTMLCMProperties;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNode;
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.IDOMNode;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class HTMLElementContentValidator extends PrimeValidator {
/**
* HTMLElementContentValidator constructor comment.
*/
public HTMLElementContentValidator() {
super();
}
/**
* Allowing the INodeAdapter to compare itself against the type allows it
* to return true in more than one case.
*/
public boolean isAdapterForType(Object type) {
return ((type == HTMLElementContentValidator.class) || super.isAdapterForType(type));
}
/**
*/
public void validate(IndexedRegion node) {
Element target = (Element) node;
if (CMUtil.isForeign(target))
return;
validateContent(target, target.getFirstChild());
}
private void validateContent(Element parent, Node child) {
if (child == null)
return;
CMElementDeclaration ed = CMUtil.getDeclaration(parent);
if(ed == null || ed.getContentType() == CMElementDeclaration.ANY)
return;
List[] extendedContent = new List[1];
while (child != null) {
// perform actual validation
validateNode(parent, child, ed, extendedContent);
child = child.getNextSibling();
}
}
// private int countExplicitSiblings(Element parent, String tagName) {
// NodeList children = parent.getChildNodes();
// int count = 0;
// for (int i = 0; i < children.getLength(); i++) {
// Node child = children.item(i);
// if (child.getNodeType() != Node.ELEMENT_NODE)
// continue;
// if (tagName.equalsIgnoreCase(((Element) child).getTagName())) {
// count++;
// }
// }
// return count;
// }
/*
* The implementation of the following method is practical but accurate.
* The accurate maximum occurrence should be retrieve from the content
* model. However, it is useful enough, since almost implicit elements are
* HTML, HEAD, or BODY.
*/
// private int getMaxOccur(Element parent, String childTag) {
// 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 void validateNode(Element target, Node child, CMElementDeclaration edec, List[] extendedContent) {
// 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
int contentType = CMElementDeclaration.ANY;
if (edec != null)
contentType = edec.getContentType();
int error = ErrorState.NONE_ERROR;
int segType = FMUtil.SEG_WHOLE_TAG;
switch (child.getNodeType()) {
case Node.ELEMENT_NODE :
Element childElem = (Element) child;
// Defect 200321:
// This validator cares only HTML/XHTML elements. If a child
// is
// an element of foreign markup languages, just ignore it.
if (CMUtil.isForeign(childElem))
return;
CMElementDeclaration ced = CMUtil.getDeclaration((Element) child);
// 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)))
return;
switch (contentType) {
case CMElementDeclaration.ANY :
// Keep going.
return;
case CMElementDeclaration.ELEMENT :
case CMElementDeclaration.MIXED :
if (ced == null)
return;
if (CMUtil.isValidChild(edec, ced))
return;
// Now, it is the time to check inclusion, unless the
// target
// document is not a XHTML.
if (!CMUtil.isXHTML(edec)) {
// pure HTML
if (CMUtil.isValidInclusion(ced, target))
return;
}
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=218143 -
* ModelQuery use not pervasive enough
*/
if (extendedContent[0] == null) {
extendedContent[0] = ModelQueryUtil.getModelQuery(target.getOwnerDocument()).getAvailableContent(target, edec, ModelQuery.INCLUDE_CHILD_NODES);
}
List availableChildElementDeclarations = extendedContent[0];
/*
* 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 :
error = ErrorState.INVALID_CONTENT_ERROR;
break;
}
// Mark the whole START tag as an error segment.
segType = FMUtil.SEG_START_TAG;
break;
case Node.TEXT_NODE :
switch (contentType) {
case CMElementDeclaration.ANY :
case CMElementDeclaration.MIXED :
case CMElementDeclaration.PCDATA :
case CMElementDeclaration.CDATA :
// D184339
// Keep going.
return;
case CMElementDeclaration.ELEMENT :
case CMElementDeclaration.EMPTY :
if (((IDOMText) child).isElementContentWhitespace())
return;
error = ErrorState.INVALID_CONTENT_ERROR;
break;
default :
error = ErrorState.INVALID_CONTENT_ERROR;
break;
}
// Mark the whole node as an error segment.
segType = FMUtil.SEG_WHOLE_TAG;
break;
case Node.COMMENT_NODE :
case Node.PROCESSING_INSTRUCTION_NODE :
if (contentType != CMElementDeclaration.EMPTY)
return;
error = ErrorState.INVALID_CONTENT_ERROR;
// Mark the whole node as an error segment.
segType = FMUtil.SEG_WHOLE_TAG;
break;
case Node.CDATA_SECTION_NODE :
if (edec.supports(HTMLCMProperties.IS_XHTML) && Boolean.TRUE.equals(edec.getProperty(HTMLCMProperties.IS_XHTML)))
return;
// Mark the whole CDATA section as an error segment
error = ErrorState.INVALID_CONTENT_ERROR;
segType = FMUtil.SEG_WHOLE_TAG;
break;
default :
error = ErrorState.INVALID_CONTENT_ERROR;
// Mark the whole node as an error segment.
segType = FMUtil.SEG_WHOLE_TAG;
break;
}
if (error != ErrorState.NONE_ERROR) {
Segment errorSeg = FMUtil.getSegment((IDOMNode) child, segType);
if (errorSeg != null)
reporter.report(new ErrorInfoImpl(error, errorSeg, child));
}
}
}