001: /*
002: * Created on 24-Jul-2005
003: *
004:
005: */
006: package com.jofti.tree;
007:
008: import java.util.Map;
009: import java.util.Stack;
010:
011: import com.jofti.btree.ValueObject;
012: import com.jofti.core.INameSpaceAware;
013: import com.jofti.core.IParsedQuery;
014: import com.jofti.core.IPredicate;
015: import com.jofti.exception.JoftiException;
016: import com.jofti.parser.AttributeValueParser;
017: import com.jofti.parser.CommonLexerTokenTypes;
018: import com.jofti.parser.Operator;
019: import com.jofti.parser.StackPredicate;
020: import com.jofti.util.FastStack;
021: import com.jofti.util.ObjectProcedureAdapter;
022: import com.jofti.util.OpenHashMap;
023:
024: /**
025: * @author Steve Woodcock
026: *
027:
028: */
029: public abstract class AbstractMatchingEngine implements MatchingEngine {
030:
031: // move these to be parser constants at some point
032: protected static final int OR = CommonLexerTokenTypes.OR;
033: protected static final int AND = CommonLexerTokenTypes.AND;
034:
035: protected AttributeValueParser attributeValueParser = new AttributeValueParser();;
036:
037: protected Map addAllOpen(final OpenHashMap large,
038: final OpenHashMap smaller) {
039: // large.ensureCapacity(large.size()+smaller.size());
040: smaller.forEachPair(new ObjectProcedureAdapter() {
041: public boolean apply(Object key, Object value) {
042: if (!large.containsKey(key)) {
043: large.put(key, value);
044: }
045: return true;
046: }
047: });
048:
049: return large;
050: }
051:
052: protected Map retainAllOpen(final OpenHashMap large,
053: final OpenHashMap smaller) {
054:
055: smaller.forEachPair(new ObjectProcedureAdapter() {
056: public boolean apply(Object key, Object value) {
057: if (!large.containsKey(key)) {
058: smaller.remove(key);
059: }
060: return true;
061: }
062: });
063:
064: return smaller;
065: }
066:
067: protected Map andResults(Map map1, Map map2) {
068: if (map1.size() > map2.size()) {
069: return retainAllOpen((OpenHashMap) map1, (OpenHashMap) map2);
070: } else {
071: return retainAllOpen((OpenHashMap) map2, (OpenHashMap) map1);
072: }
073: }
074:
075: protected Map orResults(Map map1, Map map2) {
076: if (map1.size() > map2.size()) {
077: return addAllOpen((OpenHashMap) map1, (OpenHashMap) map2);
078: } else {
079: return addAllOpen((OpenHashMap) map2, (OpenHashMap) map1);
080: }
081: }
082:
083: // we use a hash join here as we assume the results all fit into main memory
084: protected Map resultJunction(Map map1, Operator op, Map map2) {
085:
086: switch (op.getOperator()) {
087: case OR:
088: if (map1.size() == 0) {
089: return map2;
090: } else if (map2.size() == 0) {
091: return map1;
092: }
093: return orResults(map1, map2);
094: case AND:
095: if ((map1.size() == 0 || map2.size() == 0)) {
096: return new OpenHashMap(1);
097: }
098: return andResults(map1, map2);
099: default:
100: return new OpenHashMap(1);
101: }
102:
103: }
104:
105: protected Map processPredicates(Stack predicates, Class className,
106: Object nameSpace, Map aliasMap, TreeIndex index,
107: Map namedParameters) throws JoftiException {
108:
109: Map firstResults = null;
110: Map returnResults = null;
111: IPredicate first = null;
112:
113: FastStack predicateStack = null;
114: if (predicates.size() == 1) {
115: first = (IPredicate) predicates.peek();
116: } else {
117: predicateStack = new FastStack();
118: predicateStack.addAll(predicates);
119: first = (IPredicate) predicateStack.pop();
120: }
121:
122: if (first.getType() == 1) {
123: firstResults = processPredicates(((StackPredicate) first)
124: .getPredicateStack(), className, nameSpace,
125: aliasMap, index, namedParameters);
126: } else {
127: firstResults = runPredicate(first, className, nameSpace,
128: aliasMap, index, namedParameters);
129: }
130:
131: if (predicateStack == null || predicateStack.size() == 0) {
132: returnResults = firstResults;
133: } else if (predicateStack.size() % 2 == 0
134: && predicateStack.size() != 0) {
135: returnResults = processRemainder(firstResults,
136: predicateStack, className, nameSpace, aliasMap,
137: index, namedParameters);
138: } else if (predicateStack.size() % 2 == 1) {
139: throw new JoftiException(
140: "we seem to have a hanging operator or predicate ");
141: }
142: // we do not care
143: return returnResults;
144:
145: }
146:
147: protected Map processQuery(IParsedQuery query, TreeIndex index)
148: throws JoftiException {
149:
150: // then get the predicates
151:
152: Stack predicates = query.getPredicates();
153:
154: Object nameSpace = null;
155: if (((INameSpaceAware) query).getNameSpace() != null) {
156: nameSpace = ((INameSpaceAware) query).getNameSpace();
157: }
158: // wif stack is empty then we need to do a range query for the whole entry
159:
160: if (predicates.isEmpty()) {
161:
162: // we need to get all results for the index and then merge them together as ors
163: // return processNoPredicates(query.getResultFieldsMap(), nameSpace, index);
164: throw new JoftiException(
165: "At least one predicate in where clause is required in Query");
166: }
167: // first one should always be a predicate
168: Map results = processPredicates(predicates, query
169: .getClassName(), nameSpace, query.getAliasMap(), index,
170: query.getNamedValueMap());
171:
172: return results;
173: }
174:
175: protected Map processRemainder(Map results, FastStack predicates,
176: Class className, Object nameSpace, Map aliasMap,
177: TreeIndex index, Map namedParameters) throws JoftiException {
178: while (predicates.size() % 2 == 0 && predicates.size() != 0) {
179: Operator op = (Operator) predicates.pop();
180: IPredicate second = (IPredicate) predicates.pop();
181: Map secondResults = null;
182: if (second.getType() == 1) {
183: secondResults = processPredicates(
184: ((StackPredicate) second).getPredicateStack(),
185: className, nameSpace, aliasMap, index,
186: namedParameters);
187: } else {
188: secondResults = runPredicate(second, className,
189: nameSpace, aliasMap, index, namedParameters);
190: }
191: results = resultJunction(results, op, secondResults);
192: }
193: return results;
194: }
195:
196: protected Map runPredicate(IPredicate pred, Class className,
197: Object nameSpace, Map aliasMap, TreeIndex index,
198: Map namedParameters) throws JoftiException {
199: Class name = null;
200: Object tempAlias = null;
201: if (pred.getAlias() != null) {
202: tempAlias = name = (Class) aliasMap.get(pred.getAlias());
203: } else {
204: name = className;
205: }
206: String attribute = null;
207: if (!(AttributeValueParser.VALUE.equalsIgnoreCase(pred
208: .getField()))
209: && (!index.getIntrospector().getPrimitiveClasses()
210: .contains(name.toString()))) {
211: attribute = pred.getField();
212: }
213:
214: try {
215:
216: return performQuery(pred.getOperator(), name, nameSpace,
217: attribute, pred.getValue(), index, namedParameters,
218: tempAlias);
219: } catch (Throwable t) {
220: if (t instanceof JoftiException) {
221: throw (JoftiException) t;
222: } else {
223: throw new JoftiException(t);
224: }
225: }
226: }
227:
228: // wraps null value
229: protected Comparable wrapNull(Comparable value) {
230: if (value == null) {
231: return ValueObject.MIN_COMAPARBLE_VALUE;
232: }
233: return value;
234: }
235:
236: /**
237: * @param pred
238: * @param index
239: * @param name
240: * @param attribute
241: * @return
242: * @throws JoftiException
243: */
244:
245: // construct a value from the supplied parameter map
246:
247: protected void testNull(Object obj) throws JoftiException {
248: if (obj == null) {
249: throw new JoftiException(
250: "value null not permitted in query");
251: }
252: }
253:
254: protected abstract Map performQuery(int operator, Class className,
255: Object nameSpace, String attribute, Object value,
256: TreeIndex index, Map namedParameters, Object alias)
257: throws JoftiException;
258:
259: }
|