blob: a6d19bf645fe7a0013239974ff3e36bd5808c945 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.xml.core.internal.validation;
import java.util.Locale;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.validation.AbstractValidator;
import org.eclipse.wst.validation.ValidationResult;
import org.eclipse.wst.validation.ValidationState;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.operations.IWorkbenchContext;
import org.eclipse.wst.validation.internal.operations.LocalizedMessage;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
import org.eclipse.wst.validation.internal.provisional.core.IValidator;
import org.eclipse.wst.xml.core.internal.Logger;
import org.eclipse.wst.xml.core.internal.XMLCoreMessages;
import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
import org.eclipse.wst.xml.core.internal.preferences.XMLCorePreferenceNames;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.w3c.dom.Node;
/**
* Basic XML syntax checking step.
*/
public class MarkupValidator extends AbstractValidator implements IValidator {
public static final int ELEMENT_ERROR_LIMIT = 25;
public static final int PARTIAL = 1;
public static final int TOTAL = 0;
private String DQUOTE = "\""; //$NON-NLS-1$
private String SQUOTE = "'"; //$NON-NLS-1$
private IDocument fDocument;
private IContentType fRootContentType = null;
private IReporter fReporter = null;
private static final String ANNOTATIONMSG = AnnotationMsg.class.getName();
public void getAnnotationMsg(IReporter reporter, int problemId, LocalizedMessage message, Object attributeValueText, int len){
AnnotationMsg annotation = new AnnotationMsg(problemId, attributeValueText,len);
message.setAttribute(ANNOTATIONMSG, annotation);
reporter.addMessage(this, message);
}
private void addAttributeError(String messageText, String attributeValueText, int start, int length, int problemId, IStructuredDocumentRegion sdRegion, IReporter reporter, int messageSeverity) {
if (sdRegion.isDeleted()) {
return;
}
int lineNo = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(messageSeverity, messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(lineNo);
getAnnotationMsg(reporter, problemId, message, attributeValueText,length);
}
private void checkAttributesInEndTag(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
ITextRegionList textRegions = structuredDocumentRegion.getRegions();
int errorCount = 0;
int start = structuredDocumentRegion.getEndOffset();
int end = structuredDocumentRegion.getEndOffset();
for (int i = 0; (i < textRegions.size()) && (errorCount < ELEMENT_ERROR_LIMIT) && !structuredDocumentRegion.isDeleted(); i++) {
ITextRegion textRegion = textRegions.get(i);
if ((textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) || (textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) || (textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)) {
if (start > structuredDocumentRegion.getStartOffset(textRegion)) {
start = structuredDocumentRegion.getStartOffset(textRegion);
}
end = structuredDocumentRegion.getEndOffset(textRegion);
errorCount++;
}
}
// create one error for all attributes in the end tag
if (errorCount > 0) {
// Position p = new Position(start, end - start);
String messageText = XMLCoreMessages.End_tag_has_attributes;
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.END_TAG_WITH_ATTRIBUTES), messageText);
message.setOffset(start);
message.setLength(end - start);
message.setLineNo(getLineNumber(start));
getAnnotationMsg(reporter, ProblemIDsXML.AttrsInEndTag, message, null,end-start);
}
}
private void checkClosingBracket(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
ITextRegionList regions = structuredDocumentRegion.getRegions();
ITextRegion r = null;
boolean closed = false;
for (int i = 0; (i < regions.size()) && !structuredDocumentRegion.isDeleted(); i++) {
r = regions.get(i);
if ((r.getType() == DOMRegionContext.XML_TAG_CLOSE) || (r.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE)) {
closed = true;
}
}
if (!closed) {
String messageText = XMLCoreMessages.ReconcileStepForMarkup_6;
int start = structuredDocumentRegion.getStartOffset();
int length = structuredDocumentRegion.getText().trim().length();
int lineNo = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_CLOSING_BRACKET) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(lineNo);
getAnnotationMsg(reporter, ProblemIDsXML.MissingClosingBracket, message, null,length);
}
}
private void checkEmptyTag(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
// navigate to name
ITextRegionList regions = structuredDocumentRegion.getRegions();
if (regions.size() == 2) {
// missing name region
if ((regions.get(0).getType() == DOMRegionContext.XML_TAG_OPEN) && (regions.get(1).getType() == DOMRegionContext.XML_TAG_CLOSE)) {
String messageText = XMLCoreMessages.ReconcileStepForMarkup_3;
int start = structuredDocumentRegion.getStartOffset();
int length = structuredDocumentRegion.getLength();
int lineNo = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_TAG_NAME) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(lineNo);
getAnnotationMsg(reporter, ProblemIDsXML.EmptyTag, message, null,length);
}
}
}
private int getLineNumber(int start) {
int lineNo = -1;
try {
lineNo = getDocument().getLineOfOffset(start) + 1;
}
catch (BadLocationException e) {
Logger.logException(e);
}
return lineNo;
}
private void checkForAttributeValue(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
// check for attributes without a value
// track the attribute/equals/value sequence using a state of 0, 1 ,2
// representing the name, =, and value, respectively
int attrState = 0;
ITextRegionList textRegions = structuredDocumentRegion.getRegions();
// ReconcileAnnotationKey key = createKey(structuredDocumentRegion,
// getScope());
int errorCount = 0;
for (int i = 0; (i < textRegions.size()) && (errorCount < ELEMENT_ERROR_LIMIT); i++) {
ITextRegion textRegion = textRegions.get(i);
if ((textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) || isTagCloseTextRegion(textRegion)) {
// dangling name and '='
if ((attrState == 2) && (i >= 2)) {
// create annotation
ITextRegion nameRegion = textRegions.get(i - 2);
if (!(nameRegion instanceof ITextRegionContainer)) {
Object[] args = {structuredDocumentRegion.getText(nameRegion)};
String messageText = NLS.bind(XMLCoreMessages.Attribute__is_missing_a_value, args);
int start = structuredDocumentRegion.getStartOffset(nameRegion);
int end = structuredDocumentRegion.getEndOffset();
int lineNo = getLineNumber(start);
int textLength = structuredDocumentRegion.getText(nameRegion).trim().length();
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.ATTRIBUTE_HAS_NO_VALUE) , messageText);
message.setOffset(start);
message.setLength(textLength);
message.setLineNo(lineNo);
// quick fix info
ITextRegion equalsRegion = textRegions.get(i - 2 + 1);
int insertOffset = structuredDocumentRegion.getTextEndOffset(equalsRegion) - end;
Object[] additionalFixInfo = {structuredDocumentRegion.getText(nameRegion), new Integer(insertOffset)};
getAnnotationMsg(reporter, ProblemIDsXML.MissingAttrValue, message, additionalFixInfo,textLength);
errorCount++;
}
}
// name but no '=' (XML only)
else if ((attrState == 1) && (i >= 1)) {
// create annotation
ITextRegion previousRegion = textRegions.get(i - 1);
if (!(previousRegion instanceof ITextRegionContainer)) {
Object[] args = {structuredDocumentRegion.getText(previousRegion)};
String messageText = NLS.bind(XMLCoreMessages.Attribute__has_no_value, args);
int start = structuredDocumentRegion.getStartOffset(previousRegion);
int textLength = structuredDocumentRegion.getText(previousRegion).trim().length();
int lineNo = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.ATTRIBUTE_HAS_NO_VALUE), messageText);
message.setOffset(start);
message.setLength(textLength);
message.setLineNo(lineNo);
getAnnotationMsg(reporter, ProblemIDsXML.NoAttrValue, message, structuredDocumentRegion.getText(previousRegion),textLength);
errorCount++;
}
}
attrState = 1;
}
else if (textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
attrState = 2;
}
else if (textRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
attrState = 0;
}
}
}
private void checkForSpaceBeforeName(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
String sdRegionText = structuredDocumentRegion.getFullText();
if (sdRegionText.startsWith(" ")) { //$NON-NLS-1$
IStructuredDocumentRegion prev = structuredDocumentRegion.getPrevious();
if (prev != null) {
// this is possibly the case of "< tag"
if ((prev.getRegions().size() == 1) && isStartTag(prev)) {
// add the error for preceding space in tag name
String messageText = XMLCoreMessages.ReconcileStepForMarkup_2;
int start = structuredDocumentRegion.getStartOffset();
// find length of whitespace
int length = sdRegionText.trim().equals("") ? sdRegionText.length() : sdRegionText.indexOf(sdRegionText.trim()); //$NON-NLS-1$
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.WHITESPACE_BEFORE_TAGNAME) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(getLineNumber(start));
getAnnotationMsg(reporter, ProblemIDsXML.SpacesBeforeTagName, message, null,length);
}
}
}
}
private void checkNoNamespaceInPI(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
// navigate to name
ITextRegionList regions = structuredDocumentRegion.getRegions();
ITextRegion r = null;
int errorCount = 0;
for (int i = 0; (i < regions.size()) && (errorCount < ELEMENT_ERROR_LIMIT) && !structuredDocumentRegion.isDeleted(); i++) {
r = regions.get(i);
if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
String piText = structuredDocumentRegion.getText(r);
int index = piText.indexOf(":"); //$NON-NLS-1$
if (index != -1) {
String messageText = XMLCoreMessages.ReconcileStepForMarkup_4;
int start = structuredDocumentRegion.getStartOffset(r) + index;
int length = piText.trim().length() - index;
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.NAMESPACE_IN_PI_TARGET) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(getLineNumber(start));
getAnnotationMsg(reporter, ProblemIDsXML.NamespaceInPI, message, null,length);
errorCount++;
}
}
}
}
private void checkQuotesForAttributeValues(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
ITextRegionList regions = structuredDocumentRegion.getRegions();
ITextRegion r = null;
String attrValueText = ""; //$NON-NLS-1$
int errorCount = 0;
for (int i = 0; (i < regions.size()) && (errorCount < ELEMENT_ERROR_LIMIT); i++) {
r = regions.get(i);
if (r.getType() != DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
continue;
}
attrValueText = structuredDocumentRegion.getText(r);
// attribute value includes quotes in the string
// split up attribute value on quotes
/*
* WORKAROUND till
* http://dev.icu-project.org/cgi-bin/icu-bugs/incoming?findid=5207
* is fixed. (Also see BUG143628)
*/
java.util.StringTokenizer st = new java.util.StringTokenizer(attrValueText, "\"'", true); //$NON-NLS-1$
int size = st.countTokens();
// get the pieces of the attribute value
String one = "", two = ""; //$NON-NLS-1$ //$NON-NLS-2$
if (size > 0) {
one = st.nextToken();
}
if (size > 1) {
two = st.nextToken();
}
if (size > 2) {
// should be handled by parsing...
// as in we can't have an attribute value like: <element
// attr="a"b"c"/>
// and <element attr='a"b"c' /> is legal
continue;
}
if (size == 1) {
if (one.equals(DQUOTE) || one.equals(SQUOTE)) {
// missing closing quote
String message = XMLCoreMessages.ReconcileStepForMarkup_0;
addAttributeError(message, attrValueText, structuredDocumentRegion.getStartOffset(r), attrValueText.trim().length(), ProblemIDsXML.Unclassified, structuredDocumentRegion, reporter, getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_CLOSING_QUOTE));
errorCount++;
}
else {
// missing both
String message = XMLCoreMessages.ReconcileStepForMarkup_1;
addAttributeError(message, attrValueText, structuredDocumentRegion.getStartOffset(r), attrValueText.trim().length(), ProblemIDsXML.AttrValueNotQuoted, structuredDocumentRegion, reporter, getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_CLOSING_QUOTE));
errorCount++;
}
}
else if (size == 2) {
if ((one.equals(SQUOTE) && !two.equals(SQUOTE)) || (one.equals(DQUOTE) && !two.equals(DQUOTE))) {
// missing closing quote
String message = XMLCoreMessages.ReconcileStepForMarkup_0;
addAttributeError(message, attrValueText, structuredDocumentRegion.getStartOffset(r), attrValueText.trim().length(), ProblemIDsXML.Unclassified, structuredDocumentRegion, reporter, getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_CLOSING_QUOTE));
errorCount++;
}
}
}
// end of region for loop
}
private void checkStartEndTagPairs(IStructuredDocumentRegion sdRegion, IReporter reporter) {
if (sdRegion.isDeleted()) {
return;
}
// check start/end tag pairs
IDOMNode xmlNode = getXMLNode(sdRegion);
if (xmlNode == null) {
return;
}
boolean selfClosed = false;
String tagName = null;
/**
* For tags that aren't meant to be EMPTY, make sure it's empty or has an end tag
*/
if (xmlNode.isContainer()) {
IStructuredDocumentRegion endRegion = xmlNode.getEndStructuredDocumentRegion();
if (endRegion == null) {
IStructuredDocumentRegion startRegion = xmlNode.getStartStructuredDocumentRegion();
if (startRegion != null && !startRegion.isDeleted() && DOMRegionContext.XML_TAG_OPEN.equals(startRegion.getFirstRegion().getType())) {
// analyze the tag (check self closing)
ITextRegionList regions = startRegion.getRegions();
ITextRegion r = null;
int start = sdRegion.getStart();
int length = sdRegion.getTextLength();
for (int i = 0; i < regions.size(); i++) {
r = regions.get(i);
if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
tagName = sdRegion.getText(r);
start = sdRegion.getStartOffset(r);
length = r.getTextLength();
}
else if (r.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
selfClosed = true;
}
}
if (!selfClosed && (tagName != null)) {
Object[] args = {tagName};
String messageText = NLS.bind(XMLCoreMessages.Missing_end_tag_, args);
int lineNumber = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_END_TAG) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(lineNumber);
Object[] additionalFixInfo = getStartEndFixInfo(xmlNode, tagName, r);
getAnnotationMsg(reporter, ProblemIDsXML.MissingEndTag, message, additionalFixInfo,length);
}
}
}
else {
IStructuredDocumentRegion startRegion = xmlNode.getStartStructuredDocumentRegion();
if (startRegion == null || startRegion.isDeleted()) {
// analyze the tag (check self closing)
ITextRegionList regions = endRegion.getRegions();
ITextRegion r = null;
int start = sdRegion.getStart();
int length = sdRegion.getTextLength();
for (int i = 0; i < regions.size(); i++) {
r = regions.get(i);
if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
tagName = sdRegion.getText(r);
start = sdRegion.getStartOffset(r);
length = r.getTextLength();
}
}
if (tagName != null) {
Object[] args = {tagName};
String messageText = NLS.bind(XMLCoreMessages.Missing_start_tag_, args);
int lineNumber = getLineNumber(start);
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.MISSING_START_TAG), messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(lineNumber);
Object[] additionalFixInfo = getStartEndFixInfo(xmlNode, tagName, r);
getAnnotationMsg(reporter, ProblemIDsXML.MissingStartTag, message, additionalFixInfo,length);
}
}
}
}
/*
* Check for an end tag that has no start tag
*/
else {
IStructuredDocumentRegion startRegion = xmlNode.getStartStructuredDocumentRegion();
if (startRegion == null) {
IStructuredDocumentRegion endRegion = xmlNode.getEndStructuredDocumentRegion();
if (!endRegion.isDeleted()) {
// get name
ITextRegionList regions = endRegion.getRegions();
ITextRegion r = null;
for (int i = 0; i < regions.size(); i++) {
r = regions.get(i);
if (r.getType() == DOMRegionContext.XML_TAG_NAME) {
tagName = sdRegion.getText(r);
}
}
if (!selfClosed && (tagName != null)) {
String messageText = XMLCoreMessages.Indicate_no_grammar_specified_severities_error;
int start = sdRegion.getStart();
int lineNumber = getLineNumber(start);
// SEVERITY_STRUCTURE == IMessage.HIGH_SEVERITY
IMessage message = new LocalizedMessage(IMessage.HIGH_SEVERITY, messageText);
message.setOffset(start);
message.setLength(sdRegion.getTextLength());
message.setLineNo(lineNumber);
reporter.addMessage(this, message);
}
}
}
}
}
private Object[] getStartEndFixInfo(IDOMNode xmlNode, String tagName, ITextRegion r) {
// quick fix info
String tagClose = "/>"; //$NON-NLS-1$
int tagCloseOffset = xmlNode.getFirstStructuredDocumentRegion().getEndOffset();
if ((r != null) && (r.getType() == DOMRegionContext.XML_TAG_CLOSE)) {
tagClose = "/"; //$NON-NLS-1$
tagCloseOffset--;
}
IDOMNode firstChild = (IDOMNode) xmlNode.getFirstChild();
while ((firstChild != null) && (firstChild.getNodeType() == Node.TEXT_NODE)) {
firstChild = (IDOMNode) firstChild.getNextSibling();
}
int endOffset = xmlNode.getEndOffset();
int firstChildStartOffset = firstChild == null ? endOffset : firstChild.getStartOffset();
Object[] additionalFixInfo = {tagName, tagClose, new Integer(tagCloseOffset), new Integer(xmlNode.getFirstStructuredDocumentRegion().getEndOffset()), // startTagEndOffset
new Integer(firstChildStartOffset), // firstChildStartOffset
new Integer(endOffset)}; // endOffset
return additionalFixInfo;
}
private void checkStartingSpaceForPI(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion.isDeleted()) {
return;
}
IStructuredDocumentRegion prev = structuredDocumentRegion.getPrevious();
if ((prev != null) && prev.getStartOffset() == 0) {
if (prev.getType() == DOMRegionContext.XML_CONTENT) {
String messageText = XMLCoreMessages.ReconcileStepForMarkup_5;
int start = prev.getStartOffset();
int length = prev.getLength();
LocalizedMessage message = new LocalizedMessage(getPluginPreference().getInt(XMLCorePreferenceNames.WHITESPACE_AT_START) , messageText);
message.setOffset(start);
message.setLength(length);
message.setLineNo(getLineNumber(start));
getAnnotationMsg(reporter, ProblemIDsXML.SpacesBeforePI, message, null,length);
// Position p = new Position(start, length);
//
// ReconcileAnnotationKey key =
// createKey(structuredDocumentRegion, getScope());
// TemporaryAnnotation annotation = new TemporaryAnnotation(p,
// SEVERITY_SYNTAX_ERROR, message, key,
// ProblemIDsXML.SpacesBeforePI);
// results.add(annotation);
}
}
}
public int getScope() {
return PARTIAL;
}
private IDOMNode getXMLNode(IStructuredDocumentRegion sdRegion) {
if (sdRegion == null) {
return null;
}
IStructuredModel xModel = null;
IDOMNode xmlNode = null;
// get/release models should always be in a try/finally block
try {
xModel = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument());
// xModel is sometime null, when closing editor, for example
if (xModel != null) {
xmlNode = (IDOMNode) xModel.getIndexedRegion(sdRegion.getStart());
}
}
finally {
if (xModel != null) {
xModel.releaseFromRead();
}
}
return xmlNode;
}
/**
* Determines whether the IStructuredDocumentRegion is a XML "end tag"
* since they're not allowed to have attribute ITextRegions
*
* @param structuredDocumentRegion
*/
private boolean isEndTag(IStructuredDocumentRegion structuredDocumentRegion) {
if ((structuredDocumentRegion == null) || structuredDocumentRegion.isDeleted()) {
return false;
}
return structuredDocumentRegion.getFirstRegion().getType() == DOMRegionContext.XML_END_TAG_OPEN;
}
/**
* Determines if the IStructuredDocumentRegion is an XML Processing
* Instruction
*
* @param structuredDocumentRegion
*
*/
private boolean isPI(IStructuredDocumentRegion structuredDocumentRegion) {
if ((structuredDocumentRegion == null) || structuredDocumentRegion.isDeleted()) {
return false;
}
return structuredDocumentRegion.getFirstRegion().getType() == DOMRegionContext.XML_PI_OPEN;
}
/**
* Determines whether the IStructuredDocumentRegion is a XML "start tag"
* since they need to be checked for proper XML attribute region sequences
*
* @param structuredDocumentRegion
*
*/
private boolean isStartTag(IStructuredDocumentRegion structuredDocumentRegion) {
if ((structuredDocumentRegion == null) || structuredDocumentRegion.isDeleted()) {
return false;
}
return structuredDocumentRegion.getFirstRegion().getType() == DOMRegionContext.XML_TAG_OPEN;
}
// Because we check the "proper" closing separately from attribute
// sequencing, we need to know what's
// an appropriate close.
private boolean isTagCloseTextRegion(ITextRegion textRegion) {
return (textRegion.getType() == DOMRegionContext.XML_TAG_CLOSE) || (textRegion.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE);
}
/**
* Determines if the IStructuredDocumentRegion is XML Content
*
* @param structuredDocumentRegion
*
*/
private boolean isXMLContent(IStructuredDocumentRegion structuredDocumentRegion) {
if ((structuredDocumentRegion == null) || structuredDocumentRegion.isDeleted()) {
return false;
}
return structuredDocumentRegion.getFirstRegion().getType() == DOMRegionContext.XML_CONTENT;
}
private IDocument getDocument() {
return fDocument;
}
public void connect(IDocument document) {
fDocument = document;
}
public void disconnect(IDocument document) {
fDocument = null;
}
public void validate(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
if (structuredDocumentRegion == null) {
return;
}
if (isStartTag(structuredDocumentRegion)) {
// check for attributes without a value
checkForAttributeValue(structuredDocumentRegion, reporter);
// check if started tag is ended
checkStartEndTagPairs(structuredDocumentRegion, reporter);
// check empty tag <>
checkEmptyTag(structuredDocumentRegion, reporter);
// check that each attribute has quotes
checkQuotesForAttributeValues(structuredDocumentRegion, reporter);
// check that the closing '>' is there
checkClosingBracket(structuredDocumentRegion, reporter);
}
else if (isEndTag(structuredDocumentRegion)) {
// check if ending tag was started
checkStartEndTagPairs(structuredDocumentRegion, reporter);
// check for attributes in an end tag
checkAttributesInEndTag(structuredDocumentRegion, reporter);
// check that the closing '>' is there
checkClosingBracket(structuredDocumentRegion, reporter);
}
else if (isPI(structuredDocumentRegion)) {
// check validity of processing instruction
checkStartingSpaceForPI(structuredDocumentRegion, reporter);
checkNoNamespaceInPI(structuredDocumentRegion, reporter);
}
else if (isXMLContent(structuredDocumentRegion)) {
checkForSpaceBeforeName(structuredDocumentRegion, reporter);
}
else if (isXMLDoctypeDeclaration(structuredDocumentRegion)) {
checkDocumentTypeReferences(structuredDocumentRegion, reporter);
}
}
/**
* @param structuredDocumentRegion
* @param reporter
*/
private void checkDocumentTypeReferences(IStructuredDocumentRegion structuredDocumentRegion, IReporter reporter) {
}
/**
* @param structuredDocumentRegion
* @return
*/
private boolean isXMLDoctypeDeclaration(IStructuredDocumentRegion structuredDocumentRegion) {
if ((structuredDocumentRegion == null) || structuredDocumentRegion.isDeleted()) {
return false;
}
return structuredDocumentRegion.getFirstRegion().getType() == DOMRegionContext.XML_DECLARATION_OPEN && structuredDocumentRegion.getType().equals(DOMRegionContext.XML_DOCTYPE_DECLARATION);
}
public void cleanup(IReporter reporter) {
fDocument = null;
}
public void validate(IValidationContext helper, IReporter reporter) throws ValidationException {
String[] uris = helper.getURIs();
IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
if (uris.length > 0) {
IFile currentFile = null;
for (int i = 0; i < uris.length && !reporter.isCancelled(); i++) {
// might be called with just project path?
IPath path = new Path(uris[i]);
if (path.segmentCount() > 1) {
currentFile = wsRoot.getFile(path);
if (shouldValidate(currentFile, true)) {
validateV1File(currentFile, reporter);
}
}
else if (uris.length == 1) {
validateV1Project(helper, reporter);
}
}
}
else
validateV1Project(helper, reporter);
}
private boolean shouldValidate(IResourceProxy proxy) {
if(proxy.getType() == IResource.FILE) {
String name = proxy.getName();
if(name.toLowerCase(Locale.US).endsWith(".xml")) { //$NON-NLS-1$
return true;
}
}
return shouldValidate(proxy.requestResource(), false);
}
private boolean shouldValidate(IResource file, boolean checkExtension) {
if (file == null || !file.exists() || file.getType() != IResource.FILE)
return false;
if (checkExtension) {
String extension = file.getFileExtension();
if (extension != null && "xml".endsWith(extension.toLowerCase(Locale.US))) //$NON-NLS-1$
return true;
}
IContentDescription contentDescription = null;
try {
contentDescription = ((IFile) file).getContentDescription();
if (contentDescription != null) {
IContentType contentType = contentDescription.getContentType();
return contentDescription != null && contentType.isKindOf(getXMLContentType());
}
}
catch (CoreException e) {
Logger.logException(e);
}
return false;
}
/**
* @param helper
* @param reporter
*/
private void validateV1Project(IValidationContext helper, final IReporter reporter) {
// if uris[] length 0 -> validate() gets called for each project
if (helper instanceof IWorkbenchContext) {
IProject project = ((IWorkbenchContext) helper).getProject();
IResourceProxyVisitor visitor = new IResourceProxyVisitor() {
public boolean visit(IResourceProxy proxy) throws CoreException {
if (shouldValidate(proxy)) {
validateV1File((IFile) proxy.requestResource(), reporter);
}
return true;
}
};
try {
// collect all jsp files for the project
project.accept(visitor, IResource.DEPTH_INFINITE);
}
catch (CoreException e) {
Logger.logException(e);
}
}
}
/**
* @param currentFile
* @param reporter
*/
private void validateV1File(IFile currentFile, IReporter reporter) {
Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, currentFile.getFullPath().toString().substring(1));
reporter.displaySubtask(MarkupValidator.this, message);
IStructuredModel model = null;
try {
model = StructuredModelManager.getModelManager().getModelForRead(currentFile);
IStructuredDocument document = null;
if (model != null) {
document = model.getStructuredDocument();
connect(document);
IStructuredDocumentRegion validationRegion = document.getFirstStructuredDocumentRegion();
while (validationRegion != null) {
validate(validationRegion, reporter);
validationRegion = validationRegion.getNext();
}
disconnect(document);
}
}
catch (Exception e) {
Logger.logException(e);
}
finally {
if (model != null) {
model.releaseFromRead();
}
}
}
/**
* @return
*/
private IContentType getXMLContentType() {
if (fRootContentType == null) {
fRootContentType = Platform.getContentTypeManager().getContentType("org.eclipse.core.runtime.xml"); //$NON-NLS-1$
}
return fRootContentType;
}
/* (non-Javadoc)
* @see org.eclipse.wst.validation.AbstractValidator#validate(org.eclipse.core.resources.IResource, int, org.eclipse.wst.validation.ValidationState, org.eclipse.core.runtime.IProgressMonitor)
*/
public ValidationResult validate(IResource resource, int kind, ValidationState state, IProgressMonitor monitor) {
if (resource.getType() != IResource.FILE)
return null;
ValidationResult result = new ValidationResult();
fReporter = result.getReporter(monitor);
validateV1File((IFile) resource, fReporter);
return result;
}
public IReporter validate(IResource resource, int kind, ValidationState state) {
validate(resource,kind,state,new NullProgressMonitor());
return fReporter;
}
private Preferences getPluginPreference(){
return XMLCorePlugin.getDefault().getPluginPreferences();
}
public void validate(IRegion dirtyRegion, IValidationContext helper, IReporter reporter) {
if (getDocument() == null) {
return;
}
if (!(getDocument() instanceof IStructuredDocument)) {
return;
}
// remove old messages
reporter.removeAllMessages(this);
IStructuredDocumentRegion[] regions = ((IStructuredDocument) fDocument).getStructuredDocumentRegions(dirtyRegion.getOffset(), dirtyRegion.getLength());
for (int i = 0; i < regions.length; i++) {
validate(regions[i], reporter);
}
}
}