1
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
108 if (types.length == 0)
109 return false;
110
111
112 if (!Parameters.class.isAssignableFrom(types[0]))
113 return false;
114
115
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
124 int iteratorArgs = 1 + childIterators.size();
125
126
127 if (iteratorArgs != types.length && !lastIsArray)
128 return false;
129
130
131
132 if (lastIsArray && iteratorArgs < types.length-1)
133 return false;
134
135
136
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
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
170 args[0] = parameters;
171
172
173 for (int i = 1; i < args.length; ++i) {
174 args[i] = childIterators.get(i-1);
175 }
176
177
178
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 }