From 2c6a31d051828a7b4e2b89219cf758ddaac4146f Mon Sep 17 00:00:00 2001 From: amywu Date: Mon, 23 Apr 2007 19:44:08 +0000 Subject: [160548] JSP validator does not mark the non-existing attribute or the missing required attribute --- .../jst/jsp/core/internal/JSPCoreMessages.java | 2 + .../internal/JSPCorePluginResources.properties | 2 + .../internal/validation/JSPActionValidator.java | 269 +++++++++++++++++++++ .../internal/validation/JSPBatchValidator.java | 11 +- 4 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java (limited to 'bundles/org.eclipse.jst.jsp.core/src/org') diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java index 03fcf134db..3d74cb709f 100644 --- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java +++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java @@ -25,6 +25,8 @@ public class JSPCoreMessages extends NLS { public static String JSPDirectiveValidator_2; public static String JSPDirectiveValidator_3; public static String JSPDirectiveValidator_4; + public static String JSPDirectiveValidator_5; + public static String JSPDirectiveValidator_6; public static String JSPIndexManager_0; public static String JSPIndexManager_2; public static String JSP_Search; diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties index d40799b854..1129ea115e 100644 --- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties +++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties @@ -22,4 +22,6 @@ JSPDirectiveValidator_1=Cannot find the tag library descriptor for {0} JSPDirectiveValidator_2=The prefix {0} is used more than once JSPDirectiveValidator_3=A {0} value is required in this directive JSPDirectiveValidator_4=Fragment {0} was not be found at expected path {1} +JSPDirectiveValidator_5=Missing required attribute "{0}" +JSPDirectiveValidator_6=Undefined attribute name ({0}) JSPBatchValidator_0=Gathering files in {0} diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java new file mode 100644 index 0000000000..04450e31fb --- /dev/null +++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPActionValidator.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2007 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.jst.jsp.core.internal.validation; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; +import org.eclipse.jst.jsp.core.internal.Logger; +import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController; +import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager; +import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TaglibTracker; +import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts; +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.IndexedRegion; +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.internal.core.ValidationException; +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.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; +import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; +import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; +import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; + +/** + * Checks for: - missing required attributes & undefined attributes in jsp + * action tags such as jsp directives and jsp custom tags + */ +public class JSPActionValidator extends JSPValidator { + private int fSeverityMissingRequiredAttribute = IMessage.HIGH_SEVERITY; + private int fSeverityUnknownAttribute = IMessage.NORMAL_SEVERITY; + private IValidator fMessageOriginator; + private HashSet fTaglibPrefixes = new HashSet(); + + public JSPActionValidator() { + this.fMessageOriginator = this; + } + + public JSPActionValidator(IValidator validator) { + this.fMessageOriginator = validator; + } + + private void checkRequiredAttributes(IDOMElement element, CMNamedNodeMap attrMap, IReporter reporter, IFile file, IStructuredDocument document, IStructuredDocumentRegion documentRegion) { + Iterator it = attrMap.iterator(); + CMAttributeDeclaration attr = null; + while (it.hasNext()) { + attr = (CMAttributeDeclaration) it.next(); + if (attr.getUsage() == CMAttributeDeclaration.REQUIRED) { + Attr a = element.getAttributeNode(attr.getAttrName()); + if (a == null) { + String msgText = NLS.bind(JSPCoreMessages.JSPDirectiveValidator_5, attr.getAttrName()); + LocalizedMessage message = new LocalizedMessage(fSeverityMissingRequiredAttribute, msgText, file); + int start = element.getStartOffset(); + int length = element.getStartEndOffset() - start; + int lineNo = document.getLineOfOffset(start); + message.setLineNo(lineNo); + message.setOffset(start); + message.setLength(length); + + reporter.addMessage(fMessageOriginator, message); + } + } + } + } + + private boolean checkUnknownAttributes(IDOMElement element, CMNamedNodeMap cmAttrs, IReporter reporter, IFile file, IStructuredDocument document, IStructuredDocumentRegion documentRegion) { + boolean foundjspattribute = false; + + NamedNodeMap attrs = element.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { + Attr a = (Attr) attrs.item(i); + CMAttributeDeclaration adec = (CMAttributeDeclaration) cmAttrs.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 (!hasJSPRegion(((IDOMNode) a).getNameRegion())) { + String msgText = NLS.bind(JSPCoreMessages.JSPDirectiveValidator_6, a.getName()); + LocalizedMessage message = new LocalizedMessage(fSeverityUnknownAttribute, msgText, file); + int start = ((IDOMAttr) a).getNameRegionStartOffset(); + int length = ((IDOMAttr) a).getNameRegionEndOffset() - start; + int lineNo = document.getLineOfOffset(start); + message.setLineNo(lineNo); + message.setOffset(start); + message.setLength(length); + + reporter.addMessage(fMessageOriginator, message); + } + else { + foundjspattribute = true; + } + } + } + return foundjspattribute; + } + + public void cleanup(IReporter reporter) { + super.cleanup(reporter); + fTaglibPrefixes.clear(); + } + + private String getStartTagName(IStructuredDocumentRegion sdr) { + String name = new String(); + ITextRegionList subRegions = sdr.getRegions(); + if (subRegions.size() > 2) { + ITextRegion subRegion = subRegions.get(0); + if (subRegion.getType() == DOMRegionContext.XML_TAG_OPEN) { + subRegion = subRegions.get(1); + if (subRegion.getType() == DOMRegionContext.XML_TAG_NAME) { + name = sdr.getText(subRegion); + } + } + } + return name; + } + + private HashSet getTaglibPrefixes(IStructuredDocument document) { + if (fTaglibPrefixes.isEmpty()) { + // add all reserved prefixes + fTaglibPrefixes.add("jsp"); //$NON-NLS-1$ + fTaglibPrefixes.add("jspx"); //$NON-NLS-1$ + fTaglibPrefixes.add("java"); //$NON-NLS-1$ + fTaglibPrefixes.add("javax"); //$NON-NLS-1$ + fTaglibPrefixes.add("servlet"); //$NON-NLS-1$ + fTaglibPrefixes.add("sun"); //$NON-NLS-1$ + fTaglibPrefixes.add("sunw"); //$NON-NLS-1$ + + // add all taglib prefixes + TLDCMDocumentManager manager = TaglibController.getTLDCMDocumentManager(document); + List trackers = manager.getTaglibTrackers(); + for (Iterator it = trackers.iterator(); it.hasNext();) { + TaglibTracker tracker = (TaglibTracker) it.next(); + String prefix = tracker.getPrefix(); + fTaglibPrefixes.add(prefix); + } + } + return fTaglibPrefixes; + } + + private 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 == DOMRegionContext.XML_TAG_OPEN || (isNestedTagName(regionType))) + return true; + } + return false; + } + + private boolean isNestedTagName(String regionType) { + boolean result = regionType.equals(DOMJSPRegionContexts.JSP_SCRIPTLET_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_EXPRESSION_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_DECLARATION_OPEN) || regionType.equals(DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN); + return result; + } + + void performValidation(IFile f, IReporter reporter, IStructuredModel model) { + fTaglibPrefixes.clear(); + int length = model.getStructuredDocument().getLength(); + performValidation(f, reporter, model, new Region(0, length)); + } + + protected void performValidation(IFile f, IReporter reporter, IStructuredModel model, IRegion validateRegion) { + IStructuredDocument sDoc = model.getStructuredDocument(); + + // iterate all document regions + IStructuredDocumentRegion region = sDoc.getRegionAtCharacterOffset(validateRegion.getOffset()); + while (region != null && !reporter.isCancelled() && (region.getStartOffset() <= (validateRegion.getOffset() + validateRegion.getLength()))) { + if (region.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) { + // only checking directives + processDirective(reporter, f, model, region); + fTaglibPrefixes.clear(); + } + else if (region.getType() == DOMRegionContext.XML_TAG_NAME) { + // and jsp tags + String tagName = getStartTagName(region); + int colonPosition = tagName.indexOf(':'); + if (colonPosition > -1) { + // get tag's prefix and check if it's really a jsp action + // tag + String prefix = tagName.substring(0, colonPosition); + if (getTaglibPrefixes(sDoc).contains(prefix)) + processDirective(reporter, f, model, region); + } + } + region = region.getNext(); + } + } + + private void processDirective(IReporter reporter, IFile file, IStructuredModel model, IStructuredDocumentRegion documentRegion) { + IndexedRegion ir = model.getIndexedRegion(documentRegion.getStartOffset()); + if (ir instanceof IDOMElement) { + IDOMElement element = (IDOMElement) ir; + ModelQuery query = ModelQueryUtil.getModelQuery(model); + if (query != null) { + CMElementDeclaration cmElement = query.getCMElementDeclaration(element); + if (cmElement != null) { + CMNamedNodeMap cmAttributes = cmElement.getAttributes(); + + boolean foundjspattribute = checkUnknownAttributes(element, cmAttributes, reporter, file, model.getStructuredDocument(), documentRegion); + // required attributes could be hidden in jsp regions in + // tags, so if jsp regions were detected, do not check for + // missing required attributes + if (!foundjspattribute) + checkRequiredAttributes(element, cmAttributes, reporter, file, model.getStructuredDocument(), documentRegion); + } + } + } + } + + public void validate(IValidationContext helper, IReporter reporter) throws ValidationException { + reporter.removeAllMessages(this); + super.validate(helper, reporter); + } + + protected void validateFile(IFile f, IReporter reporter) { + if (DEBUG) { + Logger.log(Logger.INFO, getClass().getName() + " validating: " + f); //$NON-NLS-1$ + } + + IStructuredModel sModel = null; + try { + sModel = StructuredModelManager.getModelManager().getModelForRead(f); + if (sModel != null && !reporter.isCancelled()) { + performValidation(f, reporter, sModel); + } + } + catch (Exception e) { + Logger.logException(e); + } + finally { + if (sModel != null) + sModel.releaseFromRead(); + } + } +} diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java index f4b5ba5cfa..63357a3fff 100644 --- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java +++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/validation/JSPBatchValidator.java @@ -163,11 +163,14 @@ public final class JSPBatchValidator implements IValidatorJob, IExecutableExtens private JSPJavaValidator fJSPJavaValidator = new JSPJavaValidator(this); + private JSPActionValidator fJSPActionValidator = new JSPActionValidator(this); + public void cleanup(IReporter reporter) { fJSPDirectiveValidator.cleanup(reporter); fJSPELValidator.cleanup(reporter); fJSPJavaValidator.cleanup(reporter); + fJSPActionValidator.cleanup(reporter); } @@ -182,7 +185,7 @@ public final class JSPBatchValidator implements IValidatorJob, IExecutableExtens currentFile = wsRoot.getFile(new Path(uris[i])); if (currentFile != null && currentFile.exists()) { if (shouldValidate(currentFile) && fragmentCheck(currentFile)) { - Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i+1) + "/" + uris.length + " - " + currentFile.getFullPath().toString().substring(1)); + Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i + 1) + "/" + uris.length + " - " + currentFile.getFullPath().toString().substring(1)); reporter.displaySubtask(this, message); validateFile(currentFile, reporter); } @@ -195,7 +198,7 @@ public final class JSPBatchValidator implements IValidatorJob, IExecutableExtens // if uris[] length 0 -> validate() gets called for each project if (helper instanceof IWorkbenchContext) { IProject project = ((IWorkbenchContext) helper).getProject(); - + Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, NLS.bind(JSPCoreMessages.JSPBatchValidator_0, project.getFullPath())); reporter.displaySubtask(this, message); @@ -212,7 +215,7 @@ public final class JSPBatchValidator implements IValidatorJob, IExecutableExtens for (int i = 0; i < files.length && !reporter.isCancelled(); i++) { if (shouldValidate(files[i]) && fragmentCheck(files[i])) { - message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i+1) + "/" + files.length + " - " + files[i].getFullPath().toString().substring(1)); + message = new LocalizedMessage(IMessage.LOW_SEVERITY, "" + (i + 1) + "/" + files.length + " - " + files[i].getFullPath().toString().substring(1)); reporter.displaySubtask(this, message); validateFile(files[i], reporter); @@ -370,6 +373,8 @@ public final class JSPBatchValidator implements IValidatorJob, IExecutableExtens fJSPDirectiveValidator.performValidation(f, reporter, model.getStructuredDocument()); if (!reporter.isCancelled()) fJSPELValidator.performValidation(f, reporter, model.getStructuredDocument()); + if (!reporter.isCancelled()) + fJSPActionValidator.performValidation(f, reporter, model); } /** -- cgit v1.2.3