Package org.eclipse.jst.jsp.core.internal.validation

Source Code of org.eclipse.jst.jsp.core.internal.validation.JSPActionValidator

/*******************************************************************************
* Copyright (c) 2008, 2011 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.core.resources.ProjectScope;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
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.JSPCorePlugin;
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.contentmodel.tld.provisional.JSP11TLDNames;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDAttributeDeclaration;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache.PropertyGroup;
import org.eclipse.jst.jsp.core.internal.document.PageDirectiveAdapter;
import org.eclipse.jst.jsp.core.internal.document.PageDirectiveAdapterFactory;
import org.eclipse.jst.jsp.core.internal.preferences.JSPCorePreferenceNames;
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.sse.core.internal.validate.ValidationMessage;
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.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.CMNode;
import org.eclipse.wst.xml.core.internal.contentmodel.basic.CMNamedNodeMapImpl;
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.contentmodel.CMNodeWrapper;
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.IDOMModel;
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.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

/**
* Checks for: missing required attributes & undefined attributes in jsp
* action tags such as jsp directives and jsp custom tags as well as non-empty
* inline jsp action tags
*/
public class JSPActionValidator extends JSPValidator {
  /**
   *
   */
  private static final String PREFERENCE_NODE_QUALIFIER = JSPCorePlugin.getDefault().getBundle().getSymbolicName();
  private IValidator fMessageOriginator;
  private IPreferencesService fPreferencesService = null;
  private IScopeContext[] fScopes = null;
  private int fSeverityMissingRequiredAttribute = IMessage.HIGH_SEVERITY;
  private int fSeverityNonEmptyInlineTag = IMessage.NORMAL_SEVERITY;
  private int fSeverityUnknownAttribute = IMessage.NORMAL_SEVERITY;
  private int fSeverityUnexpectedRuntimeExpression = IMessage.NORMAL_SEVERITY;

  private HashSet fTaglibPrefixes = new HashSet();
  private boolean fIsELIgnored = false;

  public JSPActionValidator() {
    this.fMessageOriginator = this;
  }

  public JSPActionValidator(IValidator validator) {
    this.fMessageOriginator = validator;
  }

  private void checkNonEmptyInlineTag(IDOMElement element, CMElementDeclaration cmElementDecl, IReporter reporter, IFile file, IStructuredDocument document) {
    if (cmElementDecl.getContentType() == CMElementDeclaration.EMPTY && element.getChildNodes().getLength() > 0) {
      String msgText = NLS.bind(JSPCoreMessages.JSPActionValidator_0, element.getNodeName());
      LocalizedMessage message = new LocalizedMessage(fSeverityNonEmptyInlineTag, 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);
    }
  }

  /**
   * Checks an attribute for runtime expressions
   * @param a The attribute to check for runtime expressions
   * @return true if the attribute contains a runtime expression, false otherwise
   */
  private boolean checkRuntimeValue(IDOMAttr a) {
    ITextRegion value = a.getValueRegion();
    if (value instanceof ITextRegionContainer) {
      Iterator it = ((ITextRegionContainer) value).getRegions().iterator();
      while (it.hasNext()) {
        String type = ((ITextRegion) it.next()).getType();
        if (type == DOMJSPRegionContexts.JSP_EL_OPEN)
          return true;
      }
    }
    return false;
  }

  /**
   * Determines if EL should be ignored. Checks
   * <ol>
   <li>JSP version</li>
   *   <li>Page directive isELIgnored</li>
   <li>Deployment descriptor's el-ignored</li>
   * </ol>
   * @return true if EL should be ignored, false otherwise. If the JSP version is < 2.0, EL is ignored by default
   */
  private boolean isElIgnored(IPath path, IStructuredModel model) {
    if (DeploymentDescriptorPropertyCache.getInstance().getJSPVersion(path) < 2.0f)
      return true;

    PageDirectiveAdapter pdAdapter = ((PageDirectiveAdapter) (((IDOMModel) model).getDocument().getAdapterFor(PageDirectiveAdapter.class)));
    if (pdAdapter == null) {
      // double-check the factory (although there just might not be a page directive in the file)
      if (model.getFactoryRegistry().getFactoryFor(PageDirectiveAdapter.class) == null) {
        model.getFactoryRegistry().addFactory(new PageDirectiveAdapterFactory());
        pdAdapter = ((PageDirectiveAdapter) (((IDOMModel) model).getDocument().getAdapterFor(PageDirectiveAdapter.class)));
      }
    }
    if (pdAdapter != null) {
      String directiveIsELIgnored = pdAdapter.getElIgnored();
      // isELIgnored directive found
      if (directiveIsELIgnored != null)
        return Boolean.valueOf(directiveIsELIgnored).booleanValue();
    }
   
    // Check the deployment descriptor for el-ignored
    PropertyGroup[] groups = DeploymentDescriptorPropertyCache.getInstance().getPropertyGroups(path);
    if (groups.length > 0)
      return groups[0].isELignored();
    // JSP version >= 2.0 defaults to evaluating EL
    return false;
  }

  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) {
          // Attribute may be defined using a jsp:attribute action
          if (!checkJSPAttributeAction(element, attr)) {
            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);
          }
        }
      }
    }
  }
 
  /**
   * Checks for jsp:attribute actions of <code>element</code> to see if they
   * satisfy the required attribute <code>attr</code>
   *
   * @param element The element with a required attribute
   * @param attr The required attribute
   * @return <code>true</code> if a jsp:attribute action has the name of
   * the required attribute <code>attr</code>; <code>false</code> otherwise.
   */
  private boolean checkJSPAttributeAction(IDOMElement element, CMAttributeDeclaration attr) {
    if (element != null && attr != null) {
      NodeList elements = element.getElementsByTagName("jsp:attribute"); //$NON-NLS-1$
      String neededAttrName = attr.getNodeName();
      for (int i = 0; i < elements.getLength(); i++) {
        Element childElement = (Element) elements.item(i);
        /*
         * Get the name attribute of jsp:attribute and compare its
         * value to the required attribute name
         */
        if (childElement.hasAttribute("name") && neededAttrName.equals(childElement.getAttribute("name"))) {//$NON-NLS-1$ //$NON-NLS-2$
          return true;
        }
      }
    }
    return false;
  }

  private boolean checkUnknownAttributes(IDOMElement element, CMElementDeclaration elementDecl, CMNamedNodeMap cmAttrs, IReporter reporter, IFile file, IStructuredDocument document, IStructuredDocumentRegion documentRegion) {
    boolean foundjspattribute = false;
    boolean dynamicAttributesAllowed = false;
    CMElementDeclaration decl = elementDecl;
    if (decl instanceof CMNodeWrapper)
      decl = (CMElementDeclaration) ((CMNodeWrapper) decl).getOriginNode();
    if (decl instanceof TLDElementDeclaration) {
      String dynamicAttributes = ((TLDElementDeclaration) decl).getDynamicAttributes();
      dynamicAttributesAllowed = dynamicAttributes != null ? Boolean.valueOf(dynamicAttributes).booleanValue() : 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. Disregard it includes JSP structure or this
         * element supports dynamic attributes
         */
        if (!hasJSPRegion(((IDOMNode) a).getNameRegion()) && fSeverityUnknownAttribute != ValidationMessage.IGNORE) {
          if (!dynamicAttributesAllowed) {
            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;
        }
      }
      else {
        if (fSeverityUnexpectedRuntimeExpression != ValidationMessage.IGNORE && adec instanceof TLDAttributeDeclaration) {
          // The attribute cannot have a runtime evaluation of an expression
          if (!isTrue(((TLDAttributeDeclaration) adec).getRtexprvalue())) {
            IDOMAttr attr = (IDOMAttr) a;
            if(checkRuntimeValue(attr) && !fIsELIgnored) {
              String msg = NLS.bind(JSPCoreMessages.JSPActionValidator_1, a.getName());
              LocalizedMessage message = new LocalizedMessage(fSeverityUnexpectedRuntimeExpression, msg, file);
              ITextRegion region = attr.getValueRegion();
              int start = attr.getValueRegionStartOffset();
              int length = region != null ? region.getTextLength() : 0;
              int lineNo = document.getLineOfOffset(start);
              message.setLineNo(lineNo);
              message.setOffset(start);
              message.setLength(length);
              reporter.addMessage(fMessageOriginator, message);
            }
          }
        }
      }
    }
    return foundjspattribute;
  }

  private boolean isTrue(String value) {
    return JSP11TLDNames.TRUE.equalsIgnoreCase(value) || JSP11TLDNames.YES.equalsIgnoreCase(value);
  }

  public void cleanup(IReporter reporter) {
    super.cleanup(reporter);
    fTaglibPrefixes.clear();
  }

  int getMessageSeverity(String key) {
    int sev = fPreferencesService.getInt(PREFERENCE_NODE_QUALIFIER, key, IMessage.NORMAL_SEVERITY, fScopes);
    switch (sev) {
      case ValidationMessage.ERROR :
        return IMessage.HIGH_SEVERITY;
      case ValidationMessage.WARNING :
        return IMessage.NORMAL_SEVERITY;
      case ValidationMessage.INFORMATION :
        return IMessage.LOW_SEVERITY;
      case ValidationMessage.IGNORE :
        return ValidationMessage.IGNORE;
    }
    return IMessage.NORMAL_SEVERITY;
  }

  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);
      if (manager != null) {
        List trackers = manager.getTaglibTrackers();
        for (Iterator it = trackers.iterator(); it.hasNext();) {
          TaglibTracker tracker = (TaglibTracker) it.next();
          if (tracker.getElements().getLength() == 0)
            continue;
          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;
  }

  private void loadPreferences(IFile file) {
    fScopes = new IScopeContext[]{new InstanceScope(), new DefaultScope()};

    fPreferencesService = Platform.getPreferencesService();
    if (file != null && file.isAccessible()) {
      ProjectScope projectScope = new ProjectScope(file.getProject());
      if (projectScope.getNode(PREFERENCE_NODE_QUALIFIER).getBoolean(JSPCorePreferenceNames.VALIDATION_USE_PROJECT_SETTINGS, false)) {
        fScopes = new IScopeContext[]{projectScope, new InstanceScope(), new DefaultScope()};
      }
    }

    fSeverityMissingRequiredAttribute = getMessageSeverity(JSPCorePreferenceNames.VALIDATION_ACTIONS_SEVERITY_MISSING_REQUIRED_ATTRIBUTE);
    fSeverityNonEmptyInlineTag = getMessageSeverity(JSPCorePreferenceNames.VALIDATION_ACTIONS_SEVERITY_NON_EMPTY_INLINE_TAG);
    fSeverityUnknownAttribute = getMessageSeverity(JSPCorePreferenceNames.VALIDATION_ACTIONS_SEVERITY_UNKNOWN_ATTRIBUTE);
    fSeverityUnexpectedRuntimeExpression = getMessageSeverity(JSPCorePreferenceNames.VALIDATION_ACTIONS_SEVERITY_UNEXPECTED_RTEXPRVALUE);
  }

  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) {
    loadPreferences(f);
    IStructuredDocument sDoc = model.getStructuredDocument();

    fIsELIgnored = isElIgnored(f.getFullPath(), model);
    // 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();
    }
    unloadPreferences();
  }

  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 = null;

          CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl();
          List nodes = query.getAvailableContent(element, cmElement, ModelQuery.INCLUDE_ATTRIBUTES);
          for (int k = 0; k < nodes.size(); k++) {
            CMNode cmnode = (CMNode) nodes.get(k);
            if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) {
              allAttributes.put(cmnode);
            }
          }
          cmAttributes = allAttributes;

          boolean foundjspattribute = checkUnknownAttributes(element, cmElement, 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 && fSeverityMissingRequiredAttribute != ValidationMessage.IGNORE)
            checkRequiredAttributes(element, cmAttributes, reporter, file, model.getStructuredDocument(), documentRegion);

          if (fSeverityNonEmptyInlineTag != ValidationMessage.IGNORE)
            checkNonEmptyInlineTag(element, cmElement, reporter, file, model.getStructuredDocument());
        }
      }
    }
  }

  private void unloadPreferences() {
    fPreferencesService = null;
    fScopes = null;
  }

  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();
    }
  }
}
TOP

Related Classes of org.eclipse.jst.jsp.core.internal.validation.JSPActionValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.