diff options
Diffstat (limited to 'bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate')
23 files changed, 2337 insertions, 0 deletions
diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/AbstractErrorInfo.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/AbstractErrorInfo.java new file mode 100644 index 0000000000..5e6ee85f30 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/AbstractErrorInfo.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +abstract class AbstractErrorInfo implements ErrorInfo, ErrorState { + + + private int state = NONE_ERROR; + private Segment seg = null; + + public AbstractErrorInfo(int state, Segment seg) { + super(); + this.state = state; + this.seg = seg; + } + + abstract public String getHint(); + + abstract public short getTargetType(); + + public int getLength() { + return (seg == null) ? 0 : seg.getLength(); + } + + public int getOffset() { + return (seg == null) ? 0 : seg.getOffset(); + } + + public int getState() { + return this.state; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CMUtil.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CMUtil.java new file mode 100644 index 0000000000..38f351d2e1 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CMUtil.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import java.util.Iterator; + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMContent; +import org.eclipse.wst.common.contentmodel.CMDataType; +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.CMGroup; +import org.eclipse.wst.common.contentmodel.CMNode; +import org.eclipse.wst.common.contentmodel.CMNodeList; +import org.eclipse.wst.common.contentmodel.modelquery.ModelQuery; +import org.eclipse.wst.html.core.HTMLCMProperties; +import org.eclipse.wst.html.core.modelquery.HMQUtil; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.modelquery.ModelQueryUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +final class CMUtil { + + /** + * Never instantiate! + */ + private CMUtil() { + super(); + } + + /** + * You cannot always retrieve HTMLElementDeclaration via an Element instance. + * Because, it occasionally a JSP custom tag. -- 9/7/2001 + */ + public static CMElementDeclaration getDeclaration(Element target) { + Document doc = target.getOwnerDocument(); + ModelQuery query = ModelQueryUtil.getModelQuery(doc); + return query.getCMElementDeclaration(target); + } + + /** + */ + public static boolean isCaseSensitive(CMElementDeclaration decl) { + if (decl == null || (!decl.supports(HTMLCMProperties.SHOULD_IGNORE_CASE))) + return false; + return !((Boolean) decl.getProperty(HTMLCMProperties.SHOULD_IGNORE_CASE)).booleanValue(); + } + + /** + */ + private static boolean isChild(CMContent content, CMElementDeclaration target) { + switch (content.getNodeType()) { + case CMNode.ELEMENT_DECLARATION : + if (isWholeTagOmissible((CMElementDeclaration) content)) + if (isChild(((CMElementDeclaration) content).getContent(), target)) + return true; + return isSameDeclaration((CMElementDeclaration) content, target); + case CMNode.GROUP : + CMNodeList children = ((CMGroup) content).getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + CMNode child = children.item(i); + switch (child.getNodeType()) { + case CMNode.ELEMENT_DECLARATION : + if (isWholeTagOmissible((CMElementDeclaration) child)) + if (isChild(((CMElementDeclaration) child).getContent(), target)) + return true; + if (isSameDeclaration((CMElementDeclaration) child, target)) + return true; + continue; // Go next child. + case CMNode.GROUP : + if (isChild((CMContent) child, target)) + return true; + continue; // Go next child. + default : + continue; // Go next child. + } + } + } + return false; + } + + /** + */ + public static boolean isEndTagOmissible(CMElementDeclaration decl) { + if (!(decl.supports(HTMLCMProperties.OMIT_TYPE))) + return false; + String omitType = (String) decl.getProperty(HTMLCMProperties.OMIT_TYPE); + return !omitType.equals(HTMLCMProperties.Values.OMIT_NONE); + } + + /** + */ + public static boolean isWholeTagOmissible(CMElementDeclaration decl) { + if (!(decl.supports(HTMLCMProperties.OMIT_TYPE))) + return false; + String omitType = (String) decl.getProperty(HTMLCMProperties.OMIT_TYPE); + return omitType.equals(HTMLCMProperties.Values.OMIT_BOTH); + } + + /** + */ + public static boolean isJSP(CMElementDeclaration decl) { + if (!decl.supports(HTMLCMProperties.IS_JSP)) + return false; + return ((Boolean) decl.getProperty(HTMLCMProperties.IS_JSP)).booleanValue(); + } + + /** + */ + public static boolean isXHTML(CMElementDeclaration decl) { + if (!decl.supports(HTMLCMProperties.IS_XHTML)) + return false; + return ((Boolean) decl.getProperty(HTMLCMProperties.IS_XHTML)).booleanValue(); + } + + /** + * The method to distinguish HTML and XHTML from other mark up. + * This method returns true if the target is, + * (1) not JSP, + * (2) not SSI. + */ + public static boolean isHTML(CMElementDeclaration decl) { + return (!isJSP(decl)) && (!isSSI(decl)); + } + + /** + */ + private static boolean isSameDeclaration(CMElementDeclaration aDec, CMElementDeclaration otherDec) { + return aDec.getElementName() == otherDec.getElementName(); + } + + /** + */ + public static boolean isSSI(CMElementDeclaration edec) { + if (edec == null) + return false; + if (!edec.supports(HTMLCMProperties.IS_SSI)) + return false; + return ((Boolean) edec.getProperty(HTMLCMProperties.IS_SSI)).booleanValue(); + } + + /** + * Call this method only when the parent content type is one of + * the following: ANY, ELEMENT, or MIXED. + */ + public static boolean isValidChild(CMElementDeclaration parent, CMElementDeclaration child) { + if (parent == null || child == null) + return false; + if (isHTML(parent) && (!isHTML(child))) + return true; + CMContent content = parent.getContent(); + if (content == null) + return false; + return isChild(content, child); + } + + public static boolean isForeign(Element target) { + if (!(target instanceof XMLElement)) + return true; + XMLElement element = (XMLElement) target; + return !element.isGlobalTag(); + } + + /** + * This method returns true if all of the following conditions are met: + * (1) value type is ENUM, + * (2) only one value is defined in the enumeration, + * (3) the value has same name to the attribute name. + */ + public static boolean isBooleanAttr(CMAttributeDeclaration adec) { + CMDataType attrtype = adec.getAttrType(); + if (attrtype == null) + return false; + if (attrtype.getDataTypeName() != CMDataType.ENUM) + return false; + String[] values = attrtype.getEnumeratedValues(); + if (values.length != 1) + return false; + return values[0].equals(adec.getAttrName()); + } + + public static boolean isValidInclusion(CMElementDeclaration decl, Element parent) { + Iterator iter = HMQUtil.getInclusions(parent).iterator(); + while (iter.hasNext()) { + CMElementDeclaration inclusion = (CMElementDeclaration) iter.next(); + if (isSameDeclaration(decl, inclusion)) + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CompositeValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CompositeValidator.java new file mode 100644 index 0000000000..1c5a6017e5 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/CompositeValidator.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import java.util.Iterator; +import java.util.Vector; + +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.eclipse.wst.sse.core.validate.ValidationReporter; +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +abstract class CompositeValidator extends ValidationComponent { + + protected Vector components = new Vector(); + + /** + * CompositeValidator constructor comment. + */ + public CompositeValidator() { + super(); + } + + /** + */ + public void setReporter(ValidationReporter reporter) { + super.setReporter(reporter); + + Iterator i = components.iterator(); + while (i.hasNext()) { + ValidationAdapter component = (ValidationAdapter) i.next(); + if (component == null) + continue; + component.setReporter(reporter); + } + } + + /** + */ + public void validate(IndexedRegion node) { + Iterator i = components.iterator(); + while (i.hasNext()) { + ValidationComponent component = (ValidationComponent) i.next(); + if (component == null) + continue; + component.validate(node); + } + } + + /** + */ + void add(ValidationComponent validator) { + components.add(validator); + } + + /** + * This method registers all components in 'validators'. + * Each derivative must call this methid in its constructor. + */ + protected void register(ValidationComponent[] validators) { + for (int i = 0; i < validators.length; i++) { + if (validators[i] != null) + add(validators[i]); + } + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/DocumentPropagatingValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/DocumentPropagatingValidator.java new file mode 100644 index 0000000000..51edfcda31 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/DocumentPropagatingValidator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.eclipse.wst.sse.core.validate.ValidationReporter; +import org.eclipse.wst.xml.core.internal.validate.AbstractPropagatingValidator; +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +class DocumentPropagatingValidator extends AbstractPropagatingValidator { + + + private ValidationComponent validator = new HTMLSimpleDocumentValidator(); + private ElementPropagatingValidator propagatee = new ElementPropagatingValidator(); + + public DocumentPropagatingValidator() { + super(); + } + + public void validate(IndexedRegion node) { + getPropagatee().setReporter(this.reporter); + super.validate(node); + } + + public boolean isAdapterForType(Object type) { + return (type == DocumentPropagatingValidator.class || super.isAdapterForType(type)); + } + + public void setReporter(ValidationReporter reporter) { + super.setReporter(reporter); + validator.setReporter(reporter); + propagatee.setReporter(reporter); + } + + /** + * @see com.ibm.sed.validate.html.AbstractPropagatingValidator#getPropagatee() + */ + protected final ValidationComponent getPropagatee() { + return propagatee; + } + + /** + * @see com.ibm.sed.validate.html.AbstractPropagatingValidator#getValidator() + */ + protected final ValidationAdapter getValidator() { + return validator; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ElementPropagatingValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ElementPropagatingValidator.java new file mode 100644 index 0000000000..bd10b279a2 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ElementPropagatingValidator.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.eclipse.wst.sse.core.validate.ValidationReporter; +import org.eclipse.wst.xml.core.internal.validate.AbstractPropagatingValidator; +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +class ElementPropagatingValidator extends AbstractPropagatingValidator { + + + private ValidationComponent validator = new HTMLSimpleValidator(); + + public ElementPropagatingValidator() { + super(); + } + + public boolean isAdapterForType(Object type) { + return (type == ElementPropagatingValidator.class || super.isAdapterForType(type)); + } + + public void setReporter(ValidationReporter reporter) { + super.setReporter(reporter); + validator.setReporter(reporter); + } + + /** + * @see com.ibm.sed.validate.html.AbstractPropagatingValidator#getPropagatee() + */ + protected ValidationComponent getPropagatee() { + return this; + } + + /** + * @see com.ibm.sed.validate.html.AbstractPropagatingValidator#getValidator() + */ + protected ValidationAdapter getValidator() { + return validator; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfo.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfo.java new file mode 100644 index 0000000000..01ce2c0799 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfo.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +interface ErrorInfo { + + public String getHint(); + + public int getLength(); + + public int getOffset(); + + public int getState(); + + public short getTargetType(); +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfoImpl.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfoImpl.java new file mode 100644 index 0000000000..f10294e99f --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorInfoImpl.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.w3c.dom.Attr; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +final class ErrorInfoImpl extends AbstractErrorInfo { + + private Node target = null; + + // private Segment seg = null; + /** + */ + public ErrorInfoImpl(int state, Segment errorSeg, Node target) { + super(state, errorSeg); + this.target = target; + } + + /** + */ + public String getHint() { + switch (target.getNodeType()) { + case Node.ATTRIBUTE_NODE : + switch (getState()) { + case UNDEFINED_VALUE_ERROR : + case MISMATCHED_VALUE_ERROR : + case UNCLOSED_ATTR_VALUE : + //D210422 + return ((Attr) target).getValue(); + default : + return target.getNodeName(); + } + case Node.TEXT_NODE : + return ((Text) target).getData(); + case Node.ELEMENT_NODE : + default : + return target.getNodeName(); + } + } + + public short getTargetType() { + return target.getNodeType(); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorState.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorState.java new file mode 100644 index 0000000000..2c0eb26eba --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/ErrorState.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +interface ErrorState { + static final int NONE_ERROR = 0; + // generic error + static final int UNDEFINED_NAME_ERROR = 11; + static final int UNDEFINED_VALUE_ERROR = 12; + static final int MISMATCHED_VALUE_ERROR = 13; + // format error + static final int FORMAT_ERROR_LEVEL = 100; + static final int INVALID_NAME_ERROR = 101; + static final int INVALID_CHAR_ERROR = 102; + static final int MISMATCHED_ERROR = 103; + static final int MISMATCHED_END_TAG_ERROR = 104; + static final int MISSING_START_TAG_ERROR = 105; + static final int MISSING_END_TAG_ERROR = 106; + static final int UNNECESSARY_END_TAG_ERROR = 107; + static final int INVALID_ATTR_ERROR = 108; + static final int INVALID_DIRECTIVE_ERROR = 109; + static final int UNCLOSED_TAG_ERROR = 110; + static final int UNCLOSED_END_TAG_ERROR = 111; + static final int INVALID_EMPTY_ELEMENT_TAG = 112; + static final int UNCLOSED_ATTR_VALUE = 113; //D210422 + // layout error + static final int LAYOUT_ERROR_LEVEL = 1000; + static final int INVALID_CONTENT_ERROR = 1001; + static final int DUPLICATE_ERROR = 1002; + static final int COEXISTENCE_ERROR = 1003; +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/FMUtil.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/FMUtil.java new file mode 100644 index 0000000000..da4f240415 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/FMUtil.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import java.util.Iterator; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + +final class FMUtil { + + public final static int SEG_NONE = 0; + public final static int SEG_WHOLE_TAG = 1; + public final static int SEG_START_TAG = 2; + public final static int SEG_END_TAG = 3; + + /** + */ + private FMUtil() { + super(); + } + + /** + */ + public final static Segment getSegment(XMLNode target, int segType) { + if (target == null) + return new Segment(0, 0); + Segment seg = null; + IStructuredDocumentRegion startTag = null; + IStructuredDocumentRegion endTag = null; + switch (segType) { + case SEG_WHOLE_TAG : + startTag = target.getFirstStructuredDocumentRegion(); + if (startTag != null) { + endTag = target.getLastStructuredDocumentRegion(); + seg = new Segment(startTag, endTag); + } + else { + int startOffset = target.getStartOffset(); + int endOffset = target.getEndOffset(); + seg = new Segment(startOffset, endOffset - startOffset); + } + break; + case SEG_START_TAG : + startTag = target.getStartStructuredDocumentRegion(); + if (startTag != null) { + seg = new Segment(startTag); + } + else { + seg = new Segment(target.getStartOffset(), 0); + } + break; + case SEG_END_TAG : + endTag = target.getEndStructuredDocumentRegion(); + if (endTag != null) { + seg = new Segment(endTag); + } + else { + seg = new Segment(target.getEndOffset(), 0); + } + break; + case SEG_NONE : + default : + return new Segment(0, 0); + } + return seg; + } + + /** + */ + public final static 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 == XMLRegionContext.XML_TAG_OPEN || regionType == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN || regionType == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN || regionType == XMLJSPRegionContexts.JSP_DECLARATION_OPEN || regionType == XMLJSPRegionContexts.JSP_DIRECTIVE_OPEN) + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLAttributeValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLAttributeValidator.java new file mode 100644 index 0000000000..a57a30720d --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLAttributeValidator.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMDataType; +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.document.XMLAttr; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +public class HTMLAttributeValidator extends PrimeValidator { + + private static final int REGION_NAME = 1; + private static final int REGION_VALUE = 2; + //<<D210422 + private static final char SINGLE_QUOTE = '\''; + private static final char DOUBLE_QUOTE = '\"'; + + //D210422 + /** + * HTMLAttributeValidator constructor comment. + */ + public HTMLAttributeValidator() { + super(); + } + + /** + */ + private Segment getErrorSegment(XMLNode errorNode, int regionType) { + ITextRegion rgn = null; + switch (regionType) { + case REGION_NAME : + rgn = errorNode.getNameRegion(); + break; + case REGION_VALUE : + rgn = errorNode.getValueRegion(); + break; + default : + // nothing to do. + break; + } + if (rgn != null) { + if (errorNode instanceof XMLAttr) { + XMLElement ownerElement = (XMLElement) ((XMLAttr) errorNode).getOwnerElement(); + if (ownerElement != null) { + int regionStartOffset = ownerElement.getFirstStructuredDocumentRegion().getStartOffset(rgn); + int regionLength = rgn.getLength(); + return new Segment(regionStartOffset, regionLength); + } + } + } + return new Segment(errorNode.getStartOffset(), 0); + } + + /** + * 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 == HTMLAttributeValidator.class) || super.isAdapterForType(type)); + } + + /** + */ + public void validate(IndexedRegion node) { + Element target = (Element) node; + if (CMUtil.isForeign(target)) + return; + CMElementDeclaration edec = CMUtil.getDeclaration(target); + if (edec == null) + return; + CMNamedNodeMap declarations = edec.getAttributes(); + + NamedNodeMap attrs = target.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + int rgnType = REGION_NAME; + int state = ErrorState.NONE_ERROR; + Attr a = (Attr) attrs.item(i); + // D203637; If the target attr has prefix, the validator should not + // warn about it. That is, just ignore. It is able to check whether + // an attr has prefix or not by calling XMLAttr#isGlobalAttr(). + // When a attr has prefix (not global), it returns false. + boolean isXMLAttr = a instanceof XMLAttr; + if (isXMLAttr) { + XMLAttr xmlattr = (XMLAttr) a; + if (!xmlattr.isGlobalAttr()) + continue; // skip futher validation and begin next loop. + } + + CMAttributeDeclaration adec = (CMAttributeDeclaration) declarations.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 (!FMUtil.hasJSPRegion(((XMLNode) a).getNameRegion())) { + rgnType = REGION_NAME; + state = ErrorState.UNDEFINED_NAME_ERROR; + } + } + else { + // The attr declaration was found. + // At 1st, the name should be checked. + if (CMUtil.isHTML(edec) && (!CMUtil.isXHTML(edec))) { + // If the target element is pure HTML (not XHTML), some attributes + // might be written in boolean format. It should be check specifically. + if (CMUtil.isBooleanAttr(adec) && ((XMLAttr) a).hasNameOnly()) + continue; // OK, keep going. No more check is needed against this attr. + } + else { + // If the target is other than pure HTML (JSP or XHTML), the name + // must be checked exactly (ie in case sensitive way). + String actual = a.getName(); + String desired = adec.getAttrName(); + if (!actual.equals(desired)) { // case mismatch + rgnType = REGION_NAME; + state = ErrorState.MISMATCHED_ERROR; + } + } + // Then, the value must be checked. + if (state == ErrorState.NONE_ERROR) { // Need more check. + // Now, the value should be checked, if the type is ENUM. + CMDataType attrType = adec.getAttrType(); + String actualValue = a.getValue(); + if (attrType.getImpliedValueKind() == CMDataType.IMPLIED_VALUE_FIXED) { + // Check FIXED value. + String validValue = attrType.getImpliedValue(); + if (!actualValue.equals(validValue)) { + rgnType = REGION_VALUE; + state = ErrorState.UNDEFINED_VALUE_ERROR; + } + } + else { + String[] candidates = attrType.getEnumeratedValues(); + if (candidates != null && candidates.length > 0) { + // several candidates are found. + boolean found = false; + for (int index = 0; index < candidates.length; index++) { + String candidate = candidates[index]; + // At 1st, compare ignoring case. + if (actualValue.equalsIgnoreCase(candidate)) { + found = true; + if (CMUtil.isCaseSensitive(edec) && (!actualValue.equals(candidate))) { + rgnType = REGION_VALUE; + state = ErrorState.MISMATCHED_VALUE_ERROR; + } + break; // exit the loop. + } + } + if (!found) { + // No candidate was found. That is, actualValue is invalid. + // but not regard it as undefined value if it includes JSP. + if (!FMUtil.hasJSPRegion(((XMLNode) a).getValueRegion())) { + rgnType = REGION_VALUE; + state = ErrorState.UNDEFINED_VALUE_ERROR; + } + } + } + } + } + //<<D210422 + if (state == ErrorState.NONE_ERROR) { // Need more check. + if (isXMLAttr) { + String source = ((XMLAttr) a).getValueRegionText(); + if (source != null) { + char firstChar = source.charAt(0); + char lastChar = source.charAt(source.length() - 1); + if (isQuote(firstChar) || isQuote(lastChar)) { + if (lastChar != firstChar) { + rgnType = REGION_VALUE; + state = ErrorState.UNCLOSED_ATTR_VALUE; + } + } + } + } + } + //D210422 + } + if (state != ErrorState.NONE_ERROR) { + Segment seg = getErrorSegment((XMLNode) a, rgnType); + if (seg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(state, seg, a))); + } + } + } + + //<<D214022 + private boolean isQuote(char c) { + return (c == SINGLE_QUOTE) || (c == DOUBLE_QUOTE); + } + //D210422 +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLDocumentContentValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLDocumentContentValidator.java new file mode 100644 index 0000000000..14e298aed7 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLDocumentContentValidator.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.html.core.HTML40Namespace; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.xml.core.document.DocumentTypeAdapter; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.document.XMLText; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class HTMLDocumentContentValidator extends PrimeValidator { + + + private final static class Division { + private Vector explicitHtmls = new Vector(); + private Vector rest = new Vector(); + + public Division(Document document, NodeList children) { + String rootTagName = getRootTagName(document); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (isHtmlTag(child, rootTagName)) { + explicitHtmls.add(child); + } + else { + rest.add(child); + } + } + } + + public boolean hasExplicitHtmls() { + return explicitHtmls.size() > 0; + } + + public List getExplicitHtmls() { + return explicitHtmls; + } + + public Iterator getRestNodes() { + return rest.iterator(); + } + + /* utilities */ + private static boolean isHtmlTag(Node node, String tagName) { + if (node.getNodeType() != Node.ELEMENT_NODE) + return false; + return ((Element) node).getTagName().equalsIgnoreCase(tagName); + } + + private static String getRootTagName(Document document) { + DocumentTypeAdapter adapter = (DocumentTypeAdapter) ((XMLDocument) document).getAdapterFor(DocumentTypeAdapter.class); + if (adapter != null) { + DocumentType docType = adapter.getDocumentType(); + if (docType != null) { + return docType.getName(); + } + } + + return HTML40Namespace.ElementName.HTML; + } + } + + /** + * HTMLDocumentContentValidator constructor comment. + */ + public HTMLDocumentContentValidator() { + 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 == HTMLDocumentContentValidator.class) || super.isAdapterForType(type)); + } + + /** + */ + public void validate(IndexedRegion node) { + // isFragment check should be more intelligent. + boolean isFragment = true; + + Document target = (Document) node; + NodeList children = target.getChildNodes(); + if (children == null) + return; + + Division division = new Division(target, children); + if (division.hasExplicitHtmls()) { + isFragment = false; + + List explicits = division.getExplicitHtmls(); + if (explicits.size() > 1) { + for (int i = 1; i < explicits.size(); i++) { + Element html = (Element) explicits.get(i); + // report error (duplicate) + Segment errorSeg = FMUtil.getSegment((XMLNode) html, FMUtil.SEG_START_TAG); + if (errorSeg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(ErrorState.DUPLICATE_ERROR, errorSeg, html))); + } + } + } + validateContent(division.getRestNodes(), isFragment); + } + + /* + * This methods validate nodes other than HTML elements. + */ + private void validateContent(Iterator children, boolean isFragment) { + boolean foundDoctype = false; + while (children.hasNext()) { + XMLNode child = (XMLNode) children.next(); + + int error = ErrorState.NONE_ERROR; + int segType = FMUtil.SEG_WHOLE_TAG; + + switch (child.getNodeType()) { + case Node.ELEMENT_NODE : + if (! isFragment) { + Element childElem = (Element) child; + CMElementDeclaration ced = CMUtil.getDeclaration(childElem); + // Undefined element is valid. + if (ced == null) + continue; + // JSP (includes custom tags) and SSI are valid. + if (CMUtil.isForeign(childElem) || CMUtil.isSSI(ced)) + continue; // Defect 186774 + + // report error (invalid content) + error = ErrorState.INVALID_CONTENT_ERROR; + // mark the whole start tag as error. + segType = FMUtil.SEG_START_TAG; + } + break; + case Node.TEXT_NODE : + if (! isFragment) { + // TEXT node is valid when it contains white space characters only. + // Otherwise, it is invalid content. + if (((XMLText) child).isWhitespace()) + continue; + error = ErrorState.INVALID_CONTENT_ERROR; + segType = FMUtil.SEG_WHOLE_TAG; + } + break; + case Node.DOCUMENT_TYPE_NODE : + // DOCTYPE is also valid when it appears once and only once. + if (!foundDoctype) { + foundDoctype = true; + continue; + } + error = ErrorState.DUPLICATE_ERROR; + segType = FMUtil.SEG_WHOLE_TAG; + break; + case Node.COMMENT_NODE : + // always valid. + case Node.PROCESSING_INSTRUCTION_NODE : + continue; + default : + if (! isFragment) { + error = ErrorState.INVALID_CONTENT_ERROR; + segType = FMUtil.SEG_WHOLE_TAG; + } + break; + } + if (error != ErrorState.NONE_ERROR) { + Segment errorSeg = FMUtil.getSegment(child, segType); + if (errorSeg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(error, errorSeg, child))); + } + } + } + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementAncestorValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementAncestorValidator.java new file mode 100644 index 0000000000..b4c26b4561 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementAncestorValidator.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.common.contentmodel.CMNode; +import org.eclipse.wst.html.core.HTMLCMProperties; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Element; + +public class HTMLElementAncestorValidator extends PrimeValidator { + + /** + * HTMLElementAncestorValidator constructor comment. + */ + public HTMLElementAncestorValidator() { + 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 == HTMLElementAncestorValidator.class) || super.isAdapterForType(type)); + } + + /** + * Check exclusion which is defined in only HTML DTD (SGML). + */ + public void validate(IndexedRegion node) { + Element target = (Element) node; + if (CMUtil.isForeign(target)) + return; + CMElementDeclaration dec = CMUtil.getDeclaration(target); + if (dec == null) + return; // cannot validate. + if (!CMUtil.isHTML(dec)) + return; // no need to validate + if (!dec.supports(HTMLCMProperties.PROHIBITED_ANCESTORS)) + return; // cannot validate. + CMNamedNodeMap prohibited = (CMNamedNodeMap) dec.getProperty(HTMLCMProperties.PROHIBITED_ANCESTORS); + if (prohibited.getLength() <= 0) + return; // no prohibited ancestors. + + Element parent = SMUtil.getParentElement(target); + while (parent != null) { + CMNode pdec = prohibited.getNamedItem(parent.getNodeName()); + if (pdec != null) { // prohibited element is found in ancestors. + Segment errorSeg = FMUtil.getSegment((XMLNode) node, FMUtil.SEG_START_TAG); + if (errorSeg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(ErrorState.INVALID_CONTENT_ERROR, errorSeg, target))); + break; // If one prohibited ancestor is found, it's enough. + } + parent = SMUtil.getParentElement(parent); + } + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementContentValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementContentValidator.java new file mode 100644 index 0000000000..007c225719 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLElementContentValidator.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.document.XMLText; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +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.getChildNodes()); + } + + private void validateContent(Element parent, NodeList children) { + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child == null) + continue; + + // perform actual validation + validateNode(parent, child); + } + } + +// 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 occurence should be retreive 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 void validateNode(Element target, Node child) { + // 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; + CMElementDeclaration edec = CMUtil.getDeclaration(target); + 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; + } + 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 (((XMLText) child).isWhitespace()) + 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; + 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((XMLNode) child, segType); + if (errorSeg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(error, errorSeg, child))); + } + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleDocumentValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleDocumentValidator.java new file mode 100644 index 0000000000..d3630a3626 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleDocumentValidator.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +class HTMLSimpleDocumentValidator extends CompositeValidator { + + /** + * HTMLSimpleDocumentValidator constructor comment. + */ + public HTMLSimpleDocumentValidator() { + super(); + + ValidationComponent[] validators = new ValidationComponent[2]; + + validators[0] = new HTMLDocumentContentValidator(); + validators[1] = new SyntaxValidator(); + + register(validators); + } + + /** + * 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 == HTMLSimpleDocumentValidator.class) || super.isAdapterForType(type)); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleValidator.java new file mode 100644 index 0000000000..47b6ff8f3e --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLSimpleValidator.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +class HTMLSimpleValidator extends CompositeValidator { + + /** + * HTMLSimpleValidator constructor comment. + */ + public HTMLSimpleValidator() { + super(); + + ValidationComponent[] validators = new ValidationComponent[5]; + + validators[0] = new HTMLAttributeValidator(); + validators[1] = new HTMLElementContentValidator(); + validators[2] = new SyntaxValidator(); + validators[3] = new HTMLElementAncestorValidator(); + validators[4] = new NamespaceValidator(); + + register(validators); + } + + /** + * 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 == HTMLSimpleValidator.class) || super.isAdapterForType(type)); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLValidationAdapterFactory.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLValidationAdapterFactory.java new file mode 100644 index 0000000000..68185c317f --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/HTMLValidationAdapterFactory.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.eclipse.wst.sse.core.AbstractAdapterFactory; +import org.eclipse.wst.sse.core.AdapterFactory; +import org.eclipse.wst.sse.core.INodeAdapter; +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.w3c.dom.Node; + +public class HTMLValidationAdapterFactory extends AbstractAdapterFactory { + + private static HTMLValidationAdapterFactory instance = null; + + /** + * HTMLValidationAdapterFactory constructor comment. + */ + public HTMLValidationAdapterFactory() { + super(ValidationAdapter.class, true); + } + + /** + * HTMLValidationAdapterFactory constructor comment. + * @param adapterKey java.lang.Object + * @param registerAdapters boolean + */ + public HTMLValidationAdapterFactory(Object adapterKey, boolean registerAdapters) { + super(adapterKey, registerAdapters); + } + + /** + */ + protected INodeAdapter createAdapter(INodeNotifier target) { + Node node = (Node) target; + switch (node.getNodeType()) { + case Node.DOCUMENT_NODE : + return new DocumentPropagatingValidator(); + case Node.ELEMENT_NODE : + return new ElementPropagatingValidator(); + default : + return new NullValidator(); + } + } + + /** + */ + public synchronized static HTMLValidationAdapterFactory getInstance() { + if (instance != null) + return instance; + instance = new HTMLValidationAdapterFactory(); + return instance; + } + + /** + * Overriding Object's clone() method + * This is used in IModelManager's IStructuredModel copying. + */ + public AdapterFactory copy() { + return getInstance(); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/MessageFactory.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/MessageFactory.java new file mode 100644 index 0000000000..0419e925d5 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/MessageFactory.java @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import java.text.MessageFormat; +import java.util.Hashtable; + +import org.eclipse.wst.html.core.internal.Logger; +import org.eclipse.wst.html.core.internal.nls.ResourceHandler; +import org.eclipse.wst.sse.core.validate.ValidationMessage; +import org.w3c.dom.Node; + +class MessageFactory implements ErrorState { + + + private static class ErrorTable { + private class Packet { + public Packet(String msg, int severity) { + this.msg = msg; + this.severity = severity; + } + + public String getMessage() { + return msg; + } + + public int getSeverity() { + return severity; + } + + private String msg = null; + private int severity = -1; + } + + public ErrorTable() { + } + + public void put(int state, String msg, int severity) { + Packet packet = new Packet(msg, severity); + map.put(new Integer(state), packet); + } + + public String getMessage(int state) { + return getPacket(state).getMessage(); + } + + public int getSeverity(int state) { + return getPacket(state).getSeverity(); + } + + private Packet getPacket(int state) { + return (Packet) map.get(new Integer(state)); + } + + private Hashtable map = new Hashtable(); + } + + private static interface NodeType { + static final int ATTRIBUTE = 0; + static final int ELEMENT = 1; + static final int DOCUMENT_TYPE = 2; + static final int TEXT = 3; + static final int COMMENT = 4; + static final int CDATA_SECTION = 5; + static final int PROCESSING_INSTRUCTION = 6; + static final int ENTITY_REFERENCE = 7; + + static final int MAX_TYPE = 8; + } + + // error messages + private static final String MSG_NO_ERROR = ResourceHandler.getString("No_error._UI_"); //$NON-NLS-1$ = "No error." + private static final String MSG_UNDEFINED_ATTR_ERROR = ResourceHandler.getString("Undefined_attribute_name_(_ERROR_"); //$NON-NLS-1$ = "Undefined attribute name ({0})." + private static final String MSG_UNDEFINED_VALUE_ERROR = ResourceHandler.getString("Undefined_attribute_value__ERROR_"); //$NON-NLS-1$ = "Undefined attribute value ({0})." + private static final String MSG_DUPLICATE_ATTR_ERROR = ResourceHandler.getString("Multiple_values_specified__ERROR_"); //$NON-NLS-1$ = "Multiple values specified for an attribute ({0})." + private static final String MSG_MISMATCHED_ATTR_ERROR = ResourceHandler.getString("Attribute_name_({0})_uses__ERROR_"); //$NON-NLS-1$ = "Attribute name ({0}) uses wrong case character." + private static final String MSG_INVALID_ATTR_ERROR = ResourceHandler.getString("Invalid_attribute_name_({0_ERROR_"); //$NON-NLS-1$ = "Invalid attribute name ({0})." + private static final String MSG_ATTR_NO_VALUE_ERROR = ResourceHandler.getString("Invalid_attribute_({0})._ERROR_"); //$NON-NLS-1$ = "Invalid attribute ({0})." + private static final String MSG_INVALID_CONTENT_ERROR = ResourceHandler.getString("Invalid_location_of_tag_({_ERROR_"); //$NON-NLS-1$ = "Invalid location of tag ({0})." + private static final String MSG_DUPLICATE_TAG_ERROR = ResourceHandler.getString("Duplicate_tag_({0})._ERROR_"); //$NON-NLS-1$ = "Duplicate tag ({0})." + private static final String MSG_MISSING_START_TAG_ERROR = ResourceHandler.getString("No_start_tag_(<{0}>)._ERROR_"); //$NON-NLS-1$ = "No start tag (<{0}>)." + private static final String MSG_MISSING_END_TAG_ERROR = ResourceHandler.getString("No_end_tag_(</{0}>)._ERROR_"); //$NON-NLS-1$ = "No end tag (</{0}>)." + private static final String MSG_UNNECESSARY_END_TAG_ERROR = ResourceHandler.getString("End_tag_(</{0}>)_not_neede_ERROR_"); //$NON-NLS-1$ = "End tag (</{0}>) not needed." + private static final String MSG_UNDEFINED_TAG_ERROR = ResourceHandler.getString("Unknown_tag_({0})._ERROR_"); //$NON-NLS-1$ = "Unknown tag ({0})." + private static final String MSG_MISMATCHED_TAG_ERROR = ResourceHandler.getString("Tag_name_({0})_uses_wrong__ERROR_"); //$NON-NLS-1$ = "Tag name ({0}) uses wrong case character." + private static final String MSG_INVALID_TAG_ERROR = ResourceHandler.getString("Invalid_tag_name_({0})._ERROR_"); //$NON-NLS-1$ = "Invalid tag name ({0})." + private static final String MSG_INVALID_DIRECTIVE_ERROR = ResourceHandler.getString("Invalid_JSP_directive_({0}_ERROR_"); //$NON-NLS-1$ = "Invalid JSP directive ({0})." + private static final String MSG_INVALID_TEXT_ERROR = ResourceHandler.getString("Invalid_text_string_({0})._ERROR_"); //$NON-NLS-1$ = "Invalid text string ({0})." + private static final String MSG_INVALID_CHAR_ERROR = ResourceHandler.getString("Invalid_character_used_in__ERROR_"); //$NON-NLS-1$ = "Invalid character used in text string ({0})." + private static final String MSG_UNKNOWN_ERROR = ResourceHandler.getString("Unknown_error._ERROR_"); //$NON-NLS-1$ = "Unknown error." + private static final String MSG_UNCLOSED_START_TAG_ERROR = ResourceHandler.getString("Start_tag_(<{0}>)_not_clos_ERROR_"); //$NON-NLS-1$ = "Start tag (<{0}>) not closed." + private static final String MSG_UNCLOSED_END_TAG_ERROR = ResourceHandler.getString("End_tag_(</{0}>)_not_close_ERROR_"); //$NON-NLS-1$ = "End tag (</{0}>) not closed." + private static final String MSG_UNCLOSED_TAG_ERROR = ResourceHandler.getString("Tag_({0})_not_closed._ERROR_"); //$NON-NLS-1$ = "Tag ({0}) not closed." + private static final String MSG_MISMATCHED_ATTR_VALUE_ERROR = ResourceHandler.getString("Attribute_value_({0})_uses_ERROR_"); //$NON-NLS-1$ = "Attribute value ({0}) uses wrong case character." + private static final String MSG_UNCLOSED_COMMENT_ERROR = ResourceHandler.getString("Comment_not_closed._ERROR_"); //$NON-NLS-1$ = "Comment not closed." + private static final String MSG_UNCLOSED_DOCTYPE_ERROR = ResourceHandler.getString("DOCTYPE_declaration_not_cl_ERROR_"); //$NON-NLS-1$ = "DOCTYPE declaration not closed." + private static final String MSG_UNCLOSED_PI_ERROR = ResourceHandler.getString("Processing_instruction_not_ERROR_"); //$NON-NLS-1$ = "Processing instruction not closed." + private static final String MSG_UNCLOSED_CDATA_SECTION_ERROR = ResourceHandler.getString("CDATA_section_not_closed._ERROR_"); //$NON-NLS-1$ = "CDATA section not closed." + private static final String MSG_INVALID_EMPTY_ELEMENT_TAG = ResourceHandler.getString("_ERROR_Tag_({0})_should_be_an_empty-element_tag_1"); //$NON-NLS-1$ = "Tag ({0}) should be an empty-element tag." + private static final String MSG_UNCLOSED_ATTR_VALUE_ERROR = ResourceHandler.getString("_ERROR_Attribute_value_({0})_not_closed._1"); //$NON-NLS-1$ ="Attribute value ({0}) not closed." + private static ErrorTable[] errTables = new ErrorTable[NodeType.MAX_TYPE]; + + static { + for (int i = 0; i < NodeType.MAX_TYPE; i++) { + errTables[i] = new ErrorTable(); + } + // NOTE: The severities are just stub. They must be reviewed. + // -- 8/30/2001 + + // attribute error map + ErrorTable attrTable = errTables[NodeType.ATTRIBUTE];// short hand + attrTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + attrTable.put(UNDEFINED_NAME_ERROR, MSG_UNDEFINED_ATTR_ERROR, ValidationMessage.WARNING); + attrTable.put(UNDEFINED_VALUE_ERROR, MSG_UNDEFINED_VALUE_ERROR, ValidationMessage.WARNING); + attrTable.put(MISMATCHED_ERROR, MSG_MISMATCHED_ATTR_ERROR, ValidationMessage.WARNING); + attrTable.put(INVALID_NAME_ERROR, MSG_INVALID_ATTR_ERROR, ValidationMessage.WARNING); + attrTable.put(INVALID_ATTR_ERROR, MSG_ATTR_NO_VALUE_ERROR, ValidationMessage.WARNING); + attrTable.put(DUPLICATE_ERROR, MSG_DUPLICATE_ATTR_ERROR, ValidationMessage.WARNING); + attrTable.put(MISMATCHED_VALUE_ERROR, MSG_MISMATCHED_ATTR_VALUE_ERROR, ValidationMessage.ERROR); + //<<D210422 + attrTable.put(UNCLOSED_ATTR_VALUE, MSG_UNCLOSED_ATTR_VALUE_ERROR, ValidationMessage.WARNING); + //D210422 + // element error map + ErrorTable elemTable = errTables[NodeType.ELEMENT];// short hand + elemTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + elemTable.put(UNDEFINED_NAME_ERROR, MSG_UNDEFINED_TAG_ERROR, ValidationMessage.WARNING); + elemTable.put(INVALID_NAME_ERROR, MSG_INVALID_TAG_ERROR, ValidationMessage.ERROR); + elemTable.put(MISMATCHED_ERROR, MSG_MISMATCHED_TAG_ERROR, ValidationMessage.WARNING); + elemTable.put(MISMATCHED_END_TAG_ERROR, MSG_MISMATCHED_TAG_ERROR, ValidationMessage.ERROR); + elemTable.put(MISSING_START_TAG_ERROR, MSG_MISSING_START_TAG_ERROR, ValidationMessage.ERROR); + elemTable.put(MISSING_END_TAG_ERROR, MSG_MISSING_END_TAG_ERROR, ValidationMessage.WARNING); + elemTable.put(UNNECESSARY_END_TAG_ERROR, MSG_UNNECESSARY_END_TAG_ERROR, ValidationMessage.WARNING); + elemTable.put(INVALID_DIRECTIVE_ERROR, MSG_INVALID_DIRECTIVE_ERROR, ValidationMessage.ERROR); + elemTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + elemTable.put(DUPLICATE_ERROR, MSG_DUPLICATE_TAG_ERROR, ValidationMessage.WARNING); + elemTable.put(COEXISTENCE_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + elemTable.put(UNCLOSED_TAG_ERROR, MSG_UNCLOSED_START_TAG_ERROR, ValidationMessage.ERROR); + elemTable.put(UNCLOSED_END_TAG_ERROR, MSG_UNCLOSED_END_TAG_ERROR, ValidationMessage.ERROR); + elemTable.put(INVALID_EMPTY_ELEMENT_TAG, MSG_INVALID_EMPTY_ELEMENT_TAG, ValidationMessage.WARNING); + + // document type error map + ErrorTable docTable = errTables[NodeType.DOCUMENT_TYPE];// short hand + docTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + docTable.put(DUPLICATE_ERROR, MSG_DUPLICATE_TAG_ERROR, ValidationMessage.ERROR); + docTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + docTable.put(UNCLOSED_TAG_ERROR, MSG_UNCLOSED_DOCTYPE_ERROR, ValidationMessage.ERROR); + + // text error map + ErrorTable textTable = errTables[NodeType.TEXT]; + textTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + textTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_TEXT_ERROR, ValidationMessage.WARNING); + textTable.put(INVALID_CHAR_ERROR, MSG_INVALID_CHAR_ERROR, ValidationMessage.WARNING); + + // comment error map + ErrorTable commTable = errTables[NodeType.COMMENT]; + commTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + commTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + commTable.put(UNCLOSED_TAG_ERROR, MSG_UNCLOSED_COMMENT_ERROR, ValidationMessage.ERROR); + + // cdata section error map + ErrorTable cdatTable = errTables[NodeType.CDATA_SECTION]; + cdatTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + cdatTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + cdatTable.put(UNCLOSED_TAG_ERROR, MSG_UNCLOSED_CDATA_SECTION_ERROR, ValidationMessage.ERROR); + + // processing instruction error map + ErrorTable piTable = errTables[NodeType.PROCESSING_INSTRUCTION]; + piTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + piTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + piTable.put(UNCLOSED_TAG_ERROR, MSG_UNCLOSED_PI_ERROR, ValidationMessage.ERROR); + + // entity reference error map + ErrorTable erTable = errTables[NodeType.ENTITY_REFERENCE]; + erTable.put(NONE_ERROR, MSG_NO_ERROR, 0); + erTable.put(UNDEFINED_NAME_ERROR, MSG_UNDEFINED_TAG_ERROR, ValidationMessage.WARNING); + erTable.put(INVALID_CONTENT_ERROR, MSG_INVALID_CONTENT_ERROR, ValidationMessage.WARNING); + } + + /** + */ + public static ValidationMessage createMessage(ErrorInfo info) { + String errorMsg = getErrorMessage(info); + int errorSeverity = getErrorSeverity(info); + return new ValidationMessage(errorMsg, info.getOffset(), info.getLength(), errorSeverity); + } + + private static String getErrorMessage(ErrorInfo info) { + ErrorTable tab = getErrorTable(info.getTargetType()); + if (tab == null) + return MSG_UNKNOWN_ERROR; + + String template = tab.getMessage(info.getState()); + Object[] arguments = {info.getHint()}; + String s = null; + try { + s = MessageFormat.format(template, arguments); + } + catch (IllegalArgumentException e) { + Logger.logException(e); + s = template + ":" + arguments.toString(); //$NON-NLS-1$ + } + return s; + } + + /** + */ + private static int getErrorSeverity(ErrorInfo info) { + ErrorTable tab = getErrorTable(info.getTargetType()); + if (tab == null) + return 0; + return tab.getSeverity(info.getState()); + } + + private static ErrorTable getErrorTable(short nodetype) { + ErrorTable tab = null; + switch (nodetype) { + case Node.ATTRIBUTE_NODE : + tab = errTables[NodeType.ATTRIBUTE]; + break; + case Node.ELEMENT_NODE : + tab = errTables[NodeType.ELEMENT]; + break; + case Node.DOCUMENT_TYPE_NODE : + tab = errTables[NodeType.DOCUMENT_TYPE]; + break; + case Node.TEXT_NODE : + tab = errTables[NodeType.TEXT]; + break; + case Node.COMMENT_NODE : + tab = errTables[NodeType.COMMENT]; + break; + case Node.CDATA_SECTION_NODE : + tab = errTables[NodeType.CDATA_SECTION]; + break; + case Node.PROCESSING_INSTRUCTION_NODE : + tab = errTables[NodeType.PROCESSING_INSTRUCTION]; + break; + case Node.ENTITY_REFERENCE_NODE : + tab = errTables[NodeType.ENTITY_REFERENCE]; + break; + default : + return null; + } + return tab; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NamespaceValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NamespaceValidator.java new file mode 100644 index 0000000000..f2e605da44 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NamespaceValidator.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.document.XMLAttr; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class NamespaceValidator extends PrimeValidator implements ErrorState { + + private final static String XMLNS_PREFIX = "xmlns";//$NON-NLS-1$ + private final static String NS_SEPARATOR = ":";//$NON-NLS-1$ + + public NamespaceValidator() { + super(); + } + + public boolean isAdapterForType(Object type) { + return ((type == NamespaceValidator.class) || super.isAdapterForType(type)); + } + + public void validate(IndexedRegion node) { + Element target = (Element) node; + if (isXMLElement(target) && hasUnknownPrefix(target)) { + XMLElement e = (XMLElement) target; + if (!isValidPrefix(e.getPrefix(), target) && !e.isCommentTag()) { + // report unknown tag error. + Segment errorSeg = FMUtil.getSegment(e, FMUtil.SEG_START_TAG); + if (errorSeg != null) + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(UNDEFINED_NAME_ERROR, errorSeg, e))); + } + } + // (2) check prefix of each attr + NamedNodeMap attrs = target.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node n = attrs.item(i); + if (!(n instanceof XMLAttr)) + continue; + XMLAttr a = (XMLAttr) n; + String prefix = a.getPrefix(); + if ((prefix != null) && isUnknownAttr(a, target)) { + // The attr has unknown prefix. So, check it. + if (!isValidPrefix(prefix, target)) { + // report unknown attr error. + ITextRegion r = a.getNameRegion(); + if (r == null) + continue; + int a_offset = a.getNameRegionStartOffset(); + int a_length = a.getNameRegion().getLength(); + reporter.report(MessageFactory.createMessage(new ErrorInfoImpl(UNDEFINED_NAME_ERROR, new Segment(a_offset, a_length), a))); + } + } + } + } + + // private methods + private boolean isXMLElement(Element target) { + return target instanceof XMLElement; + } + + private boolean hasUnknownPrefix(Element target) { + return isUnknownElement(target) && CMUtil.isForeign(target); + } + + private boolean isUnknownElement(Element target) { + CMElementDeclaration dec = CMUtil.getDeclaration(target); + return dec == null; + } + + private boolean isUnknownAttr(XMLAttr attr, Element target) { + CMElementDeclaration dec = CMUtil.getDeclaration(target); + if (dec == null) + return true; // unknown. + CMNamedNodeMap adecls = dec.getAttributes(); + CMAttributeDeclaration adec = (CMAttributeDeclaration) adecls.getNamedItem(attr.getName()); + return adec == null; + } + + private boolean isValidPrefix(String prefix, Element e) { + if (prefix.equals(XMLNS_PREFIX)) + return true; // "xmlns:foo" attr is always valid. + + // (1) check the element has the namespace definition or not. + if (isValidPrefixWithinElement(prefix, e)) + return true; + + // (2) check ancestors of the element have the namespace definition or not. + Element parent = SMUtil.getParentElement(e); + while (parent != null) { + if (isValidPrefixWithinElement(prefix, parent)) + return true; + parent = SMUtil.getParentElement(parent); + } + return false; + } + + private boolean isValidPrefixWithinElement(String prefix, Element e) { + String ns = XMLNS_PREFIX + NS_SEPARATOR + prefix; + NamedNodeMap attrs = e.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Node n = attrs.item(i); + if (n == null) + continue; + if (n.getNodeType() != Node.ATTRIBUTE_NODE) + continue; + if (ns.equals(((Attr) n).getName())) + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NullValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NullValidator.java new file mode 100644 index 0000000000..1a9cbc0e7a --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/NullValidator.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.validate.ValidationReporter; +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +/** + * NullValidator class is intended to be a replacement of null + * for ValidationComponent type. + */ +final class NullValidator extends ValidationComponent { + + public NullValidator() { + super(); + } + + public void validate(IndexedRegion node) { + return; + } + + public void setReporter(ValidationReporter reporter) { + return; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/PrimeValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/PrimeValidator.java new file mode 100644 index 0000000000..6278a3ab23 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/PrimeValidator.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.eclipse.wst.xml.core.internal.validate.ValidationComponent; + +abstract class PrimeValidator extends ValidationComponent { + + /** + * PrimeValidator constructor comment. + */ + public PrimeValidator() { + super(); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SMUtil.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SMUtil.java new file mode 100644 index 0000000000..97adcf80fc --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SMUtil.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +final class SMUtil { + + private SMUtil() { + super(); + } + + /* get an ancestor element ignoring implicit ones. */ + public static Element getParentElement(Node child) { + if (child == null) + return null; + + Node p = child.getParentNode(); + while (p != null) { + if (p.getNodeType() == Node.ELEMENT_NODE) { + return (Element) p; + } + p = p.getParentNode(); + } + return null; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/Segment.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/Segment.java new file mode 100644 index 0000000000..2e5db726e5 --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/Segment.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; + +public class Segment { + + private int offset = 0; + private int length = 0; + + /** + */ + public Segment(int offset, int length) { + super(); + this.offset = offset; + this.length = length; + } + + public Segment(IStructuredDocumentRegion region) { + super(); + this.offset = region.getStartOffset(); + this.length = region.getLength(); + } + + /** + * NOTE: 'start' and 'end' must be the start and end of the contiguous regions. + * Otherwise, this class cannot work correctly. + */ + public Segment(IStructuredDocumentRegion start, IStructuredDocumentRegion end) { + super(); + this.offset = start.getStartOffset(); + int endOffset = (end == null) ? start.getEndOffset() : end.getEndOffset(); + this.length = endOffset - this.offset; + } + + //public Segment(ITextRegion start, ITextRegion end) { + // super(); + // this.offset = start.getStartOffset(); + // int endOffset = (end == null) ? start.getEndOffset() : end.getEndOffset(); + // this.length = endOffset - this.offset; + //} + /** + */ + public int getLength() { + return this.length; + } + + /** + */ + public int getOffset() { + return this.offset; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java new file mode 100644 index 0000000000..01b217727e --- /dev/null +++ b/bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java @@ -0,0 +1,345 @@ +/******************************************************************************* + * Copyright (c) 2004 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.validate; + +import java.util.Iterator; + +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.html.core.document.HTMLDocumentTypeEntry; +import org.eclipse.wst.html.core.document.HTMLDocumentTypeRegistry; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.document.InvalidCharacterException; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.internal.document.SourceValidator; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +class SyntaxValidator extends PrimeValidator implements ErrorState { + + + static private boolean isValidRegion(ITextRegion rgn) { + String type = rgn.getType(); + if (type == null) + return false; // no type is invalid. + if (type == XMLRegionContext.XML_END_TAG_OPEN || type == XMLRegionContext.XML_TAG_NAME || type == XMLRegionContext.XML_TAG_CLOSE) { + return true; + } + return false; + } + + static private String getTagName(IStructuredDocumentRegion tag) { + ITextRegionList regions = tag.getRegions(); + Iterator iter = regions.iterator(); + while (iter.hasNext()) { + ITextRegion rgn = (ITextRegion) iter.next(); + if (rgn.getType() == XMLRegionContext.XML_TAG_NAME) + return tag.getText(rgn); + } + return "";//$NON-NLS-1$ + } + + static private boolean isEmptyContent(CMElementDeclaration decl) { + return (decl != null) && (decl.getContentType() == CMElementDeclaration.EMPTY); + } + + public SyntaxValidator() { + super(); + } + + public boolean isAdapterForType(Object type) { + return ((type == SyntaxValidator.class) || super.isAdapterForType(type)); + } + + class ElementInfo { + public ElementInfo() { + super(); + } + + public XMLElement target = null; + public CMElementDeclaration decl = null; + public IStructuredDocumentRegion startTag = null; + public IStructuredDocumentRegion endTag = null; + public boolean hasStartTag = false; + public boolean hasEndTag = false; + public boolean isXHTML = false; + } + + public void validate(IndexedRegion indexedNode) { + Node node = (Node) indexedNode; + validateChildren(node); + + if (node.getNodeType() != Node.ELEMENT_NODE) + return; + if (!(node instanceof XMLElement)) + return; + + ElementInfo info = new ElementInfo(); + info.target = (XMLElement) node; + + // gather information to validate from target at once. + getInfo(info); + + if (info.target.isGlobalTag()) { + validateTags(info); + validateNames(info); + if (info.decl != null && info.isXHTML) { + validateTagCase(info); + } + } + } + + private void getInfo(ElementInfo info) { + info.decl = CMUtil.getDeclaration(info.target); + info.startTag = info.target.getStartStructuredDocumentRegion(); + info.endTag = info.target.getEndStructuredDocumentRegion(); + + info.hasStartTag = (info.startTag != null); + info.hasEndTag = (info.endTag != null); + + Document doc = info.target.getOwnerDocument(); + if (!(doc instanceof XMLDocument)) + return; + String typeid = ((XMLDocument) doc).getDocumentTypeId(); + if (typeid != null) { + HTMLDocumentTypeEntry entry = HTMLDocumentTypeRegistry.getInstance().getEntry(typeid); + info.isXHTML = (entry != null && entry.isXMLType()); + } + } + + class TagErrorInfoImpl extends AbstractErrorInfo { + private String hint = null; + + public TagErrorInfoImpl(int state, IStructuredDocumentRegion tag, String hint) { + super(state, new Segment(tag)); + this.hint = hint; + } + + public String getHint() { + return hint; + } + + public short getTargetType() { + return Node.ELEMENT_NODE; + } + } + + private boolean isEndTagCorrupted(ElementInfo info) { + ITextRegionList regions = info.endTag.getRegions(); + if (regions == null || regions.isEmpty()) + return false; + Iterator iter = regions.iterator(); + while (iter.hasNext()) { + ITextRegion rgn = (ITextRegion) iter.next(); + if (!isValidRegion(rgn)) + return true; // found invalid region type. + } + return false; // all regions are valid. + } + + private String getEndTagFullText(ElementInfo info) { + String hint = "";//$NON-NLS-1$ + ITextRegionList regions = info.endTag.getRegions(); + Iterator iter = regions.iterator(); + while (iter.hasNext()) { + ITextRegion rgn = (ITextRegion) iter.next(); + String type = rgn.getType(); + if (type == null) + continue; + if (type == XMLRegionContext.XML_END_TAG_OPEN || type == XMLRegionContext.XML_TAG_CLOSE) + continue; + hint += info.endTag.getFullText(rgn); + } + return hint; + } + + private void reportCorruptedEndTagError(ElementInfo info) { + String hint = getEndTagFullText(info); + TagErrorInfoImpl error = new TagErrorInfoImpl(UNDEFINED_NAME_ERROR, info.endTag, hint); + this.reporter.report(MessageFactory.createMessage(error)); + } + + private void validateTags(ElementInfo info) { + if (info.hasStartTag) { + if (!info.target.isStartTagClosed()) { + // Mark the whole START tag as an error segment. + Segment errorSeg = new Segment(info.startTag); + report(UNCLOSED_TAG_ERROR, errorSeg, info.target); + } + } + else { + if (info.hasEndTag) { + if (info.decl != null) { + if (CMUtil.isHTML(info.decl) && !info.target.hasChildNodes()) { + if (info.target.isContainer()) { + // Set the error mark to the start of the element. + Segment errorSeg = new Segment(info.target.getStartOffset(), 0); + report(MISSING_START_TAG_ERROR, errorSeg, info.target); + } + else { + // Mark the whole END tag as an error segment. + Segment errorSeg = new Segment(info.endTag); + report(UNNECESSARY_END_TAG_ERROR, errorSeg, info.target); + } + } + else if (info.isXHTML) { + Segment errorSeg = new Segment(info.target.getStartOffset(), 0); + report(MISSING_START_TAG_ERROR, errorSeg, info.target); + } + } + } + } + + if (info.hasEndTag) { + if (!info.target.isClosed()) { + // Set the whole END tag as error segment. + Segment errorSeg = new Segment(info.endTag); + report(UNCLOSED_END_TAG_ERROR, errorSeg, info.target); + } + } + else { + if (info.isXHTML) { // XHTML + if (!info.target.isEmptyTag()) { + if (isEmptyContent(info.decl)) { + // EMPTY element should be written in <.../> form. + Segment errorSeg = FMUtil.getSegment(info.target, FMUtil.SEG_START_TAG); + report(INVALID_EMPTY_ELEMENT_TAG, errorSeg, info.target); + } + else { + // end tag is required. + Segment errorSeg = new Segment(info.target.getEndOffset(), 0); + report(MISSING_END_TAG_ERROR, errorSeg, info.target); + } + } + } + else { // HTML + if (info.hasStartTag) { + if (info.decl != null && CMUtil.isHTML(info.decl) && !info.target.isEmptyTag() && !CMUtil.isEndTagOmissible(info.decl)) { + // Set the error mark to the end of the element. + Segment errorSeg = new Segment(info.target.getEndOffset(), 0); + report(MISSING_END_TAG_ERROR, errorSeg, info.target); + } + } + } + } + } + + /* perform validation about tag name definition. */ + private void validateNames(ElementInfo info) { + boolean corrupted = info.hasEndTag && isEndTagCorrupted(info); + if (info.decl == null) { + // If no declaration is retrieved, the target is really + // unknown element. + if (!info.hasStartTag && corrupted) { + reportCorruptedEndTagError(info); + } + else { + Segment errorSeg = FMUtil.getSegment(info.target, FMUtil.SEG_START_TAG); + report(UNDEFINED_NAME_ERROR, errorSeg, info.target); + } + } + else { + // Even if a declaration could be retrieved, if the end + // tag is corrupted, it should be reported as undefined + // name. (D202493) + if (corrupted) { + reportCorruptedEndTagError(info); + } + } + } + + /* perform validation tag case only for XHTML document */ + private void validateTagCase(ElementInfo info) { + String declared = info.decl.getElementName(); + String startTagName = "";//$NON-NLS-1$ + String endTagName = "";//$NON-NLS-1$ + if (declared == null) + return; + + // start tag + if (info.hasStartTag) { + startTagName = getTagName(info.startTag); + if (!declared.equals(startTagName)) { + TagErrorInfoImpl error = new TagErrorInfoImpl(MISMATCHED_ERROR, info.startTag, startTagName); + this.reporter.report(MessageFactory.createMessage(error)); + } + } + // end tag + if (info.hasEndTag) { + endTagName = getTagName(info.endTag); + if (!info.hasStartTag || (!endTagName.equals(startTagName))) { + if (!declared.equals(endTagName)) { + TagErrorInfoImpl error = new TagErrorInfoImpl(MISMATCHED_ERROR, info.endTag, endTagName); + this.reporter.report(MessageFactory.createMessage(error)); + } + } + } + } + + private void validateChildren(Node target) { + if ((target.getNodeType() == Node.ELEMENT_NODE) && CMUtil.isForeign((Element) target)) + return; + + for (Node child = target.getFirstChild(); child != null; child = child.getNextSibling()) { + switch (child.getNodeType()) { + case Node.TEXT_NODE : + { + XMLNode text = (XMLNode) child; + int charOffset = validateTextSource(text); + if (charOffset >= 0) { + charOffset += text.getStartOffset(); + Segment errorSeg = new Segment(charOffset, 1); + if (errorSeg != null) + report(INVALID_CHAR_ERROR, errorSeg, text); + } + break; + } + case Node.COMMENT_NODE : + case Node.DOCUMENT_TYPE_NODE : + case Node.PROCESSING_INSTRUCTION_NODE : + case Node.CDATA_SECTION_NODE : + { + XMLNode tag = (XMLNode) child; + if (!tag.isClosed()) { + Segment errorSeg = FMUtil.getSegment(tag, FMUtil.SEG_WHOLE_TAG); + if (errorSeg != null) + report(UNCLOSED_TAG_ERROR, errorSeg, tag); + } + break; + } + default : + break; + } + } + } + + private int validateTextSource(XMLNode text) { + try { + SourceValidator validator = new SourceValidator(text); + validator.validateSource(text.getSource()); + } + catch (InvalidCharacterException ex) { + return ex.getOffset(); + } + return -1; + } + + private void report(int state, Segment errorSeg, Node node) { + ErrorInfo info = new ErrorInfoImpl(state, errorSeg, node); + reporter.report(MessageFactory.createMessage(info)); + } +}
\ No newline at end of file |