2009/04/15 - Apache HiveMind has been retired.

For more information, please explore the Attic.

Clover coverage report - Code Coverage for hivemind-lib release 1.2.1
Coverage timestamp: Fri Feb 10 2006 16:34:07 PST
file stats: LOC: 228   Methods: 5
NCLOC: 99   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
StrategyRegistryImpl.java 90% 95.1% 100% 93.8%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.hivemind.lib.util;
 16   
 17    import java.util.HashMap;
 18    import java.util.Iterator;
 19    import java.util.LinkedList;
 20    import java.util.Map;
 21    import java.util.WeakHashMap;
 22   
 23    import org.apache.hivemind.service.ClassFabUtils;
 24    import org.apache.hivemind.util.Defense;
 25   
 26    /**
 27    * Thread-safe implementation of {@link org.apache.hivemind.lib.util.StrategyRegistry}.
 28    *
 29    * @author Howard Lewis Ship
 30    * @since 1.1
 31    */
 32   
 33    public class StrategyRegistryImpl implements StrategyRegistry
 34    {
 35    /**
 36    * A Map of adaptor objects, keyed on registration Class.
 37    */
 38   
 39    private Map _registrations = new HashMap();
 40   
 41    /**
 42    * A Map of adaptor objects, keyed on subject Class.
 43    */
 44   
 45    private Map _cache = new WeakHashMap();
 46   
 47  104 public synchronized void register(Class registrationClass, Object adaptor)
 48    {
 49  104 Defense.notNull(registrationClass, "registrationClass");
 50  104 Defense.notNull(adaptor, "adaptor");
 51   
 52  104 if (_registrations.containsKey(registrationClass))
 53  1 throw new IllegalArgumentException(UtilMessages
 54    .duplicateRegistration(registrationClass));
 55   
 56  103 _registrations.put(registrationClass, adaptor);
 57   
 58    // Can't tell what is and isn't valid in the cache.
 59    // Also, normally all registrations occur before any adaptors
 60    // are searched for, so this is not a big deal.
 61   
 62  103 _cache.clear();
 63    }
 64   
 65  13 public synchronized Object getStrategy(Class subjectClass)
 66    {
 67  13 Defense.notNull(subjectClass, "subjectClass");
 68   
 69  13 Object result = _cache.get(subjectClass);
 70   
 71  13 if (result != null)
 72  0 return result;
 73   
 74  13 result = searchForAdaptor(subjectClass);
 75   
 76    // Record the result in the cache
 77   
 78  12 _cache.put(subjectClass, result);
 79   
 80  12 return result;
 81    }
 82   
 83    /**
 84    * Searches the registration Map for a match, based on inheritance.
 85    * <p>
 86    * Searches class inheritance first, then interfaces (in a rather vague order). Really should
 87    * match the order from the JVM spec.
 88    * <p>
 89    * There's a degenerate case where we may check the same interface more than once:
 90    * <ul>
 91    * <li>Two interfaces, I1 and I2
 92    * <li>Two classes, C1 and C2
 93    * <li>I2 extends I1
 94    * <li>C2 extends C1
 95    * <li>C1 implements I1
 96    * <li>C2 implements I2
 97    * <li>The search will be: C2, C1, I2, I1, I1
 98    * <li>I1 is searched twice, because C1 implements it, and I2 extends it
 99    * <li>There are other such cases, but none of them cause infinite loops and most are rare (we
 100    * could guard against it, but its relatively expensive).
 101    * <li>Multiple checks only occur if we don't find a registration
 102    * </ul>
 103    * <p>
 104    * This method is only called from a synchronized block, so it is implicitly synchronized.
 105    */
 106   
 107  13 private Object searchForAdaptor(Class subjectClass)
 108    {
 109  13 LinkedList queue = null;
 110  13 Object result = null;
 111   
 112    // Step one: work up through the class inheritance.
 113   
 114  13 Class searchClass = subjectClass;
 115   
 116    // Primitive types have null, not Object, as their parent
 117    // class.
 118   
 119  13 while (searchClass != Object.class && searchClass != null)
 120    {
 121  17 result = _registrations.get(searchClass);
 122  17 if (result != null)
 123  6 return result;
 124   
 125    // Not an exact match. If the search class
 126    // implements any interfaces, add them to the queue.
 127   
 128  11 Class[] interfaces = searchClass.getInterfaces();
 129  11 int length = interfaces.length;
 130   
 131  11 if (queue == null && length > 0)
 132  6 queue = new LinkedList();
 133   
 134  11 for (int i = 0; i < length; i++)
 135  11 queue.addLast(interfaces[i]);
 136   
 137    // Advance up to the next superclass
 138   
 139  11 searchClass = getSuperclass(searchClass);
 140   
 141    }
 142   
 143    // Ok, the easy part failed, lets start searching
 144    // interfaces.
 145   
 146  7 if (queue != null)
 147    {
 148  5 while (!queue.isEmpty())
 149    {
 150  8 searchClass = (Class) queue.removeFirst();
 151   
 152  8 result = _registrations.get(searchClass);
 153  8 if (result != null)
 154  2 return result;
 155   
 156    // Interfaces can extend other interfaces; add them
 157    // to the queue.
 158   
 159  6 Class[] interfaces = searchClass.getInterfaces();
 160  6 int length = interfaces.length;
 161   
 162  6 for (int i = 0; i < length; i++)
 163  0 queue.addLast(interfaces[i]);
 164    }
 165    }
 166   
 167    // Not a match on interface; our last gasp is to check
 168    // for a registration for java.lang.Object
 169   
 170  5 result = _registrations.get(Object.class);
 171  5 if (result != null)
 172  4 return result;
 173   
 174    // No match? That's rare ... and an error.
 175   
 176  1 throw new IllegalArgumentException(UtilMessages.strategyNotFound(subjectClass));
 177    }
 178   
 179    /**
 180    * Returns the superclass of the given class, with a single tweak: If the search class is an
 181    * array class, and the component type is an object class (but not Object), then the simple
 182    * Object array class is returned. This reflects the fact that an array of any class may be
 183    * assignable to <code>Object[]</code>, even though the superclass of an array is always
 184    * simply <code>Object</code>.
 185    */
 186   
 187  11 private Class getSuperclass(Class searchClass)
 188    {
 189  11 if (searchClass.isArray())
 190    {
 191  2 Class componentType = searchClass.getComponentType();
 192   
 193  2 if (!componentType.isPrimitive() && componentType != Object.class)
 194  1 return Object[].class;
 195    }
 196   
 197  10 return searchClass.getSuperclass();
 198    }
 199   
 200  1 public synchronized String toString()
 201    {
 202  1 StringBuffer buffer = new StringBuffer();
 203  1 buffer.append("AdaptorRegistry[");
 204   
 205  1 Iterator i = _registrations.entrySet().iterator();
 206  1 boolean showSep = false;
 207   
 208  1 while (i.hasNext())
 209    {
 210  1 if (showSep)
 211  0 buffer.append(' ');
 212   
 213  1 Map.Entry entry = (Map.Entry) i.next();
 214   
 215  1 Class registeredClass = (Class) entry.getKey();
 216   
 217  1 buffer.append(ClassFabUtils.getJavaClassName(registeredClass));
 218  1 buffer.append("=");
 219  1 buffer.append(entry.getValue());
 220   
 221  1 showSep = true;
 222    }
 223   
 224  1 buffer.append("]");
 225   
 226  1 return buffer.toString();
 227    }
 228    }