RuleSetFactory.java :  » Code-Analyzer » pmd-4.2rc1 » net » sourceforge » pmd » Java Open Source

Java Open Source » Code Analyzer » pmd 4.2rc1 
pmd 4.2rc1 » net » sourceforge » pmd » RuleSetFactory.java
/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */
package net.sourceforge.pmd;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import net.sourceforge.pmd.util.ResourceLoader;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * RuleSetFactory is responsible for creating RuleSet instances from XML content.
 */
public class RuleSetFactory {

  private int minPriority = Rule.LOWEST_PRIORITY;

  /**
   * Set the minimum rule priority threshold for all Rules which are loaded
   * from RuleSets via reference.
   * 
   * @param minPriority The minimum priority.
   */
  public void setMinimumPriority(int minPriority) {
    this.minPriority = minPriority;
  }

  /**
   * Returns an Iterator of RuleSet objects loaded from descriptions from the
   * "rulesets.properties" resource.
   *
   * @return An Iterator of RuleSet objects.
   */
  public Iterator<RuleSet> getRegisteredRuleSets() throws RuleSetNotFoundException {
    try {
      Properties props = new Properties();
      props.load(ResourceLoader.loadResourceAsStream("rulesets/rulesets.properties"));
      String rulesetFilenames = props.getProperty("rulesets.filenames");
      return createRuleSets(rulesetFilenames).getRuleSetsIterator();
    } catch (IOException ioe) {
      throw new RuntimeException(
          "Couldn't find rulesets.properties; please ensure that the rulesets directory is on the classpath.  Here's the current classpath: "
              + System.getProperty("java.class.path"));
    }
  }

  /**
   * Create a RuleSets from a list of names.
   * The ClassLoader of the RuleSetFactory class is used.
   *
   * @param ruleSetFileNames  A comma-separated list of rule set files.
   * @return The new RuleSets.
   * @throws RuleSetNotFoundException if unable to find a resource.
   */
  public RuleSets createRuleSets(String ruleSetFileNames) throws RuleSetNotFoundException {
    return createRuleSets(ruleSetFileNames, getClass().getClassLoader());
  }

  /**
   * Create a RuleSets from a list of names with a specified ClassLoader.
   *
   * @param ruleSetFileNames  A comma-separated list of rule set files.
   * @param classLoader The ClassLoader to load Classes and resources.
   * @return The new RuleSets.
   * @throws RuleSetNotFoundException if unable to find a resource.
   */
  public RuleSets createRuleSets(String ruleSetFileNames, ClassLoader classLoader) throws RuleSetNotFoundException {
    RuleSets ruleSets = new RuleSets();

    for (StringTokenizer st = new StringTokenizer(ruleSetFileNames, ","); st.hasMoreTokens();) {
      RuleSet ruleSet = createSingleRuleSet(st.nextToken().trim(), classLoader);
      ruleSets.addRuleSet(ruleSet);
    }

    return ruleSets;
  }

  /**
   * Create a ruleset from a name or from a list of names
   *
   * @param name        name of rule set file loaded as a resource
   * @param classLoader the classloader used to load the ruleset and subsequent rules
   * @return the new ruleset
   * @throws RuleSetNotFoundException
   * @deprecated Use createRuleSets instead, because this method puts all rules in one
   *             single RuleSet object, and thus removes name and language of the
   *             originating rule set files.
   */
  public RuleSet createRuleSet(String name, ClassLoader classLoader) throws RuleSetNotFoundException {
    RuleSets ruleSets = createRuleSets(name, classLoader);
    RuleSet result = new RuleSet();
    RuleSet[] allRuleSets = ruleSets.getAllRuleSets();
    for (RuleSet ruleSet : allRuleSets) {
      result.addRuleSet(ruleSet);
    }
    return result;
  }

  /**
   * Create a RuleSet from a file name resource.
   * The ClassLoader of the RuleSetFactory class is used.
   *
   * @param ruleSetFileName The name of rule set file loaded as a resource.
   * @return A new RuleSet.
   * @throws RuleSetNotFoundException if unable to find a resource.
   */
  public RuleSet createSingleRuleSet(String ruleSetFileName) throws RuleSetNotFoundException {
    return createSingleRuleSet(ruleSetFileName, getClass().getClassLoader());
  }

  /**
   * Create a RuleSet from a file name resource with a specified ClassLoader.
   *
   * @param ruleSetFileName The name of rule set file loaded as a resource.
   * @param classLoader The ClassLoader to load Classes and resources.
   * @return A new RuleSet.
   * @throws RuleSetNotFoundException if unable to find a resource.
   */
  private RuleSet createSingleRuleSet(String ruleSetFileName, ClassLoader classLoader)
      throws RuleSetNotFoundException {
    return parseRuleSetNode(ruleSetFileName, tryToGetStreamTo(ruleSetFileName, classLoader), classLoader);
  }

  /**
   * Create a RuleSet from an InputStream.
   * The ClassLoader of the RuleSetFactory class is used.
   *
   * @param inputStream InputStream containing the RuleSet XML configuration.
   * @return A new RuleSet.
   */
  public RuleSet createRuleSet(InputStream inputStream) {
    return createRuleSet(inputStream, getClass().getClassLoader());
  }

  /**
   * Create a RuleSet from an InputStream with a specified ClassLoader.
   *
   * @param inputStream InputStream containing the RuleSet XML configuration.
   * @param classLoader The ClassLoader to load Classes and resources.
   * @return A new RuleSet.
   */
  public RuleSet createRuleSet(InputStream inputStream, ClassLoader classLoader) {
    return parseRuleSetNode(null, inputStream, classLoader);
  }

  /**
   * Try to load a resource with the specified class loader
   *
   * @param name A resource name (e.g. a RuleSet description).
   * @param classLoader The ClassLoader to load Classes and resources.
   * @return An InputStream to that resource.
   * @throws RuleSetNotFoundException if unable to find a resource.
   */
  private InputStream tryToGetStreamTo(String name, ClassLoader classLoader) throws RuleSetNotFoundException {
    InputStream in = ResourceLoader.loadResourceAsStream(name, classLoader);
    if (in == null) {
      throw new RuleSetNotFoundException(
          "Can't find resource "
              + name
              + ".  Make sure the resource is a valid file or URL or is on the CLASSPATH.  Here's the current classpath: "
              + System.getProperty("java.class.path"));
    }
    return in;
  }

  /**
   * Parse a ruleset node to construct a RuleSet.
   * 
   * @param inputStream InputStream containing the RuleSet XML configuration.
   * @param classLoader The ClassLoader to load Classes and resources.
   * @return The new RuleSet.
   */
  private RuleSet parseRuleSetNode(String fileName, InputStream inputStream, ClassLoader classLoader) {
    try {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document document = builder.parse(inputStream);
      Element ruleSetElement = document.getDocumentElement();

      RuleSet ruleSet = new RuleSet();
      ruleSet.setFileName(fileName);
      ruleSet.setName(ruleSetElement.getAttribute("name"));
      ruleSet.setLanguage(Language.getByName(ruleSetElement.getAttribute("language")));

      NodeList nodeList = ruleSetElement.getChildNodes();
      for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
          if (node.getNodeName().equals("description")) {
            ruleSet.setDescription(parseTextNode(node));
          } else if (node.getNodeName().equals("include-pattern")) {
            ruleSet.addIncludePattern(parseTextNode(node));
          } else if (node.getNodeName().equals("exclude-pattern")) {
            ruleSet.addExcludePattern(parseTextNode(node));
          } else if (node.getNodeName().equals("rule")) {
            parseRuleNode(ruleSet, node, classLoader);
          }
        }
      }

      return ruleSet;
    } catch (ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + cnfe.getMessage());
    } catch (InstantiationException ie) {
      ie.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + ie.getMessage());
    } catch (IllegalAccessException iae) {
      iae.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + iae.getMessage());
    } catch (ParserConfigurationException pce) {
      pce.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + pce.getMessage());
    } catch (RuleSetNotFoundException rsnfe) {
      rsnfe.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + rsnfe.getMessage());
    } catch (IOException ioe) {
      ioe.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + ioe.getMessage());
    } catch (SAXException se) {
      se.printStackTrace();
      throw new RuntimeException("Couldn't find that class " + se.getMessage());
    }
  }

  /**
   * Parse a rule node.
   *
   * @param ruleSet The RuleSet being constructed.
   * @param ruleNode Must be a rule element node.
   * @param classLoader The ClassLoader to load Classes and resources.
   */
  private void parseRuleNode(RuleSet ruleSet, Node ruleNode, ClassLoader classLoader) throws ClassNotFoundException,
      InstantiationException, IllegalAccessException, RuleSetNotFoundException {
    Element ruleElement = (Element)ruleNode;
    String ref = ruleElement.getAttribute("ref");
    if (ref.endsWith("xml")) {
      parseRuleSetReferenceNode(ruleSet, ruleElement, ref);
    } else if (ref.trim().length() == 0) {
      parseSingleRuleNode(ruleSet, ruleNode, classLoader);
    } else {
      parseRuleReferenceNode(ruleSet, ruleNode, ref);
    }
  }

  /**
   * Parse a rule node as an RuleSetReference for all Rules.  Every Rule from
   * the referred to RuleSet will be added as a RuleReference except for those
   * explicitly excluded.
   *
   * @param ruleSet The RuleSet being constructed.
   * @param ruleElement Must be a rule element node.
   * @param ref The RuleSet reference.
   */
  private void parseRuleSetReferenceNode(RuleSet ruleSet, Element ruleElement, String ref)
      throws RuleSetNotFoundException {

    RuleSetReference ruleSetReference = new RuleSetReference();
    ruleSetReference.setAllRules(true);
    ruleSetReference.setRuleSetFileName(ref);
    NodeList excludeNodes = ruleElement.getChildNodes();
    for (int i = 0; i < excludeNodes.getLength(); i++) {
      if ((excludeNodes.item(i).getNodeType() == Node.ELEMENT_NODE)
          && (excludeNodes.item(i).getNodeName().equals("exclude"))) {
        Element excludeElement = (Element)excludeNodes.item(i);
        ruleSetReference.addExclude(excludeElement.getAttribute("name"));
      }
    }

    RuleSetFactory ruleSetFactory = new RuleSetFactory();
    RuleSet otherRuleSet = ruleSetFactory.createRuleSet(ResourceLoader.loadResourceAsStream(ref));
    for (Rule rule : otherRuleSet.getRules()) {
      if (!ruleSetReference.getExcludes().contains(rule.getName()) && rule.getPriority() <= minPriority) {
        RuleReference ruleReference = new RuleReference();
        ruleReference.setRuleSetReference(ruleSetReference);
        ruleReference.setRule(rule);
        ruleSet.addRule(ruleReference);
      }
    }
  }

  /**
   * Parse a rule node as a single Rule.  The Rule has been fully defined within
   * the context of the current RuleSet.
   *
   * @param ruleSet The RuleSet being constructed.
   * @param ruleNode Must be a rule element node.
   * @param classLoader The ClassLoader to load Classes and resources.
   */
  private void parseSingleRuleNode(RuleSet ruleSet, Node ruleNode, ClassLoader classLoader)
      throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    Element ruleElement = (Element)ruleNode;

    String attribute = ruleElement.getAttribute("class");
    Class<?> c = classLoader.loadClass(attribute);
    Rule rule = (Rule)c.newInstance();

    rule.setName(ruleElement.getAttribute("name"));
    String since = ruleElement.getAttribute("since");
    if (since.length() > 0) {
      rule.setSince(since);
    }
    rule.setMessage(ruleElement.getAttribute("message"));
    rule.setRuleSetName(ruleSet.getName());
    rule.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));

    if (ruleElement.hasAttribute("dfa") && ruleElement.getAttribute("dfa").equals("true")) {
      rule.setUsesDFA();
    }

    if (ruleElement.hasAttribute("typeResolution") && ruleElement.getAttribute("typeResolution").equals("true")) {
      rule.setUsesTypeResolution();
    }

    for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
      Node node = ruleElement.getChildNodes().item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
        if (node.getNodeName().equals("description")) {
          rule.setDescription(parseTextNode(node));
        } else if (node.getNodeName().equals("example")) {
          rule.addExample(parseTextNode(node));
        } else if (node.getNodeName().equals("priority")) {
          rule.setPriority(Integer.parseInt(parseTextNode(node).trim()));
        } else if (node.getNodeName().equals("properties")) {
          Properties p = new Properties();
          parsePropertiesNode(p, node);
          for (Map.Entry<Object, Object> entry : p.entrySet()) {
            rule.addProperty((String)entry.getKey(), (String)entry.getValue());
          }
        }
      }
    }
    if (rule.getPriority() <= minPriority) {
      ruleSet.addRule(rule);
    }
  }

  /**
   * Parse a rule node as a RuleReference.  A RuleReference is a single Rule
   * which comes from another RuleSet with some of it's attributes potentially
   * overridden.
   *
   * @param ruleSet The RuleSet being constructed.
   * @param ruleNode Must be a rule element node.
   * @param classLoader The ClassLoader to load Classes and resources.
   * @param ref A reference to a Rule.
   */
  private void parseRuleReferenceNode(RuleSet ruleSet, Node ruleNode, String ref) throws RuleSetNotFoundException {
    RuleSetFactory ruleSetFactory = new RuleSetFactory();

    ExternalRuleID externalRuleID = new ExternalRuleID(ref);
    RuleSet externalRuleSet = ruleSetFactory.createRuleSet(ResourceLoader.loadResourceAsStream(externalRuleID.getFilename()));
    Rule externalRule = externalRuleSet.getRuleByName(externalRuleID.getRuleName());
    if (externalRule == null) {
      throw new IllegalArgumentException("Unable to find rule " + externalRuleID.getRuleName()
          + "; perhaps the rule name is mispelled?");
    }

    RuleSetReference ruleSetReference = new RuleSetReference();
    ruleSetReference.setAllRules(false);
    ruleSetReference.setRuleSetFileName(externalRuleID.getFilename());

    RuleReference ruleReference = new RuleReference();
    ruleReference.setRuleSetReference(ruleSetReference);
    ruleReference.setRule(externalRule);

    Element ruleElement = (Element)ruleNode;
    if (ruleElement.hasAttribute("name")) {
      ruleReference.setName(ruleElement.getAttribute("name"));
    }
    if (ruleElement.hasAttribute("message")) {
      ruleReference.setMessage(ruleElement.getAttribute("message"));
    }
    if (ruleElement.hasAttribute("externalInfoUrl")) {
      ruleReference.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));
    }
    for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
      Node node = ruleElement.getChildNodes().item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
        if (node.getNodeName().equals("description")) {
          ruleReference.setDescription(parseTextNode(node));
        } else if (node.getNodeName().equals("example")) {
          ruleReference.addExample(parseTextNode(node));
        } else if (node.getNodeName().equals("priority")) {
          ruleReference.setPriority(Integer.parseInt(parseTextNode(node)));
        } else if (node.getNodeName().equals("properties")) {
          Properties p = new Properties();
          parsePropertiesNode(p, node);
          ruleReference.addProperties(p);
        }
      }
    }

    if (externalRule.getPriority() <= minPriority) {
      ruleSet.addRule(ruleReference);
    }
  }

  /**
   * Parse a properties node.
   *
   * @param p The Properties to which the properties should be added.
   * @param propertiesNode Must be a properties element node.
   */
  private static void parsePropertiesNode(Properties p, Node propertiesNode) {
    for (int i = 0; i < propertiesNode.getChildNodes().getLength(); i++) {
      Node node = propertiesNode.getChildNodes().item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("property")) {
        parsePropertyNode(p, node);
      }
    }
  }

  /**
   * Parse a property node.
   *
   * @param p The Properties to which the property should be added.
   * @param propertyNode Must be a property element node.
   */
  private static void parsePropertyNode(Properties p, Node propertyNode) {
    Element propertyElement = (Element)propertyNode;
    String name = propertyElement.getAttribute("name");
    String value = propertyElement.getAttribute("value");
    // TODO String description = propertyElement.getAttribute("description");
    if (value.trim().length() == 0) {
      for (int i = 0; i < propertyNode.getChildNodes().getLength(); i++) {
        Node node = propertyNode.getChildNodes().item(i);
        if ((node.getNodeType() == Node.ELEMENT_NODE) && node.getNodeName().equals("value")) {
          value = parseTextNode(node);
        }
      }
    }
    if (propertyElement.hasAttribute("pluginname")) {
      p.setProperty("pluginname", propertyElement.getAttributeNode("pluginname").getNodeValue());
    }
    p.setProperty(name, value);
  }

  /**
   * Parse a String from a textually type node.
   *
   * @param node The node.
   * @return The String.
   */
  private static String parseTextNode(Node node) {
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < node.getChildNodes().getLength(); i++) {
      Node childNode = node.getChildNodes().item(i);
      if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) {
        buffer.append(childNode.getNodeValue());
      }
    }
    return buffer.toString();
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.