Coverage Report - org.galagosearch.core.retrieval.structured.FeatureFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
FeatureFactory
80%
69/86
59%
34/58
0
 
 1  
 // BSD License (http://www.galagosearch.org/license)
 2  
 package org.galagosearch.core.retrieval.structured;
 3  
 
 4  
 import java.lang.reflect.Array;
 5  
 import java.lang.reflect.Constructor;
 6  
 import java.util.ArrayList;
 7  
 import java.util.HashMap;
 8  
 import java.util.List;
 9  
 import org.galagosearch.core.retrieval.query.Node;
 10  
 import org.galagosearch.core.retrieval.query.NodeType;
 11  
 import org.galagosearch.core.scoring.DirichletScorer;
 12  
 import org.galagosearch.tupleflow.Parameters;
 13  
 
 14  
 /**
 15  
  *
 16  
  * @author trevor
 17  
  */
 18  4
 public class FeatureFactory {
 19  4
     static String[][] sOperatorLookup = {
 20  
         {FilteredCombinationIterator.class.getName(), "filter"},
 21  
         {UnfilteredCombinationIterator.class.getName(), "combine"},
 22  
         {SynonymIterator.class.getName(), "syn"},
 23  
         {SynonymIterator.class.getName(), "synonym"},
 24  
         {ExtentInsideIterator.class.getName(), "inside"},
 25  
         {OrderedWindowIterator.class.getName(), "ordered"},
 26  
         {OrderedWindowIterator.class.getName(), "od"},
 27  
         {UnorderedWindowIterator.class.getName(), "unordered"},
 28  
         {UnorderedWindowIterator.class.getName(), "uw"},
 29  
         {ScaleIterator.class.getName(), "scale"}
 30  
     };
 31  4
     static String[][] sFeatureLookup = {
 32  
         {DirichletScorer.class.getName(), "dirichlet"}
 33  
     };
 34  
     HashMap<String, String> featureLookup;
 35  
     HashMap<String, String> operatorLookup;
 36  
     Parameters parameters;
 37  
 
 38  44
     public FeatureFactory(Parameters parameters) {
 39  44
         operatorLookup = new HashMap<String, String>();
 40  44
         featureLookup = new HashMap<String, String>();
 41  44
         this.parameters = parameters;
 42  
         
 43  88
         for (String[] item : sFeatureLookup) {
 44  44
             featureLookup.put(item[1], item[0]);
 45  
         }
 46  
 
 47  484
         for (String[] item : sOperatorLookup) {
 48  440
             operatorLookup.put(item[1], item[0]);
 49  
         }
 50  44
     }
 51  
 
 52  
     public String getClassName(Node node) throws Exception {
 53  28
         String operator = node.getOperator();
 54  
 
 55  28
         if (operator.equals("feature")) {
 56  8
             return getFeatureClassName(node.getParameters());
 57  
         }
 58  20
         String className = operatorLookup.get(operator);
 59  
 
 60  20
         if (className == null) {
 61  0
             throw new IllegalArgumentException(
 62  
                     "Unknown operator name: #" + operator);
 63  
         }
 64  20
         return className;
 65  
     }
 66  
 
 67  
     public String getFeatureClassName(Parameters parameters) throws Exception {
 68  12
         if (parameters.containsKey("class")) {
 69  0
             return parameters.get("class");
 70  
         }
 71  
 
 72  12
         String name = parameters.get("name", parameters.get("default", (String) null));
 73  
 
 74  12
         if (name == null) {
 75  0
             throw new Exception(
 76  
                     "Didn't find 'class', 'name', or 'default' parameter in this feature description.");
 77  
         }
 78  
 
 79  12
         String className = featureLookup.get(name);
 80  
 
 81  12
         if (className == null) {
 82  0
             throw new Exception("Couldn't find a class for the feature named " + name + ".");
 83  
         }
 84  
 
 85  12
         return className;
 86  
     }
 87  
 
 88  
     @SuppressWarnings("unchecked")
 89  
     public Class<StructuredIterator> getClass(Node node) throws Exception {
 90  24
         String className = getClassName(node);
 91  24
         Class c = Class.forName(className);
 92  
 
 93  24
         if (StructuredIterator.class.isAssignableFrom(c)) {
 94  24
             return (Class<StructuredIterator>) c;
 95  
         } else {
 96  0
             throw new Exception("Found a class, but it's not a DocumentDataIterator: " + className);
 97  
         }
 98  
     }
 99  
     
 100  
     public NodeType getNodeType(Node node) throws Exception {
 101  20
         return new NodeType(getClass(node));
 102  
     }
 103  
 
 104  
     boolean isUsableConstructor(
 105  
             Class[] types,
 106  
             ArrayList<StructuredIterator> childIterators) {
 107  
         // We require at least one parameter in a usable constructor.
 108  24
         if (types.length == 0)
 109  0
             return false;
 110  
             
 111  
         // The first parameter needs to be a parameters object.
 112  24
         if (!Parameters.class.isAssignableFrom(types[0]))
 113  0
             return false;
 114  
             
 115  
         // Only the last parameter can be an array
 116  48
         for (int i = 0; i < types.length-1; ++i) {
 117  24
             if (types[i].isArray())
 118  0
                 return false;
 119  
         }
 120  
         
 121  24
         boolean lastIsArray = types[types.length-1].isArray();
 122  
         
 123  
         // Does it have an appropriate number of arguments?
 124  24
         int iteratorArgs = 1 + childIterators.size();
 125  
         // If the last argument isn't an array, the argument list length
 126  
         // needs to match exactly.
 127  24
         if (iteratorArgs != types.length && !lastIsArray)
 128  0
             return false;
 129  
         // If the last argument is an array, we need to have enough arguments to
 130  
         // satisfy everything but the array parameter (since it's okay to put
 131  
         // zero things in an array).
 132  24
         if (lastIsArray && iteratorArgs < types.length-1)
 133  0
             return false;
 134  
         
 135  
         // We now know it has the right number of args, so we check
 136  
         // the iterator parameters to make sure they match.
 137  24
         int iteratorIndex = 0;
 138  
         int typeIndex;
 139  
         
 140  48
         for (typeIndex = 1; typeIndex < types.length; typeIndex++) {
 141  24
             Class currentType = types[typeIndex];
 142  24
             if (currentType.isArray()) {
 143  
                 // we'll check all the rest of the child iterators here
 144  0
                 for (; iteratorIndex < childIterators.size(); ++iteratorIndex) {
 145  0
                    StructuredIterator iterator = childIterators.get(iteratorIndex);
 146  0
                    if (!currentType.isAssignableFrom(iterator.getClass())) {
 147  0
                        return false;
 148  
                    }
 149  
                 }
 150  
             } else {
 151  24
                 if (iteratorIndex >= childIterators.size())
 152  0
                     return false;
 153  
 
 154  24
                 StructuredIterator iterator = childIterators.get(iteratorIndex);
 155  24
                 if (!currentType.isAssignableFrom(iterator.getClass()))
 156  0
                     return false;
 157  
             }
 158  
         }
 159  
 
 160  24
         return true;
 161  
     }
 162  
     
 163  
     Object[] argsForConstructor(Class[] types,
 164  
             Parameters parameters,
 165  
             ArrayList<StructuredIterator> childIterators) {
 166  24
         assert types.length > 0;
 167  
         
 168  24
         Object[] args = new Object[types.length];
 169  
         // The first argument is a parameters object.
 170  24
         args[0] = parameters;
 171  
         
 172  
         // The remaining arguments come from childIterators.
 173  44
         for (int i = 1; i < args.length; ++i) {
 174  20
             args[i] = childIterators.get(i-1);
 175  
         }
 176  
 
 177  
         // If the last argument is an array, we need to convert any remaining
 178  
         // childIterators to an array form.
 179  24
         Class lastType = types[types.length-1];
 180  24
         if (lastType.isArray()) {
 181  12
             List<StructuredIterator> remaining =
 182  
                 childIterators.subList(types.length-2, childIterators.size());
 183  12
             Object typedArray = Array.newInstance(lastType.getComponentType(), 0);
 184  12
             Object[] remainingArray = remaining.toArray((Object[]) typedArray);
 185  12
             args[args.length-1] = remainingArray;
 186  
         }
 187  
         
 188  24
         return args;
 189  
     }
 190  
     
 191  
     /**
 192  
      * Given a query node, generates the corresponding iterator object that can be used
 193  
      * for structured retrieval.  This method just calls getClass() on the node,
 194  
      * then instantiates the resulting class.
 195  
      * 
 196  
      * If the class returned by getClass() is a ScoringFunction, it must contain
 197  
      * a constructor that takes a single Parameters object.  If the class returned by 
 198  
      * getFeatureClass() is some kind of StructuredIterator (either a ScoreIterator,
 199  
      * ExtentIterator or CountIterator), it must take a Parameters object and an
 200  
      * ArrayList of DocumentDataIterators as parameters.
 201  
      */
 202  
     public StructuredIterator getIterator(Node node, ArrayList<StructuredIterator> childIterators) throws Exception {
 203  16
         NodeType type = getNodeType(node);
 204  
         
 205  16
         Constructor constructor = type.getConstructor();
 206  16
         Class[] types = type.getParameterTypes(1 + childIterators.size());
 207  
             
 208  16
         if (!isUsableConstructor(types, childIterators)) {
 209  0
             throw new Exception("Couldn't find a reasonable constructor.");
 210  
         }
 211  
         
 212  16
         Parameters parametersCopy = new Parameters();
 213  16
         parametersCopy.copy(node.getParameters());
 214  16
         Object[] args = argsForConstructor(constructor.getParameterTypes(),
 215  
                                            parametersCopy,
 216  
                                            childIterators);
 217  16
         RequiredStatistics required =
 218  
             type.getIteratorClass().getAnnotation(RequiredStatistics.class);
 219  16
         if (required != null) {
 220  16
             for (String statistic : required.statistics()) {
 221  8
                 parametersCopy.add(statistic, parameters.get(statistic, null));
 222  
             }
 223  
         }
 224  16
         return (StructuredIterator) constructor.newInstance(args);
 225  
     }
 226  
 }