001: /**
002: * Copyright (C) 2006 NetMind Consulting Bt.
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 3 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package hu.netmind.persistence.parser;
018:
019: import hu.netmind.persistence.*;
020: import java.util.*;
021: import org.apache.log4j.Logger;
022:
023: /**
024: * The parser resolver. Handles resolving objects and attributes
025: * to table names and such. This class also handles the symbol table.
026: * @author Brautigam Robert
027: * @version Revision: $Revision$
028: */
029: public class WhereResolver implements Resolver {
030: private static Logger logger = Logger
031: .getLogger(WhereResolver.class);
032:
033: private StoreContext context;
034: private HashMap symbolTable;
035: private Vector allTableTerms;
036: private Vector allLeftTableTerms;
037: // Main class stuff only used by 'find', when selecting single class
038: private TableTerm mainTerm;
039: private ClassInfo mainClassInfo;
040: private ClassSpecifier mainClassSpecifier;
041:
042: public WhereResolver(StoreContext context) {
043: this .context = context;
044: allLeftTableTerms = new Vector();
045: symbolTable = new HashMap();
046: mainTerm = null;
047: mainClassInfo = null;
048: }
049:
050: /**
051: * Resolve main table name.
052: */
053: public TableTerm resolve(ClassSpecifier specifier, boolean selected) {
054: String alias = specifier.getAlias() != null ? specifier
055: .getAlias() : specifier.getClassName();
056: // Insert symbol table entry
057: SymbolTableEntry entry = (SymbolTableEntry) symbolTable
058: .get(alias);
059: TableTerm tableTerm = null;
060: if (entry == null) {
061: // Determine class for specifier
062: ClassInfo classInfo = context.getClassTracker()
063: .getMatchingClassInfo(specifier.getClassName());
064: if (classInfo == null)
065: throw new ParserException(ParserException.SYMBOL_ERROR,
066: "could not find class for table name: "
067: + specifier.getClassName());
068: // Entry was null, create
069: entry = new SymbolTableEntry();
070: entry.alias = specifier.getAlias();
071: entry.tableName = classInfo.getTableName(classInfo
072: .getSourceEntry());
073: entry.automatic = false;
074: entry.expression = null;
075: entry.selected = true;
076: entry.classInfo = classInfo;
077: symbolTable.put(alias, entry);
078: if (specifier.getAlias() == null) {
079: // Insert default table name symbol too, for later left table
080: // term fixing, which will use the full table name
081: symbolTable.put(entry.tableName, entry);
082: }
083: logger.debug("adding table to symbol table: "
084: + entry.tableName + "(" + alias + ")");
085: // Construct table term
086: tableTerm = new TableTerm(classInfo.getTableName(classInfo
087: .getSourceEntry()), specifier.getAlias());
088: // Remember main table
089: if (selected) {
090: logger.debug("table entry " + entry.tableName + "("
091: + alias + ") is selected, adding left tables.");
092: // Remember
093: mainClassSpecifier = specifier;
094: mainClassInfo = classInfo;
095: mainTerm = tableTerm; // Main term is the selected class/table
096: // Fix entry to contain all relevant classes
097: fixLeftTableTerms(entry);
098: tableTerm.setLeftTableTerms(entry.leftTerms);
099: }
100: } else {
101: // Construct table term
102: tableTerm = new TableTerm(entry.tableName, entry.alias);
103: }
104: // Return with table term
105: logger.debug("translated: " + specifier + " -> " + tableTerm);
106: return tableTerm;
107: }
108:
109: private ReferenceTerm getReferenceTerm(SymbolTableEntry entry) {
110: switch (entry.type) {
111: case SymbolTableEntry.TYPE_HANDLED:
112: return new ReferenceTerm(entry.tableName, entry.alias,
113: entry.referenceColumn);
114: case SymbolTableEntry.TYPE_OBJECT:
115: return new ReferenceTerm(entry.tableName, entry.alias,
116: "persistence_id");
117: default:
118: return null;
119: }
120: }
121:
122: /**
123: * Resolver attribute chain.
124: */
125: public ReferenceTerm resolve(ClassSpecifier specifier,
126: List attributeSpecifiers, boolean selected) {
127: // Resolve class specifier
128: // This is not as easy as it sounds: if there are no attribute
129: // specifiers and there is no alias given, it may mean that
130: // this identifier represents an attribute to selected main class
131: // (if there is one).
132: if (((attributeSpecifiers == null) || (attributeSpecifiers
133: .size() == 0))
134: && (specifier.getAlias() == null)
135: && (mainClassInfo != null)) {
136: String attributeName = specifier.getClassName();
137: if (mainClassInfo.getAttributeType(attributeName) != null) {
138: // Simulate a normal entry
139: specifier = mainClassSpecifier;
140: attributeSpecifiers = new Vector();
141: attributeSpecifiers.add(new AttributeSpecifier(
142: attributeName, null, null));
143: }
144: }
145: // Now go through names, the root name does already exist,
146: // because that is the class. All other names are
147: // combined with attributes (book.title). This also should
148: // be inserted into symbol table, if it is a class.
149: resolve(specifier, selected); // Resolve class
150: StringBuffer alias = new StringBuffer(
151: specifier.getAlias() == null ? specifier.getClassName()
152: : specifier.getAlias());
153: SymbolTableEntry previousEntry = (SymbolTableEntry) symbolTable
154: .get(alias.toString());
155: ClassInfo previousInfo = previousEntry.classInfo;
156: AttributeSpecifier primitiveAttributeSpecifier = null;
157: for (int i = 0; (attributeSpecifiers != null)
158: && (i < attributeSpecifiers.size()); i++) {
159: AttributeSpecifier spec = (AttributeSpecifier) attributeSpecifiers
160: .get(i);
161: String attributeName = spec.getIdentifier();
162: Class attributeClass = previousInfo
163: .getAttributeType(attributeName);
164: if (attributeClass == null)
165: throw new ParserException(ParserException.ABORT,
166: "can not resolve the identifier '"
167: + attributeName + "' on '"
168: + alias.toString() + "', classinfo: "
169: + previousInfo);
170: alias.append("." + attributeName);
171: logger.debug("processing attribute: " + alias);
172: int attributeType = context.getClassTracker().getType(
173: attributeClass);
174: // Handle additional array specifier.
175: if ((spec.getKeyname() != null)
176: && (attributeType != ClassTracker.TYPE_HANDLED))
177: throw new ParserException(ParserException.ABORT,
178: "additional array specifier found, but type is not 'handled': "
179: + alias);
180: if (attributeType == ClassTracker.TYPE_HANDLED) {
181: logger
182: .debug("attribute '"
183: + alias
184: + "' found to be a handled type, letting the handler in.");
185: // If the array specifier is given, add to alias
186: if (spec.getKeyname() != null)
187: alias.append("['" + spec.getKeyname() + "']");
188: // Calculate the symbol table entry for this part
189: SymbolTableEntry entry = (SymbolTableEntry) symbolTable
190: .get(alias.toString());
191: if (entry == null) {
192: entry = context.getTypeHandlerTracker().getHandler(
193: attributeClass).getSymbolEntry(spec,
194: previousEntry, previousInfo,
195: getReferenceTerm(previousEntry));
196: if (entry.alias != null) {
197: // Entry wants to be in the symbol table
198: symbolTable.put(entry.alias, entry);
199: } else {
200: // Entry does no want into the symbol table,
201: // so create an extremal symbol.
202: int num = 0;
203: while (symbolTable.containsKey(alias.toString()
204: + "-notused" + num))
205: num++;
206: symbolTable.put(alias.toString() + "-notused"
207: + num, entry);
208: }
209: }
210: previousEntry = entry;
211: // If this is not the last specifier, then calculate the
212: // class info for the next specifier.
213: if (i + 1 < attributeSpecifiers.size())
214: previousInfo = context.getTypeHandlerTracker()
215: .getHandler(attributeClass).getSymbolInfo(
216: entry, spec);
217: }
218: // Reserved type
219: if (attributeType == ClassTracker.TYPE_RESERVED)
220: throw new ParserException(ParserException.ABORT,
221: "attribute type is reserved, can not handle it: "
222: + alias.toString());
223: // Attribute is an object
224: if (attributeType == ClassTracker.TYPE_OBJECT) {
225: logger.debug("attribute '" + alias
226: + "' found to be an object.");
227: ClassInfo containerInfo = previousInfo;
228: if (spec.getClassName() != null)
229: previousInfo = context.getClassTracker()
230: .getMatchingClassInfo(spec.getClassName());
231: else
232: previousInfo = context
233: .getClassTracker()
234: .getClassInfo(
235: previousInfo
236: .getAttributeType(attributeName),
237: null);
238: // Get entry
239: SymbolTableEntry entry = (SymbolTableEntry) symbolTable
240: .get(alias.toString());
241: if (entry == null) {
242: // Create entry
243: entry = new SymbolTableEntry();
244: entry.alias = null;
245: entry.tableName = previousInfo
246: .getTableName(previousInfo.getSourceEntry());
247: entry.automatic = true;
248: entry.type = SymbolTableEntry.TYPE_OBJECT;
249: entry.classInfo = previousInfo;
250: symbolTable.put(alias.toString(), entry);
251: // Create expression
252: Expression expr = new Expression();
253: // Determine whether the entry's tableName is correct, or if
254: // a superclass's table is required for attribute
255: TableTerm previousTerm = new TableTerm(
256: previousEntry.tableName,
257: previousEntry.alias);
258: ClassEntry super ClassEntry = containerInfo
259: .getAttributeClassEntry(attributeName);
260: if (logger.isDebugEnabled())
261: logger.debug("selecting object attribute "
262: + attributeName
263: + ", class: "
264: + previousEntry.classInfo
265: .getSourceEntry()
266: + ", superclass: " + super ClassEntry);
267: if (!previousEntry.classInfo.getSourceEntry()
268: .equals(super ClassEntry)) {
269: // Class holding the attribute differs from entry's class.
270: // Note: if a superclass is in charge, there should be no
271: // dynamic name.
272: ClassInfo super Info = context.getClassTracker()
273: .getClassInfo(super ClassEntry);
274: String super TableName = super Info
275: .getTableName(super ClassEntry);
276: previousTerm = new TableTerm(super TableName,
277: null);
278: if (logger.isDebugEnabled())
279: logger
280: .debug("determining whether to allocate supertable: "
281: + super TableName
282: + ", tables allocated: "
283: + previousEntry.allocatedSuperTables);
284: if (!previousEntry.allocatedSuperTables
285: .contains(super TableName)) {
286: logger.debug("allocating superclass: "
287: + super ClassEntry);
288: // This superclass was not yet allocated
289: ReferenceTerm connectLeftTerm = new ReferenceTerm(
290: previousEntry.tableName,
291: previousEntry.alias,
292: "persistence_id");
293: previousEntry.termList.add(connectLeftTerm);
294: expr.add(connectLeftTerm);
295: expr.add("=");
296: ReferenceTerm connectRightTerm = new ReferenceTerm(
297: previousTerm, "persistence_id");
298: previousEntry.termList
299: .add(connectRightTerm);
300: expr.add(connectRightTerm);
301: expr.add("and");
302: // Add to list
303: previousEntry.allocatedSuperTables
304: .add(super TableName);
305: }
306: }
307: // Rest of expression
308: ReferenceTerm leftTerm = new ReferenceTerm(
309: previousTerm, attributeName);
310: previousEntry.termList.add(leftTerm);
311: expr.add(leftTerm);
312: expr.add("=");
313: ReferenceTerm refTerm = getReferenceTerm(entry);
314: expr.add(refTerm);
315: entry.termList.add(refTerm);
316: entry.expression = expr;
317: }
318: previousEntry = entry;
319: }
320: // Attribute is primitive type
321: if (attributeType == ClassTracker.TYPE_PRIMITIVE) {
322: logger.debug("attribute '" + alias
323: + "' found to be a primitive attribute.");
324: // A primitive type should be the last in the list
325: if (i + 1 < attributeSpecifiers.size())
326: throw new ParserException(ParserException.ABORT,
327: "A primitive type encountered, but not the last in list: "
328: + alias.toString());
329: // Mark primitive type
330: primitiveAttributeSpecifier = spec;
331: }
332: }
333: // If we're here, that means, attribute list has ended.
334: // Either in a primitive type attribute or in object.
335: SymbolTableEntry entry = (SymbolTableEntry) symbolTable
336: .get(alias.toString());
337: if (entry == null) {
338: // This means we are in an attribute, so get previous entry
339: entry = previousEntry;
340: }
341: // Construct and return term
342: ReferenceTerm result = null;
343: if (primitiveAttributeSpecifier != null) {
344: // Attribute specified, this means, we must check whether
345: // this attribute is part of the class in entry or in superclass.
346: TableTerm tableTerm = new TableTerm(entry.tableName,
347: entry.alias, entry.leftTerms);
348: ClassEntry super ClassEntry = entry.classInfo
349: .getAttributeClassEntry(primitiveAttributeSpecifier
350: .getIdentifier());
351: if (logger.isDebugEnabled())
352: logger.debug("selecting primitive attribute "
353: + primitiveAttributeSpecifier.getIdentifier()
354: + ", class: "
355: + entry.classInfo.getSourceEntry()
356: + ", superclass: " + super ClassEntry);
357: if (!entry.classInfo.getSourceEntry().equals(
358: super ClassEntry)) {
359: // Class holding the attribute differs from entry's class
360: ClassInfo super Info = context.getClassTracker()
361: .getClassInfo(super ClassEntry);
362: String super TableName = super Info
363: .getTableName(super ClassEntry);
364: tableTerm = new TableTerm(super TableName, null);
365: if (!entry.allocatedSuperTables
366: .contains(super TableName)) {
367: logger
368: .debug("primitive attribute is a related class' attribute, and not yet allocated.");
369: Expression expr = entry.expression;
370: if (expr == null) {
371: expr = new Expression();
372: entry.expression = expr;
373: }
374: if (expr.size() > 0)
375: expr.add("and");
376: // This superclass was not yet allocated
377: ReferenceTerm connectLeftTerm = new ReferenceTerm(
378: entry.tableName, entry.alias,
379: "persistence_id");
380: entry.termList.add(connectLeftTerm);
381: expr.add(connectLeftTerm);
382: expr.add("=");
383: ReferenceTerm connectRightTerm = new ReferenceTerm(
384: tableTerm, "persistence_id");
385: entry.termList.add(connectRightTerm);
386: expr.add(connectRightTerm);
387: // Add to list
388: entry.allocatedSuperTables.add(super TableName);
389: }
390: }
391: // Construct result
392: String attributeName = primitiveAttributeSpecifier
393: .getIdentifier();
394: boolean id = false;
395: if ("persistenceid".equalsIgnoreCase(attributeName)) {
396: attributeName = "persistence_id";
397: id = true;
398: }
399: result = new ReferenceTerm(tableTerm, attributeName);
400: if (id) // Mark specifically for id reference
401: result.setId();
402: } else {
403: // Term did not end in attribute specification, so leave
404: // persistence id, which is available in all tables
405: result = getReferenceTerm(entry);
406: }
407: entry.termList.add(result);
408: return result; // Return term
409: }
410:
411: /**
412: * Fix a primitive expression. Called from parser!
413: * The problem is expressions like this: find holder where holder.attr = 'Ni'.
414: * If the holder.attr is an object type, and not declared 'primitive', then
415: * the parser can not know it is meant to be primitive only when it
416: * comes to the right term which is primitive. So in this case, we
417: * must alter the expression to include the primitive type's table.
418: */
419: public void fixPrimitiveExpression(Expression expr) {
420: // Fix is only necessary if
421: // - left term is a reference term
422: // - left term does not point to a primitive type
423: // - right term is a constant term
424: // - right term is a primitive type (and not id)
425: Object rawLeftTerm = expr.get(0);
426: Object rawRightTerm = expr.get(2);
427: if (!(rawLeftTerm instanceof ReferenceTerm))
428: return;
429: if (!(rawRightTerm instanceof ConstantTerm))
430: return;
431: ReferenceTerm leftTerm = (ReferenceTerm) rawLeftTerm;
432: ConstantTerm rightTerm = (ConstantTerm) rawRightTerm;
433: if (context.getClassTracker().getType(
434: rightTerm.getValue().getClass()) != ClassTracker.TYPE_PRIMITIVE)
435: return; // Not primitive
436: if (rightTerm.isId() || leftTerm.isId())
437: return; // Is an Id expression specifically
438: ClassInfo leftInfo = context.getClassTracker()
439: .getTableClassInfo(leftTerm.getTableName());
440: if ((leftInfo != null)
441: && (context.getClassTracker().getType(
442: leftInfo.getAttributeType(leftTerm
443: .getColumnName())) == ClassTracker.TYPE_PRIMITIVE))
444: return; // Attribute is primitive
445: if (logger.isDebugEnabled())
446: logger.debug("fixing primitive expression: " + expr);
447: // Now insert expression referencing the primitive type's table
448: // Note: We must insert a symbol table entry for the primitive type's
449: // table, but it's not a real symbol, it should have an extremal
450: // name.
451: ClassInfo primitiveInfo = context.getClassTracker()
452: .getClassInfo(rightTerm.getValue().getClass(),
453: rightTerm.getValue());
454: String primitiveTableName = primitiveInfo
455: .getTableName(primitiveInfo.getSourceEntry());
456: TableTerm primitiveTableTerm = new TableTerm(
457: primitiveTableName, null);
458: SymbolTableEntry entry = new SymbolTableEntry();
459: entry.alias = null;
460: entry.tableName = primitiveTableName;
461: entry.automatic = true;
462: entry.type = SymbolTableEntry.TYPE_OBJECT;
463: symbolTable.put("primitive" + symbolTable.size(), entry);
464: // Create contact expression to primitive table
465: Expression contactExpr = new Expression();
466: contactExpr.add(leftTerm);
467: contactExpr.add("=");
468: ReferenceTerm contactTerm = new ReferenceTerm(
469: primitiveTableTerm, "persistence_id");
470: contactExpr.add(contactTerm);
471: entry.termList.add(contactTerm);
472: entry.expression = contactExpr;
473: // Alter original expression
474: ReferenceTerm valueTerm = new ReferenceTerm(primitiveTableTerm,
475: "value");
476: entry.termList.add(valueTerm);
477: expr.set(0, valueTerm); // Replace original left term
478: }
479:
480: private boolean fixContainsNegated(Expression expr,
481: boolean isNegated) {
482: if (expr == null)
483: return false;
484: boolean originalNegated = isNegated;
485: isNegated = false;
486: for (int i = 0; i < expr.size(); i++) {
487: Object obj = expr.get(i);
488: if (obj instanceof String) {
489: if ("contains".equalsIgnoreCase((String) obj)) {
490: if (isNegated ^ originalNegated)
491: return true;
492: expr.set(i, "="); // Replace with '=' sign
493: } else
494: isNegated = "not".equalsIgnoreCase((String) obj);
495: }
496: if ((obj instanceof Expression)
497: && (fixContainsNegated((Expression) obj, isNegated
498: ^ originalNegated)))
499: return true;
500: }
501: return false;
502: }
503:
504: /**
505: * Fix unaliased generated table term.
506: */
507: private Expression fixAutomaticTerms(Expression expr) {
508: // First, generate all temporary names
509: allTableTerms = new Vector(); // Fill all table terms vector
510: Expression plusExpression = new Expression();
511: String namePrefix = "t_";
512: int tempNameIndex = 1;
513: // Entries will be filtered through a set, because a single
514: // entry could be present in the map multiple times (for example
515: // a main table is available as an alias, and as a table name entry)
516: Iterator iterator = new HashSet(symbolTable.values())
517: .iterator();
518: while (iterator.hasNext()) {
519: SymbolTableEntry entry = (SymbolTableEntry) iterator.next();
520: // Generate index for class and all superclasses!
521: HashMap tableNameAliases = new HashMap();
522: Vector tableNames = new Vector(entry.allocatedSuperTables);
523: if (entry.automatic)
524: tableNames.add(entry.tableName);
525: else
526: allTableTerms.add(new TableTerm(entry.tableName,
527: entry.alias));
528: for (int i = 0; i < tableNames.size(); i++) {
529: while (symbolTable.get(namePrefix + tempNameIndex) != null)
530: tempNameIndex++;
531: tableNameAliases.put(tableNames.get(i), namePrefix
532: + tempNameIndex);
533: allTableTerms.add(new TableTerm((String) tableNames
534: .get(i), namePrefix + tempNameIndex));
535: tempNameIndex++;
536: }
537: // Walk through referred terms and fill in the gaps
538: for (int i = 0; i < entry.termList.size(); i++) {
539: TableTerm term = (TableTerm) entry.termList.get(i);
540: String newAlias = (String) tableNameAliases.get(term
541: .getTableName());
542: if (newAlias != null)
543: term.setAlias(newAlias);
544: }
545: // Remember it's expression
546: if (entry.expression != null) {
547: if (logger.isDebugEnabled())
548: logger.debug("entry '" + entry.alias
549: + "' has connector expression: "
550: + entry.expression);
551: if (plusExpression.size() > 0)
552: plusExpression.add("and");
553: plusExpression.addAll(entry.expression);
554: }
555: }
556: // Now create final expression
557: Expression result = expr;
558: if (plusExpression.size() != 0) {
559: if (result.size() != 0) {
560: result = new Expression();
561: result.add(plusExpression);
562: result.add("and");
563: result.add(expr);
564: } else {
565: result = plusExpression;
566: }
567: }
568: // Return
569: return result;
570: }
571:
572: /**
573: * All all left table terms to term.
574: */
575: private void fixLeftTableTerms(ClassInfo info, TableTerm term,
576: List leftTerms) {
577: // Calculate left table terms
578: Set relatedClassEntries = new HashSet(context.getClassTracker()
579: .getRelatedClassEntries(info.getSourceEntry()));
580: for (int i = 0; i < leftTerms.size(); i++) {
581: // Go through left term, if this is a left term to the info, then
582: // insert it to this term's left terms
583: TableTerm leftTerm = (TableTerm) leftTerms.get(i);
584: ClassEntry leftEntry = context.getClassTracker()
585: .getTableClassInfo(leftTerm.getTableName())
586: .getSourceEntry();
587: if (relatedClassEntries.contains(leftEntry))
588: term.getLeftTableTerms().add(leftTerm);
589: }
590: }
591:
592: /**
593: * Get the left table terms, which will contained in the select.
594: */
595: private void fixLeftTableTerms(SymbolTableEntry mainEntry) {
596: // Calculate left table terms
597: List relatedClassEntries = context.getClassTracker()
598: .getRelatedClassEntries(
599: mainEntry.classInfo.getSourceEntry());
600: if (logger.isDebugEnabled())
601: logger.debug("found related classes: "
602: + relatedClassEntries + ", to class info: "
603: + mainEntry.classInfo);
604: for (int i = 0; i < relatedClassEntries.size(); i++) {
605: ClassEntry relatedClassEntry = (ClassEntry) relatedClassEntries
606: .get(i);
607: ClassInfo relatedClassInfo = context.getClassTracker()
608: .getClassInfo(relatedClassEntry);
609: if (logger.isDebugEnabled())
610: logger.debug("found left joined class: "
611: + relatedClassEntry + ", class info: "
612: + relatedClassInfo);
613: if (relatedClassInfo == null)
614: throw new ParserException(ParserException.ABORT,
615: "object class not found for loading: '"
616: + relatedClassEntry + "'");
617: // Create the term and add to mainterm
618: TableTerm leftTerm = new TableTerm(relatedClassInfo
619: .getTableName(relatedClassEntry), null);
620: if (logger.isDebugEnabled())
621: logger.debug("adding left term to: "
622: + mainEntry.tableName + "(" + mainEntry.alias
623: + "), left term: " + leftTerm);
624: allLeftTableTerms.add(leftTerm);
625: mainEntry.termList.add(leftTerm);
626: mainEntry.leftTerms.add(leftTerm);
627: mainEntry.allocatedSuperTables.add(leftTerm.getTableName());
628: }
629: }
630:
631: /**
632: * Add date constraint expressions.
633: */
634: private Expression fixDateConstraints(List localAllTableTerms,
635: List localAllLeftTableTerms, Expression expr,
636: TimeControl timeControl) {
637: Expression dateExpression = new Expression();
638: // Generate date constraints for all tables
639: for (int i = 0; i < localAllTableTerms.size(); i++) {
640: TableTerm term = (TableTerm) localAllTableTerms.get(i);
641: // Add to date constraints
642: if (dateExpression.size() != 0)
643: dateExpression.add("and");
644: if (localAllLeftTableTerms.contains(term)) {
645: // Left table
646: timeControl.applyToLeftTable(dateExpression, term);
647: } else {
648: // Not a left table, so no null values possible
649: timeControl.apply(dateExpression, term);
650: }
651: }
652: // Return
653: Expression result = expr;
654: if (dateExpression.size() != 0) {
655: if ((result != null) && (result.size() != 0)) {
656: result = new Expression();
657: result.add(dateExpression);
658: result.add("and");
659: result.add(expr);
660: } else {
661: result = dateExpression;
662: }
663: }
664: // Return
665: return result;
666: }
667:
668: private boolean validViewSelectTables(QueryStatement stmt) {
669: if (stmt.getMode() == QueryStatement.MODE_FIND)
670: return true; // Valid, because it is in find mode
671: // Check whether all selected terms are storable
672: for (int i = 0; i < stmt.getSelectTerms().size(); i++) {
673: TableTerm term = (TableTerm) stmt.getSelectTerms().get(i);
674: ClassEntry entry = context.getClassTracker()
675: .getTableClassInfo(term.getTableName())
676: .getSourceEntry();
677: if (!entry.isStorable())
678: return false;
679: }
680: return true;
681: }
682:
683: private void fixNonstorableTerms(List selectTerms, Expression expr) {
684: // Go through all terms, and those which are not storable and
685: // not selected need to be substituted with ids table.
686: if (expr == null)
687: return;
688: for (int i = 0; i < expr.size(); i++) {
689: Object value = expr.get(i);
690: if (value instanceof Expression) {
691: fixNonstorableTerms(selectTerms, (Expression) value);
692: } else if (value instanceof TableTerm) {
693: // Found a table term
694: TableTerm term = (TableTerm) value;
695: ClassInfo info = context.getClassTracker()
696: .getTableClassInfo(term.getTableName());
697: if (info == null)
698: continue; // No worries, most likely an internal table
699: ClassEntry entry = info.getSourceEntry();
700: if ((!selectTerms.contains(term))
701: && (!entry.isStorable())) {
702: // A non-storable entry's term found which is not selected,
703: // so substitute
704: allTableTerms.remove(term); // Remove from time control
705: term.setTableName("persistence_object_ids"); // Alter to ids
706: }
707: }
708: }
709: }
710:
711: private void fixOrderBys(QueryStatement stmt) {
712: if (stmt.getOrderByList() == null)
713: stmt.setOrderByList(new Vector());
714: if (stmt.getMode() == QueryStatement.MODE_FIND) {
715: // Find mode: add default order by on persistence_id
716: TableTerm mainTerm = (TableTerm) stmt.getSelectTerms().get(
717: 0);
718: OrderBy orderBy = new OrderBy(new ReferenceTerm(mainTerm,
719: "persistence_id"), OrderBy.ASCENDING);
720: if (!stmt.getOrderByList().contains(orderBy))
721: stmt.getOrderByList().add(orderBy);
722: } else {
723: // View mode: add all view attributes as order bys, so
724: // listing becomes unambigous.
725: for (int i = 0; i < stmt.getSelectTerms().size(); i++) {
726: ReferenceTerm term = (ReferenceTerm) stmt
727: .getSelectTerms().get(i);
728: OrderBy orderBy = new OrderBy(term, OrderBy.ASCENDING);
729: if (!stmt.getOrderByList().contains(orderBy))
730: stmt.getOrderByList().add(orderBy);
731: }
732: }
733: }
734:
735: /**
736: * Generate the final statements.
737: */
738: public QueryStatementList generate(QueryStatement stmt) {
739: Expression expr = stmt.getQueryExpression();
740: if (expr == null)
741: expr = new Expression();
742: if (logger.isDebugEnabled()) {
743: logger.debug("symbol table before finalizing: "
744: + symbolTable);
745: logger.debug("expression before finalizing: " + expr);
746: }
747: // Add default order bys
748: fixOrderBys(stmt);
749: // Check whether 'contains' operator is negated (this is not allowed)
750: // if it does not, then substitute with '=' sign.
751: if (fixContainsNegated(expr, false))
752: throw new ParserException(
753: ParserException.ABORT,
754: "'contains' operator is negated, this may not mean what you think it means, so it's disallowed.");
755: // If this is a view select, check whether all selected terms
756: // are storable.
757: if (!validViewSelectTables(stmt))
758: throw new ParserException(
759: ParserException.ABORT,
760: "view selects can not contain non-storable terms (ie. interface types and abstract objects as selected terms).");
761: // Generate left table terms for selected tables
762: stmt.setAllLeftTableTerms(allLeftTableTerms);
763: // Generate aliases and connector expressions
764: stmt.setQueryExpression(fixAutomaticTerms(expr));
765: // Fix all non-storable terms that are used in the expression but
766: // not selected.
767: fixNonstorableTerms(stmt.getSelectTerms(), stmt
768: .getQueryExpression());
769: // Now generate all the root queries for this query.
770: // View selects can not contain non-storable terms, and find
771: // selects can only contain 1 main term, so get the roots for
772: // this main term, and iterate over it's roots.
773: QueryStatementList stmts = new QueryStatementList();
774: TableTerm selectTerm = (TableTerm) stmt.getSelectTerms().get(0);
775: ClassEntry selectEntry = context.getClassTracker()
776: .getTableClassInfo(selectTerm.getTableName())
777: .getSourceEntry();
778: List roots = context.getClassTracker()
779: .getStorableRootClassEntries(selectEntry);
780: // Prepare local lists, these will be used later
781: Vector localAllTableTermsCore = new Vector(allTableTerms);
782: Vector localAllLeftTableTermsCore = new Vector(
783: allLeftTableTerms);
784: localAllTableTermsCore.remove(selectTerm);
785: localAllTableTermsCore
786: .removeAll(selectTerm.getLeftTableTerms());
787: localAllLeftTableTermsCore.removeAll(selectTerm
788: .getLeftTableTerms());
789: if (logger.isDebugEnabled())
790: logger.debug("determined roots for " + selectEntry + ": "
791: + roots);
792: // Now generate the statements themselves
793: for (int r = 0; r < roots.size(); r++) {
794: // Clone statement
795: QueryStatement subStmt = stmt.deepCopy();
796: Vector localAllTableTerms = new Vector(
797: localAllTableTermsCore);
798: Vector localAllLeftTableTerms = new Vector(
799: localAllLeftTableTermsCore);
800: // The counter determines with which root to replace select term with
801: ClassEntry rootEntry = (ClassEntry) roots.get(r);
802: if (logger.isDebugEnabled())
803: logger.debug("entry " + selectEntry
804: + " is to be replaced with root entry: "
805: + rootEntry);
806: ClassInfo rootInfo = context.getClassTracker()
807: .getClassInfo(rootEntry);
808: TableTerm rootTerm = selectTerm.deepCopy();
809: rootTerm.setTableName(rootInfo.getTableName(rootEntry));
810: rootTerm.getLeftTableTerms().clear();
811: fixLeftTableTerms(rootInfo, rootTerm, selectTerm
812: .getLeftTableTerms()); // Add left table terms
813: if (logger.isDebugEnabled())
814: logger.debug("entry " + selectEntry + " (" + selectTerm
815: + ") is replaced with root entry: " + rootEntry
816: + " (" + rootTerm + ")");
817: // Do replace operation in statement. This will replace
818: // the term everywhere.
819: subStmt.replace(selectTerm, rootTerm);
820: // All tables to the local all tables list for later date fix
821: localAllTableTerms.add(rootTerm);
822: localAllTableTerms.addAll(rootTerm.getLeftTableTerms());
823: localAllLeftTableTerms.addAll(rootTerm.getLeftTableTerms());
824: subStmt.setAllLeftTableTerms(localAllLeftTableTerms);
825: // Generate static representation (it is important, that this is
826: // before date constraints are added
827: subStmt.setStaticRepresentation(subStmt.getMode()
828: + " "
829: + subStmt.getSelectTerms().toString()
830: + " "
831: + (subStmt.getQueryExpression() != null ? subStmt
832: .getQueryExpression().toString() : "")
833: + (subStmt.getOrderByList() != null ? subStmt
834: .getOrderByList().toString() : ""));
835: // Generate date constraints
836: subStmt.setQueryExpression(fixDateConstraints(
837: localAllTableTerms, localAllLeftTableTerms, subStmt
838: .getQueryExpression(), subStmt
839: .getTimeControl()));
840: // Debug
841: if (logger.isDebugEnabled())
842: logger.debug("generated statement, selected: "
843: + subStmt.getSelectTerms() + ", expression: "
844: + subStmt.getQueryExpression() + ", order by: "
845: + subStmt.getOrderByList());
846: // Insert complete query into result query list
847: stmts.add(subStmt);
848: }
849: // Now generate the all ids statement. This is a statement which provides
850: // all ids of all selected objects in one statement.
851: if (stmts.size() == 1) {
852: // Only one statement, so the ids are provided by that
853: stmts.setAllIdsStatement((QueryStatement) stmts.get(0));
854: } else {
855: // Multiple statements. This is only possible in 'find' mode, so
856: // there is a single term, which needs to be substituted with the
857: // 'ids' table. This is possible, since the table in question is
858: // non-storable, that means it has got no attributes.
859: QueryStatement subStmt = stmt.deepCopy();
860: if (logger.isDebugEnabled())
861: logger.debug("entry " + selectEntry
862: + " is to be replaced with ids table.");
863: TableTerm rootTerm = new TableTerm(
864: "persistence_object_ids", selectTerm.getAlias());
865: // Do replace operation in statement. This will replace
866: // the term everywhere.
867: subStmt.replace(selectTerm, rootTerm);
868: // We do not want date constraints on ids table, so the core
869: // table lists are ok.
870: subStmt.setAllLeftTableTerms(localAllLeftTableTermsCore);
871: // Generate static representation (it is important, that this is
872: // before date constraints are added
873: subStmt.setStaticRepresentation(subStmt.getMode()
874: + " "
875: + subStmt.getSelectTerms().toString()
876: + " "
877: + (subStmt.getQueryExpression() != null ? subStmt
878: .getQueryExpression().toString() : "")
879: + (subStmt.getOrderByList() != null ? subStmt
880: .getOrderByList().toString() : ""));
881: // Generate date constraints
882: subStmt.setQueryExpression(fixDateConstraints(
883: localAllTableTermsCore, localAllLeftTableTermsCore,
884: subStmt.getQueryExpression(), subStmt
885: .getTimeControl()));
886: // Debug
887: if (logger.isDebugEnabled())
888: logger
889: .debug("generated 'all ids' statement, selected: "
890: + subStmt.getSelectTerms()
891: + ", expression: "
892: + subStmt.getQueryExpression()
893: + ", order by: "
894: + subStmt.getOrderByList());
895: // Set all ids select term
896: stmts.setAllIdsStatement(subStmt);
897: }
898: return stmts;
899: }
900:
901: public static class SymbolTableEntry {
902: public static final int TYPE_HANDLED = 1;
903: public static final int TYPE_OBJECT = 3;
904: public static final int TYPE_PRIMITIVE = 4;
905:
906: public String alias; // Alias of table entry
907: public String tableName; // The table name
908: public String referenceColumn;
909: // Used by handled types, definies the
910: // reference column, if sub-specified.
911: public boolean selected; // Whether this entry will be selected
912: public List termList; // List of terms in which it could not
913: // be resolved yet
914: public List leftTerms; // Left table terms for entry.
915: public List allocatedSuperTables;
916: // The list of tables of superclasses
917: // already allocated (used)
918: public ClassInfo classInfo;// Class info if symbol is a table
919: public boolean automatic; // Whether symbol should be automatically
920: // generated
921: public Expression expression;
922: // The expression this entry generated
923: public int type = TYPE_OBJECT;
924:
925: public SymbolTableEntry() {
926: termList = new LinkedList();
927: allocatedSuperTables = new LinkedList();
928: leftTerms = new LinkedList();
929: }
930:
931: public String toString() {
932: return "[Symbol entry: " + tableName + " (" + alias + "), "
933: + type + ":" + automatic + "]";
934: }
935: }
936: }
|