diff options
author | nitind | 2004-11-23 19:23:00 +0000 |
---|---|---|
committer | nitind | 2004-11-23 19:23:00 +0000 |
commit | 958d79a852fed44e64fc5297402dd49f5f81d24c (patch) | |
tree | e548c626c38f28f0e538b875f82058cfefd6dcd8 /bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java | |
parent | b0f7b2695ef3646cc3f23f6712d409ff46b2aa7a (diff) | |
download | webtools.sourceediting-958d79a852fed44e64fc5297402dd49f5f81d24c.tar.gz webtools.sourceediting-958d79a852fed44e64fc5297402dd49f5f81d24c.tar.xz webtools.sourceediting-958d79a852fed44e64fc5297402dd49f5f81d24c.zip |
initial check-in
Diffstat (limited to 'bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java')
-rw-r--r-- | bundles/org.eclipse.wst.html.core/src/org/eclipse/wst/html/core/validate/SyntaxValidator.java | 345 |
1 files changed, 345 insertions, 0 deletions
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 |