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  
                         }
 63  10
                         String problem = validateFilter(filterRoot);
 64  10
                         if (problem.length() > 0) {
 65  4
                                 throw new SDMetricsException(null, null,
 66  4
                                                 "Invalid design rule filter: " + filter + "\n"
 67  2
                                                                 + problem);
 68  
                         }
 69  
                 }
 70  11
         }
 71  
 
 72  
         /**
 73  
          * Checks if the application area(s) of a design rule match this filter.
 74  
          * 
 75  
          * @param rule The design rule to check.
 76  
          * @return <code>true</code> if the design rule matches and should be
 77  
          *         checked according to this filter.
 78  
          */
 79  
         public boolean match(Rule rule) {
 80  25
                 return evalAppAreaMatch(rule.getApplicableAreas(), filterRoot);
 81  
         }
 82  
 
 83  
         /* Recursively evaluate the rule's operator tree to determine the match. */
 84  
         private boolean evalAppAreaMatch(Collection<String> areas,
 85  
                         ExpressionNode filter) {
 86  54
                 if (filter == null) {
 87  4
                         return true; // no filter => accept everything
 88  
                 }
 89  
 
 90  50
                 if (filter.isIdentifier()) {
 91  27
                         if (areas == null) {
 92  5
                                 return true; // no phases specified => all phases are accepted!
 93  
                         }
 94  22
                         return areas.contains(filter.getValue());
 95  23
                 } else if (filter.isStringConstant()) {
 96  3
                         if (areas == null) {
 97  1
                                 return false; // no phases specified => not accepted
 98  
                         }
 99  2
                         return areas.contains(filter.getValue());
 100  
                 }
 101  
 
 102  20
                 if ("!".equals(filter.getValue())) {
 103  6
                         return !evalAppAreaMatch(areas, filter.getLeftNode());
 104  
                 }
 105  
 
 106  14
                 if ("&".equals(filter.getValue())) {
 107  10
                         return evalAppAreaMatch(areas, filter.getLeftNode())
 108  5
                                         && evalAppAreaMatch(areas, filter.getRightNode());
 109  
                 }
 110  
 
 111  9
                 return evalAppAreaMatch(areas, filter.getLeftNode())
 112  4
                                 || evalAppAreaMatch(areas, filter.getRightNode());
 113  
         }
 114  
 
 115  
         /**
 116  
          * Validates a rule filter expression.
 117  
          * 
 118  
          * @param filter Operator tree of the rule filter string.
 119  
          * @return Empty string if everything is OK, else a description of the
 120  
          *         problems found.
 121  
          */
 122  
         private String validateFilter(ExpressionNode filter) {
 123  29
                 if (filter == null) {
 124  0
                         return "";
 125  
                 }
 126  29
                 if (filter.isIdentifier() || filter.isStringConstant()) {
 127  16
                         return "";
 128  
                 }
 129  13
                 if (filter.isOperation()) {
 130  12
                         if ("&".equals(filter.getValue()) || "|".equals(filter.getValue())) {
 131  16
                                 return validateFilter(filter.getLeftNode())
 132  8
                                                 + validateFilter(filter.getRightNode());
 133  4
                         } else if ("!".equals(filter.getValue())) {
 134  3
                                 return validateFilter(filter.getLeftNode());
 135  
                         } else {
 136  2
                                 return "Operation '" + filter.getValue()
 137  1
                                                 + "' not allowed in filter expressions.";
 138  
                         }
 139  
                 }
 140  1
                 return "Unexpected operand '" + filter.getValue() + "'.";
 141  
         }
 142  
 
 143  
         /**
 144  
          * Checks the identifiers of the rule filter for plausibility.
 145  
          * <p>
 146  
          * An identifier is valid if it is explicitly defined as the application
 147  
          * area of at least one rule. Otherwise, the identifier is suspect because
 148  
          * it is not listed by any of the rules.
 149  
          * 
 150  
          * @param metricStore Contains the definitions of the rules
 151  
          * @return Empty string if all application areas of the filter string are
 152  
          *         valid, otherwise the name of a suspect application area in the
 153  
          *         filter string.
 154  
          */
 155  
         public String checkIdentifiers(MetricStore metricStore) {
 156  
                 // collect explicitly defined application areas from all design rules
 157  4
                 HashSet<String> explicitAppAreas = new HashSet<String>();
 158  60
                 for (MetaModelElement type : metricStore.getMetaModel()) {
 159  156
                         for (Rule rule : metricStore.getRules(type)) {
 160  52
                                 if (rule.isEnabled() && rule.getApplicableAreas() != null) {
 161  28
                                         explicitAppAreas.addAll(rule.getApplicableAreas());
 162  
                                 }
 163  
                         }
 164  
                 }
 165  
 
 166  4
                 return checkIdentifiers(explicitAppAreas, filterRoot);
 167  
         }
 168  
 
 169  
         /* Recursively check the identifiers in the operator tree. */
 170  
         private String checkIdentifiers(Collection<String> areas,
 171  
                         ExpressionNode node) {
 172  11
                 if (node == null) {
 173  2
                         return "";
 174  
                 }
 175  9
                 if (node.isIdentifier() || node.isStringConstant()) {
 176  5
                         String appArea = node.getValue();
 177  5
                         return areas.contains(appArea) ? "" : appArea;
 178  
                 }
 179  4
                 String result = checkIdentifiers(areas, node.getLeftNode());
 180  4
                 if (result.length() == 0) {
 181  3
                         result = checkIdentifiers(areas, node.getRightNode());
 182  
                 }
 183  4
                 return result;
 184  
         }
 185  
 }