Coverage Report - com.sdmetrics.metrics.ProcedureCache - www.sdmetrics.com
 
Classes in this File Line Coverage Branch Coverage Complexity
ProcedureCache
100%
53/53
100%
4/4
2,143
ProcedureCache$ProcedureInfo
100%
2/2
N/A
2,143
 
 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.HashMap;
 25  
 import java.util.LinkedList;
 26  
 
 27  
 /**
 28  
  * A cache for calculation procedure and expression operation instances during a
 29  
  * calculation run.
 30  
  * <p>
 31  
  * Each calculation of a metric, set, or rule for a model elements requires a
 32  
  * calculation procedure instance that executes the calculation algorithm.
 33  
  * Likewise, each operation in a metric, set or boolean expression requires an
 34  
  * operation instance to calculate the operation value. Because the calculation
 35  
  * classes are configurable, their instantiation involves reflection. To avoid
 36  
  * having to create lots of calculation procedure instances by reflection, we
 37  
  * keep them in a cache for reuse.
 38  
  * 
 39  
  * @param <T> The type of calculation procedures stored in this cache.
 40  
  */
 41  
 abstract class ProcedureCache<T extends AbstractProcedure> {
 42  
 
 43  
         /** Represents one calculation procedure class. */
 44  25488
         private class ProcedureInfo {
 45  
                 /** The calculation procedure class. */
 46  
                 Class<? extends T> procedureClass;
 47  
                 /** The available instances of the class. */
 48  12744
                 LinkedList<T> availableInstances = new LinkedList<T>();
 49  
         }
 50  
 
 51  
         /**
 52  
          * Stores the calculation procedure instances by their procedure names
 53  
          * (projection, compoundmetric, etc).
 54  
          */
 55  1049
         private final HashMap<String, ProcedureInfo> buckets = new HashMap<String, ProcedureInfo>();
 56  
 
 57  
         /**
 58  
          * Describes elements in the cache (such as"metric procedure" or
 59  
          * "boolean operator").
 60  
          */
 61  
         private final String procedureDescription;
 62  
 
 63  
         /**
 64  
          * Constructor.
 65  
          * 
 66  
          * @param description Description of the elements in the cache.
 67  
          */
 68  1049
         ProcedureCache(String description) {
 69  1049
                 this.procedureDescription = description;
 70  1049
         }
 71  
 
 72  
         /**
 73  
          * Adds a custom defined procedure from the metric definition file. Checks
 74  
          * if the procedure class can be loaded, has the expected type, and can be
 75  
          * instantiated.
 76  
          * 
 77  
          * @param name Name of the procedure
 78  
          * @param className Fully qualified class name of the procedure
 79  
          * @throws SDMetricsException Procedure class is unsuitable.
 80  
          */
 81  
         void addProcedureClass(String name, String className)
 82  
                         throws SDMetricsException {
 83  
 
 84  915
                 Class<? extends T> procedureClass = null;
 85  
                 try {
 86  915
                         procedureClass = loadClass(className);
 87  912
                 } catch (Exception ex) {
 88  6
                         throw new SDMetricsException(
 89  3
                                         null,
 90  3
                                         null,
 91  6
                                         "Could not load class '"
 92  3
                                                         + className
 93  3
                                                         + "'. "
 94  3
                                                         + "\n"
 95  3
                                                         + ex.getClass().getSimpleName()
 96  3
                                                         + ": "
 97  3
                                                         + ex.getMessage()
 98  3
                                                         + "\nMake sure the class is on the classpath, has public visibility, and extends the required base class.");
 99  
                 }
 100  
 
 101  912
                 addProcedureClass(name, procedureClass);
 102  
 
 103  
                 // We create the first instance immediately to find potential errors
 104  
                 // as early as possible.
 105  912
                 T procedure = getProcedure(name);
 106  911
                 returnProcedure(procedure);
 107  911
         }
 108  
 
 109  
         /**
 110  
          * Adds a standard procedure that is available at compile time.
 111  
          * 
 112  
          * @param name Name of the procedure
 113  
          * @param procClass Class of the procedure
 114  
          */
 115  
         void addProcedureClass(String name, Class<? extends T> procClass) {
 116  12744
                 ProcedureInfo pInfo = new ProcedureInfo();
 117  12744
                 pInfo.procedureClass = procClass;
 118  12744
                 buckets.put(name, pInfo);
 119  12744
         }
 120  
 
 121  
         /**
 122  
          * Checks if this cache has a calculation procedure of a given name.
 123  
          * 
 124  
          * @param name Name of the procedure
 125  
          * @return <code>true</code> if a procedure class has been registered for
 126  
          *         this name
 127  
          */
 128  
         boolean hasProcedure(String name) {
 129  906
                 return buckets.containsKey(name);
 130  
         }
 131  
 
 132  
         /**
 133  
          * Obtains a procedure instance from the cache.
 134  
          * <p>
 135  
          * The instance should be returned to the cache after use.
 136  
          * 
 137  
          * @param procedureName Name of the procedure
 138  
          * @return An instance the procedure's class
 139  
          * @throws SDMetricsException The instance could not be created.
 140  
          */
 141  
         T getProcedure(String procedureName) throws SDMetricsException {
 142  3068
                 ProcedureInfo pInfo = buckets.get(procedureName);
 143  3068
                 if (pInfo == null)
 144  12
                         throw new SDMetricsException(null, null, "Unknown "
 145  6
                                         + procedureDescription + " '" + procedureName + "'.");
 146  
 
 147  3062
                 if (pInfo.availableInstances.isEmpty())
 148  
                         try {
 149  
                                 // Create and return a new instance
 150  2315
                                 T result = pInfo.procedureClass.getConstructor(new Class<?>[0])
 151  1157
                                                 .newInstance(new Object[0]);
 152  1157
                                 result.setName(procedureName);
 153  1157
                                 return result;
 154  1
                         } catch (Exception ex) {
 155  2
                                 throw new SDMetricsException(
 156  1
                                                 null,
 157  1
                                                 null,
 158  2
                                                 "Could not instantiate class '"
 159  1
                                                                 + pInfo.procedureClass.getName()
 160  1
                                                                 + "'. "
 161  1
                                                                 + "\n"
 162  1
                                                                 + ex.getClass().getSimpleName()
 163  1
                                                                 + ": "
 164  1
                                                                 + ex.getMessage()
 165  1
                                                                 + "\nMake sure the class has a public constructor with empty argument list and throws no exceptions.");
 166  
                         }
 167  
 
 168  
                 // return an existing instance from the cache
 169  1904
                 return pInfo.availableInstances.removeLast();
 170  
         }
 171  
 
 172  
         /**
 173  
          * Returns a procedure instance to the cache for reuse.
 174  
          * 
 175  
          * @param procedure The procedure instance.
 176  
          */
 177  
         void returnProcedure(T procedure) {
 178  3028
                 procedure.clear();
 179  3028
                 ProcedureInfo pInfo = buckets.get(procedure.getName());
 180  3028
                 pInfo.availableInstances.add(procedure);
 181  3028
         }
 182  
 
 183  
         /**
 184  
          * Load the procedure class.
 185  
          * 
 186  
          * @param className Fully qualified class name of the procedure class.
 187  
          * @return Class object for the specified class
 188  
          * @throws ClassNotFoundException The class could not be loaded.
 189  
          */
 190  
         abstract protected Class<? extends T> loadClass(String className)
 191  
                         throws ClassNotFoundException;
 192  
 }