View Javadoc

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  public class FeatureFactory {
19      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      static String[][] sFeatureLookup = {
32          {DirichletScorer.class.getName(), "dirichlet"}
33      };
34      HashMap<String, String> featureLookup;
35      HashMap<String, String> operatorLookup;
36      Parameters parameters;
37  
38      public FeatureFactory(Parameters parameters) {
39          operatorLookup = new HashMap<String, String>();
40          featureLookup = new HashMap<String, String>();
41          this.parameters = parameters;
42          
43          for (String[] item : sFeatureLookup) {
44              featureLookup.put(item[1], item[0]);
45          }
46  
47          for (String[] item : sOperatorLookup) {
48              operatorLookup.put(item[1], item[0]);
49          }
50      }
51  
52      public String getClassName(Node node) throws Exception {
53          String operator = node.getOperator();
54  
55          if (operator.equals("feature")) {
56              return getFeatureClassName(node.getParameters());
57          }
58          String className = operatorLookup.get(operator);
59  
60          if (className == null) {
61              throw new IllegalArgumentException(
62                      "Unknown operator name: #" + operator);
63          }
64          return className;
65      }
66  
67      public String getFeatureClassName(Parameters parameters) throws Exception {
68          if (parameters.containsKey("class")) {
69              return parameters.get("class");
70          }
71  
72          String name = parameters.get("name", parameters.get("default", (String) null));
73  
74          if (name == null) {
75              throw new Exception(
76                      "Didn't find 'class', 'name', or 'default' parameter in this feature description.");
77          }
78  
79          String className = featureLookup.get(name);
80  
81          if (className == null) {
82              throw new Exception("Couldn't find a class for the feature named " + name + ".");
83          }
84  
85          return className;
86      }
87  
88      @SuppressWarnings("unchecked")
89      public Class<StructuredIterator> getClass(Node node) throws Exception {
90          String className = getClassName(node);
91          Class c = Class.forName(className);
92  
93          if (StructuredIterator.class.isAssignableFrom(c)) {
94              return (Class<StructuredIterator>) c;
95          } else {
96              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         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         if (types.length == 0)
109             return false;
110             
111         // The first parameter needs to be a parameters object.
112         if (!Parameters.class.isAssignableFrom(types[0]))
113             return false;
114             
115         // Only the last parameter can be an array
116         for (int i = 0; i < types.length-1; ++i) {
117             if (types[i].isArray())
118                 return false;
119         }
120         
121         boolean lastIsArray = types[types.length-1].isArray();
122         
123         // Does it have an appropriate number of arguments?
124         int iteratorArgs = 1 + childIterators.size();
125         // If the last argument isn't an array, the argument list length
126         // needs to match exactly.
127         if (iteratorArgs != types.length && !lastIsArray)
128             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         if (lastIsArray && iteratorArgs < types.length-1)
133             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         int iteratorIndex = 0;
138         int typeIndex;
139         
140         for (typeIndex = 1; typeIndex < types.length; typeIndex++) {
141             Class currentType = types[typeIndex];
142             if (currentType.isArray()) {
143                 // we'll check all the rest of the child iterators here
144                 for (; iteratorIndex < childIterators.size(); ++iteratorIndex) {
145                    StructuredIterator iterator = childIterators.get(iteratorIndex);
146                    if (!currentType.isAssignableFrom(iterator.getClass())) {
147                        return false;
148                    }
149                 }
150             } else {
151                 if (iteratorIndex >= childIterators.size())
152                     return false;
153 
154                 StructuredIterator iterator = childIterators.get(iteratorIndex);
155                 if (!currentType.isAssignableFrom(iterator.getClass()))
156                     return false;
157             }
158         }
159 
160         return true;
161     }
162     
163     Object[] argsForConstructor(Class[] types,
164             Parameters parameters,
165             ArrayList<StructuredIterator> childIterators) {
166         assert types.length > 0;
167         
168         Object[] args = new Object[types.length];
169         // The first argument is a parameters object.
170         args[0] = parameters;
171         
172         // The remaining arguments come from childIterators.
173         for (int i = 1; i < args.length; ++i) {
174             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         Class lastType = types[types.length-1];
180         if (lastType.isArray()) {
181             List<StructuredIterator> remaining =
182                 childIterators.subList(types.length-2, childIterators.size());
183             Object typedArray = Array.newInstance(lastType.getComponentType(), 0);
184             Object[] remainingArray = remaining.toArray((Object[]) typedArray);
185             args[args.length-1] = remainingArray;
186         }
187         
188         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         NodeType type = getNodeType(node);
204         
205         Constructor constructor = type.getConstructor();
206         Class[] types = type.getParameterTypes(1 + childIterators.size());
207             
208         if (!isUsableConstructor(types, childIterators)) {
209             throw new Exception("Couldn't find a reasonable constructor.");
210         }
211         
212         Parameters parametersCopy = new Parameters();
213         parametersCopy.copy(node.getParameters());
214         Object[] args = argsForConstructor(constructor.getParameterTypes(),
215                                            parametersCopy,
216                                            childIterators);
217         RequiredStatistics required =
218             type.getIteratorClass().getAnnotation(RequiredStatistics.class);
219         if (required != null) {
220             for (String statistic : required.statistics()) {
221                 parametersCopy.add(statistic, parameters.get(statistic, null));
222             }
223         }
224         return (StructuredIterator) constructor.newInstance(args);
225     }
226 }