de.andrena.tools.macker.rule.RuleSetBuilder.java Source code

Java tutorial

Introduction

Here is the source code for de.andrena.tools.macker.rule.RuleSetBuilder.java

Source

/*______________________________________________________________________________
 *
 * Macker   http://innig.net/macker/
 *
 * Copyright 2002-2003 Paul Cantrell
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 2, as published by the
 * Free Software Foundation. See the file LICENSE.html for more information.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
 * Place, Suite 330 / Boston, MA 02111-1307 / USA.
 *______________________________________________________________________________
 */

package de.andrena.tools.macker.rule;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.input.sax.XMLReaders;

import de.andrena.tools.macker.rule.filter.Filter;
import de.andrena.tools.macker.rule.filter.FilterFinder;

public class RuleSetBuilder {

    private final SAXBuilder saxBuilder;

    public RuleSetBuilder() {
        saxBuilder = new SAXBuilder(XMLReaders.XSDVALIDATING);
    }

    public Collection<RuleSet> build(final InputStream is) throws RulesException {
        try {
            return build(saxBuilder.build(is));
        } catch (final JDOMException jdome) {
            throw new RulesDocumentException(jdome);
        } catch (final IOException ioe) {
            throw new RulesDocumentException(ioe);
        }
    }

    public Collection<RuleSet> build(final Reader reader) throws RulesException {
        try {
            return build(saxBuilder.build(reader));
        } catch (final JDOMException jdome) {
            throw new RulesDocumentException(jdome);
        } catch (final IOException ioe) {
            throw new RulesDocumentException(ioe);
        }
    }

    public Collection<RuleSet> build(final File file) throws RulesException {
        try {
            return build(saxBuilder.build(file));
        } catch (final JDOMException jdome) {
            throw new RulesDocumentException(jdome);
        } catch (final IOException ioe) {
            throw new RulesDocumentException(ioe);
        }
    }

    public Collection<RuleSet> build(final String fileName) throws RulesException {
        return build(new File(fileName));
    }

    public Collection<RuleSet> build(final Document doc) throws RulesException {
        return build(doc.getRootElement());
    }

    public Collection<RuleSet> build(final Element elem) throws RulesException {
        final Collection<RuleSet> ruleSets = new ArrayList<RuleSet>();

        for (final Element rsElem : getChildren(elem, "ruleset")) {
            ruleSets.add(buildRuleSet(rsElem, RuleSet.getMackerDefaults()));
        }
        return ruleSets;
    }

    private Collection<Element> getChildren(final Element elem, final String tagName) {
        return elem.getChildren(tagName);
    }

    public RuleSet buildRuleSet(final Element ruleSetElem, final RuleSet parent) throws RulesException {
        final RuleSet ruleSet = new RuleSet(parent);

        final String name = ruleSetElem.getAttributeValue("name");
        if (name != null) {
            ruleSet.setName(name);
        }

        buildSeverity(ruleSet, ruleSetElem);

        for (final Element subElem : getChildren(ruleSetElem)) {
            final String subElemName = subElem.getName();
            if (subElemName.equals("pattern")) {
                final String patternName = subElem.getAttributeValue("name");
                if (ruleSet.declaresPattern(patternName)) {
                    throw new RulesDocumentException(subElem,
                            "Pattern named \"" + patternName + "\" is already defined in this context");
                }

                ruleSet.setPattern(patternName, buildPattern(subElem, ruleSet));
            } else if (subElemName.equals("subset")) {
                if (ruleSet.getSubsetPattern() != null) {
                    throw new RulesDocumentException(subElem,
                            "<ruleset> may only contain a single <subset> element");
                }
                ruleSet.setSubsetPattern(buildPattern(subElem, ruleSet));
            } else if (subElemName.equals("access-rule")) {
                ruleSet.addRule(buildAccessRule(subElem, ruleSet));
            } else if (subElemName.equals("var")) {
                ruleSet.addRule(buildVariable(subElem, ruleSet));
            } else if (subElemName.equals("foreach")) {
                ruleSet.addRule(buildForEach(subElem, ruleSet));
            } else if (subElemName.equals("ruleset")) {
                ruleSet.addRule(buildRuleSet(subElem, ruleSet));
            } else if (subElemName.equals("message")) {
                ruleSet.addRule(buildMessage(subElem, ruleSet));
            }
        }

        return ruleSet;
    }

    public Pattern buildPattern(final Element patternElem, final RuleSet ruleSet) throws RulesException {
        return buildPattern(patternElem, ruleSet, true, null);
    }

    public Pattern buildPattern(final Element patternElem, final RuleSet ruleSet, final boolean isTopElem,
            final Pattern nextPat) throws RulesException {
        // handle options

        final String otherPatName = patternElem.getAttributeValue("pattern");
        final String className = getClassNameAttributeValue(patternElem);
        final String filterName = patternElem.getAttributeValue("filter");

        CompositePatternType patType;
        if (patternElem.getName().equals("include")) {
            patType = CompositePatternType.INCLUDE;
        } else if (patternElem.getName().equals("exclude")) {
            patType = filterName == null ? CompositePatternType.EXCLUDE : CompositePatternType.INCLUDE;
        } else if (isTopElem) {
            patType = CompositePatternType.INCLUDE;
        } else {
            throw new RulesDocumentException(patternElem,
                    "Invalid element <" + patternElem.getName() + "> --" + " expected <include> or <exclude>");
        }

        if (otherPatName != null && className != null) {
            throw new RulesDocumentException(patternElem,
                    "patterns cannot have both a \"pattern\" and a \"class\" attribute");
        }

        // do the head thing

        Pattern head = null;
        if (className != null) {
            head = new RegexPattern(className);
        } else if (otherPatName != null) {
            head = ruleSet.getPattern(otherPatName);
            if (head == null) {
                throw new UndeclaredPatternException(otherPatName);
            }
        }

        // build up children

        Pattern childrenPat = null;
        final List<Element> children = new ArrayList<Element>(getChildren(patternElem)); // !
        // workaround
        // for
        // bug
        // in
        // JUnit
        // List children = patternElem.getChildren(); // this should work
        // instead when JUnit bug is fixed
        for (final ListIterator<Element> childIter = children.listIterator(children.size()); childIter
                .hasPrevious();) {
            final Element subElem = childIter.previous();
            if (subElem.getName().equals("message")) {
                continue;
            }

            childrenPat = buildPattern(subElem, ruleSet, false, childrenPat);
        }

        // wrap head in a filter if necessary

        if (filterName != null) {
            final Map<String, String> options = new HashMap<String, String>();
            for (final Attribute attr : getAttributes(patternElem)) {
                options.put(attr.getName(), attr.getValue());
            }
            options.remove("name");
            options.remove("pattern");
            options.remove("class");
            options.remove("regex");

            final Filter filter = FilterFinder.findFilter(filterName);
            head = filter.createPattern(ruleSet,
                    head == null ? new ArrayList<Pattern>() : Collections.singletonList(head), options);

            if (patternElem.getName().equals("exclude")) {
                head = CompositePattern.create(CompositePatternType.EXCLUDE, head, null, null);
            }
        }

        // pull together composite

        return CompositePattern.create(patType, head, childrenPat, nextPat);
    }

    private Collection<Attribute> getAttributes(final Element patternElem) {
        return patternElem.getAttributes();
    }

    public Variable buildVariable(final Element forEachElem, final RuleSet parent) throws RulesException {
        final String varName = forEachElem.getAttributeValue("name");
        if (varName == null) {
            throw new RulesDocumentException(forEachElem, "<var> is missing the \"name\" attribute");
        }

        final String value = forEachElem.getAttributeValue("value");
        if (value == null) {
            throw new RulesDocumentException(forEachElem, "<var> is missing the \"value\" attribute");
        }

        return new Variable(parent, varName, value);
    }

    public Message buildMessage(final Element messageElem, final RuleSet parent) throws RulesException {
        final Message message = new Message(parent, messageElem.getText());
        buildSeverity(message, messageElem);
        return message;
    }

    public ForEach buildForEach(final Element forEachElem, final RuleSet parent) throws RulesException {
        final String varName = forEachElem.getAttributeValue("var");
        if (varName == null) {
            throw new RulesDocumentException(forEachElem, "<foreach> is missing the \"var\" attribute");
        }

        final String className = getClassNameAttributeValue(forEachElem);
        if (className == null) {
            throw new RulesDocumentException(forEachElem, "<foreach> is missing the \"class\" attribute");
        }

        final ForEach forEach = new ForEach(parent);
        forEach.setVariableName(varName);
        forEach.setRegex(className);
        forEach.setRuleSet(buildRuleSet(forEachElem, parent));
        return forEach;
    }

    public AccessRule buildAccessRule(final Element ruleElem, final RuleSet ruleSet) throws RulesException {
        AccessRule prevRule = null, topRule = null;
        for (final Element subElem : getChildren(ruleElem)) {
            final AccessRule accRule = new AccessRule(ruleSet);

            if (subElem.getName().equals("allow")) {
                accRule.setType(AccessRuleType.ALLOW);
            } else if (subElem.getName().equals("deny")) {
                accRule.setType(AccessRuleType.DENY);
            } else if (subElem.getName().equals("from") || subElem.getName().equals("to")
                    || subElem.getName().equals("message")) {
                continue;
            } else {
                throw new RulesDocumentException(subElem, "Invalid element <" + subElem.getName() + "> --"
                        + " expected an access rule (<deny> or <allow>)");
            }

            final Element fromElem = subElem.getChild("from");
            if (fromElem != null) {
                accRule.setFrom(buildPattern(fromElem, ruleSet));
            }

            final Element toElem = subElem.getChild("to");
            if (toElem != null) {
                accRule.setTo(buildPattern(toElem, ruleSet));
            }

            if (!subElem.getChildren().isEmpty()) {
                accRule.setChild(buildAccessRule(subElem, ruleSet));
            }

            if (topRule == null) {
                topRule = accRule;
            } else {
                prevRule.setNext(accRule);
            }
            prevRule = accRule;
        }
        if (topRule != null) {
            topRule.setMessage(ruleElem.getChildText("message"));
            buildSeverity(topRule, ruleElem);
        }
        return topRule;
    }

    private List<Element> getChildren(final Element ruleElem) {
        return ruleElem.getChildren();
    }

    public void buildSeverity(final Rule rule, final Element elem) throws RulesDocumentException {
        final String severityS = elem.getAttributeValue("severity");
        if (severityS != null && !"".equals(severityS)) {
            RuleSeverity severity;
            try {
                severity = RuleSeverity.fromName(severityS);
            } catch (final IllegalArgumentException iae) {
                throw new RulesDocumentException(elem, iae.getMessage());
            }
            rule.setSeverity(severity);
        }
    }

    private String getClassNameAttributeValue(final Element elem) {
        String value = elem.getAttributeValue("class");
        if (value == null) {
            value = elem.getAttributeValue("regex");
            if (value != null) {
                System.err.println(
                        "WARNING: The \"regex\" attribute is deprecated, and will be removed in v1.0.  Use \"class\" instead");
            }
        }
        return value;
    }

}