Coverage Report - com.sdmetrics.metrics.RuleEngine - www.sdmetrics.com
 
Classes in this File Line Coverage Branch Coverage Complexity
RuleEngine
100%
47/47
87%
14/16
3,5
 
 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.Collections;
 26  
 import java.util.HashMap;
 27  
 import java.util.HashSet;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 
 31  
 import com.sdmetrics.math.ExpressionNode;
 32  
 import com.sdmetrics.math.ExpressionParser;
 33  
 import com.sdmetrics.model.MetaModelElement;
 34  
 import com.sdmetrics.model.ModelElement;
 35  
 
 36  
 /**
 37  
  * Checks the design rules for a model and reports violations.
 38  
  */
 39  
 
 40  
 public class RuleEngine {
 41  
         /**
 42  
          * Prefix in the rule exemption information to indicate exemption of a
 43  
          * certain design rule.
 44  
          */
 45  
         private final static String EXEMPTIONPREFIX = "violates_";
 46  
 
 47  
         /** Metrics engine to use for rule checking. */
 48  
         private final MetricsEngine engine;
 49  
         /** Cache to keep the rule calculation procedures for reuse. */
 50  
         private final RuleProcedureCache ruleProcedures;
 51  
 
 52  
         /** Element type that holds the rule exemption information. */
 53  
         private final MetaModelElement exemptionType;
 54  
         /**
 55  
          * Expression that produces the rule exemption information for the exemption
 56  
          * type element.
 57  
          */
 58  
         private final ExpressionNode exemptionExpression;
 59  
 
 60  
         /**
 61  
          * Cache for rule procedures to store values that are expensive to
 62  
          * calculate.
 63  
          */
 64  118
         private final HashMap<Object, Object> valueCache = new HashMap<Object, Object>();
 65  
 
 66  
         /**
 67  
          * Creates a new rule engine.
 68  
          * 
 69  
          * @param me Metrics engine to use for rule checking.
 70  
          */
 71  118
         public RuleEngine(MetricsEngine me) {
 72  118
                 this.engine = me;
 73  118
                 MetricStore store = me.getMetricStore();
 74  118
                 ruleProcedures = store.getRuleProcedures();
 75  118
                 exemptionType = store.getRuleExemptionType();
 76  118
                 exemptionExpression = new ExpressionNode(store.getRuleExemptionTag());
 77  118
         }
 78  
 
 79  
         /**
 80  
          * Returns the metrics engine that this rule engine uses.
 81  
          * 
 82  
          * @return Metrics Engine of this rule engine.
 83  
          */
 84  
         public MetricsEngine getMetricsEngine() {
 85  26
                 return engine;
 86  
         }
 87  
 
 88  
         /**
 89  
          * Gets the names of the rules that a model element is allowed to violate.
 90  
          * Searches through the tagged values or comments of the model element,
 91  
          * looking for occurrences of the exemption prefix "_violates". Extracts the
 92  
          * identifiers following each such occurrence. These define the names of the
 93  
          * rules the element is allowed to violate. Returns the set of these names.
 94  
          * 
 95  
          * @param element Element to retrieve rule exemptions for.
 96  
          * @return The set of the names of the rules the model element is allowed to
 97  
          *         violate.
 98  
          * @throws SDMetricsException The tagged values or comments could not be
 99  
          *         accessed.
 100  
          */
 101  
         public Collection<String> collectExemptedRules(ModelElement element)
 102  
                         throws SDMetricsException {
 103  3
                 Collection<ModelElement> ownedElements = element.getOwnedElements();
 104  3
                 if (exemptionType == null || ownedElements == null)
 105  1
                         return Collections.emptySet();
 106  
 
 107  2
                 HashSet<String> exemptRuleNames = new HashSet<String>(2);
 108  
                 // find all tagged values or comments
 109  7
                 for (ModelElement child : ownedElements) {
 110  3
                         if (child.getType() != exemptionType)
 111  1
                                 continue;
 112  
 
 113  
                         // Get the tag value or comment body string
 114  4
                         String tag = String.valueOf(engine.evalExpression(child,
 115  2
                                         exemptionExpression, null));
 116  
 
 117  
                         // find all occurrences of "violates_" in the tag value
 118  2
                         int index = tag.indexOf(EXEMPTIONPREFIX);
 119  7
                         while (index >= 0) {
 120  
                                 // extract the rule name following the prefix
 121  3
                                 index += EXEMPTIONPREFIX.length();
 122  3
                                 int ruleNameEndIndex = index;
 123  41
                                 while (ruleNameEndIndex < tag.length()
 124  76
                                                 && ExpressionParser.isIdentifierCharacter(tag
 125  76
                                                                 .charAt(ruleNameEndIndex))) {
 126  35
                                         ruleNameEndIndex++;
 127  
                                 }
 128  
                                 // add rule name to the set of exempted rules
 129  3
                                 exemptRuleNames.add(tag.substring(index, ruleNameEndIndex));
 130  3
                                 index = tag.indexOf(EXEMPTIONPREFIX, ruleNameEndIndex);
 131  
                         }
 132  
                 }
 133  2
                 return exemptRuleNames;
 134  
         }
 135  
 
 136  
         /**
 137  
          * Checks a design rule for a model element.
 138  
          * 
 139  
          * @param element The model element to check.
 140  
          * @param rule The rule to check.
 141  
          * @return The list of detected rule violations.
 142  
          * @throws SDMetricsException An error occurred checking the design rule.
 143  
          */
 144  
         public List<RuleViolation> checkRule(ModelElement element, Rule rule)
 145  
                         throws SDMetricsException {
 146  
                 try {
 147  
                         // Obtain the procedure to check the rule
 148  23
                         String procedureName = rule.getProcedureName();
 149  46
                         RuleProcedure procedure = ruleProcedures
 150  23
                                         .getProcedure(procedureName);
 151  
 
 152  
                         // Perform the check, return the procedure for reuse
 153  23
                         procedure.setRuleEngine(this);
 154  23
                         procedure.checkRule(element, rule);
 155  21
                         List<RuleViolation> result = procedure.getViolations();
 156  21
                         ruleProcedures.returnProcedure(procedure);
 157  21
                         if (result == null)
 158  7
                                 return Collections.emptyList();
 159  14
                         return result;
 160  1
                 } catch (SDMetricsException ex) {
 161  1
                         ex.fillInPerpetrators(element, rule);
 162  1
                         throw ex;
 163  1
                 } catch (RuntimeException ex) {
 164  
                         // wrap exceptions in an SDMetricsException so we know
 165  
                         // what rule/element is to blame
 166  1
                         throw new SDMetricsException(element, rule, ex);
 167  
                 }
 168  
         }
 169  
 
 170  
         /**
 171  
          * Gets the value cache for rule procedures to store values that are
 172  
          * expensive to calculate.
 173  
          * <p>
 174  
          * If there is expensive data to calculate that can be reused across model
 175  
          * elements, rule procedures may store them here.
 176  
          * 
 177  
          * @return value cache for rule procedures
 178  
          */
 179  
         Map<Object, Object> getValuesCache() {
 180  10
                 return valueCache;
 181  
         }
 182  
 
 183  
         /**
 184  
          * Clears the value cache for rule procedures.
 185  
          * <p>
 186  
          * The cache can be cleared any time. When you perform a comprehensive rule
 187  
          * check (checking all rules for all model elements), a good strategy is to
 188  
          * check all elements of one type, and clear the cache before proceeding
 189  
          * with the next type.
 190  
          */
 191  
         public void clearValuesCache() {
 192  1
                 valueCache.clear();
 193  1
         }
 194  
 }