Coverage Report - com.sdmetrics.metrics.RuleFilter - www.sdmetrics.com
 
Classes in this File Line Coverage Branch Coverage Complexity
RuleFilter
98%
62/63
98%
63/64
7,429
 
 1  
 /*
 2  
  * SDMetrics Open Core for UML design measurement
 3  
  * Copyright (c) Juergen Wuest
 4  
  * To contact the author, see <http://www.sdmetrics.com/Contact.html>.
 5  
  * 
 6  
  * This file is part of the SDMetrics Open Core.
 7  
  * 
 8  
  * SDMetrics Open Core is free software: you can redistribute it and/or modify
 9  
  * it under the terms of the GNU Affero General Public License as
 10  
  * published by the Free Software Foundation, either version 3 of the
 11  
  * License, or (at your option) any later version.
 12  
     
 13  
  * SDMetrics Open Core is distributed in the hope that it will be useful,
 14  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16  
  * GNU Affero General Public License for more details.
 17  
  *
 18  
  * You should have received a copy of the GNU Affero General Public License
 19  
  * along with SDMetrics Open Core.  If not, see <http://www.gnu.org/licenses/>.
 20  
  *
 21  
  */
 22  
 package com.sdmetrics.metrics;
 23  
 
 24  
 import java.util.Collection;
 25  
 import java.util.HashSet;
 26  
 
 27  
 import com.sdmetrics.math.ExpressionNode;
 28  
 import com.sdmetrics.math.ExpressionParser;
 29  
 import com.sdmetrics.model.MetaModelElement;
 30  
 
 31  
 /**
 32  
  * Represents and evaluates a design rule filter expression.
 33  
  * <p>
 34  
  * A design rule can be assigned one or more "application areas", for example,
 35  
  * rules aimed at the analysis phase, the design phase, rules for real time
 36  
  * systems, etc. The rule filter selects rules based on their application areas.
 37  
  */
 38  
 public class RuleFilter {
 39  
         /** Operator tree of the filter expression. */
 40  
         private ExpressionNode filterRoot;
 41  
 
 42  
         /** Constructs a new empty rule filter that accepts all application areas. */
 43  1
         public RuleFilter() {
 44  1
                 filterRoot = null;
 45  1
         }
 46  
 
 47  
         /**
 48  
          * Construct a new rule filter from a filter string.
 49  
          * 
 50  
          * @param filter The rule filter string.
 51  
          * @throws SDMetricsException The filter string could not be parsed or
 52  
          *         contains illegal operations.
 53  
          */
 54  14
         public RuleFilter(String filter) throws SDMetricsException {
 55  14
                 if (filter != null && filter.length() > 0) {
 56  11
                         ExpressionParser p = new ExpressionParser();
 57  11
                         filterRoot = p.parseExpression(filter);
 58  11
                         if (filterRoot == null)
 59  2
                                 throw new SDMetricsException(null, null,
 60  2
                                                 "Invalid design rule filter: " + filter + "\n"
 61  1
                                                                 + p.getErrorInfo());
 62  10
                         String problem = validateFilter(filterRoot);
 63  10
                         if (problem.length() > 0)
 64  4
                                 throw new SDMetricsException(null, null,
 65  4
                                                 "Invalid design rule filter: " + filter + "\n"
 66  2
                                                                 + problem);
 67  
                 }
 68  11
         }
 69  
 
 70  
         /**
 71  
          * Checks if the application area(s) of a design rule match this filter.
 72  
          * 
 73  
          * @param rule The design rule to check.
 74  
          * @return <code>true</code> if the design rule matches and should be
 75  
          *         checked according to this filter.
 76  
          */
 77  
         public boolean match(Rule rule) {
 78  25
                 return evalAppAreaMatch(rule.getApplicableAreas(), filterRoot);
 79  
         }
 80  
 
 81  
         /* Recursively evaluate the rule's operator tree to determine the match. */
 82  
         private boolean evalAppAreaMatch(Collection<String> areas,
 83  
                         ExpressionNode filter) {
 84  54
                 if (filter == null)
 85  4
                         return true; // no filter => accept everything
 86  
 
 87  50
                 if (filter.isIdentifier()) {
 88  27
                         if (areas == null)
 89  5
                                 return true; // no phases specified => all phases are accepted!
 90  22
                         return areas.contains(filter.getValue());
 91  23
                 } else if (filter.isStringConstant()) {
 92  3
                         if (areas == null)
 93  1
                                 return false; // no phases specified => not accepted
 94  2
                         return areas.contains(filter.getValue());
 95  
                 }
 96  
 
 97  20
                 if ("!".equals(filter.getValue()))
 98  6
                         return !evalAppAreaMatch(areas, filter.getLeftNode());
 99  
 
 100  14
                 if ("&".equals(filter.getValue()))
 101  10
                         return evalAppAreaMatch(areas, filter.getLeftNode())
 102  5
                                         && evalAppAreaMatch(areas, filter.getRightNode());
 103  
 
 104  9
                 return evalAppAreaMatch(areas, filter.getLeftNode())
 105  4
                                 || evalAppAreaMatch(areas, filter.getRightNode());
 106  
         }
 107  
 
 108  
         /**
 109  
          * Validates a rule filter expression.
 110  
          * 
 111  
          * @param filter Operator tree of the rule filter string.
 112  
          * @return Empty string if everything is OK, else a description of the
 113  
          *         problems found.
 114  
          */
 115  
         private String validateFilter(ExpressionNode filter) {
 116  29
                 if (filter == null)
 117  0
                         return "";
 118  29
                 if (filter.isIdentifier() || filter.isStringConstant())
 119  16
                         return "";
 120  13
                 if (filter.isOperation())
 121  12
                         if ("&".equals(filter.getValue()) || "|".equals(filter.getValue()))
 122  16
                                 return validateFilter(filter.getLeftNode())
 123  8
                                                 + validateFilter(filter.getRightNode());
 124  4
                         else if ("!".equals(filter.getValue()))
 125  3
                                 return validateFilter(filter.getLeftNode());
 126  
                         else
 127  2
                                 return "Operation '" + filter.getValue()
 128  1
                                                 + "' not allowed in filter expressions.";
 129  1
                 return "Unexpected operand '" + filter.getValue() + "'.";
 130  
         }
 131  
 
 132  
         /**
 133  
          * Checks the identifiers of the rule filter for plausibility.
 134  
          * <p>
 135  
          * An identifier is valid if it is explicitly defined as the application
 136  
          * area of at least one rule. Otherwise, the identifier is suspect because
 137  
          * it is not listed by any of the rules.
 138  
          * 
 139  
          * @param metricStore Contains the definitions of the rules
 140  
          * @return Empty string if all application areas of the filter string are
 141  
          *         valid, otherwise the name of a suspect application area in the
 142  
          *         filter string.
 143  
          */
 144  
         public String checkIdentifiers(MetricStore metricStore) {
 145  
                 // collect explicitly defined application areas from all design rules
 146  4
                 HashSet<String> explicitAppAreas = new HashSet<String>();
 147  60
                 for (MetaModelElement type : metricStore.getMetaModel()) {
 148  156
                         for (Rule rule : metricStore.getRules(type)) {
 149  52
                                 if (rule.isEnabled() && rule.getApplicableAreas() != null)
 150  28
                                         explicitAppAreas.addAll(rule.getApplicableAreas());
 151  
                         }
 152  
                 }
 153  
 
 154  4
                 return checkIdentifiers(explicitAppAreas, filterRoot);
 155  
         }
 156  
 
 157  
         /* Recursively check the identifiers in the operator tree. */
 158  
         private String checkIdentifiers(Collection<String> areas,
 159  
                         ExpressionNode node) {
 160  11
                 if (node == null)
 161  2
                         return "";
 162  9
                 if (node.isIdentifier() || node.isStringConstant()) {
 163  5
                         String appArea = node.getValue();
 164  5
                         return areas.contains(appArea) ? "" : appArea;
 165  
                 }
 166  4
                 String result = checkIdentifiers(areas, node.getLeftNode());
 167  4
                 if (result.length() == 0)
 168  3
                         result = checkIdentifiers(areas, node.getRightNode());
 169  4
                 return result;
 170  
         }
 171  
 }