001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.expressions;
038:
039: import java.util.*;
040: import java.io.*;
041: import oracle.toplink.essentials.exceptions.*;
042: import oracle.toplink.essentials.queryframework.*;
043: import oracle.toplink.essentials.internal.helper.*;
044: import oracle.toplink.essentials.internal.expressions.*;
045: import oracle.toplink.essentials.internal.sessions.AbstractRecord;
046: import oracle.toplink.essentials.internal.sessions.AbstractSession;
047: import oracle.toplink.essentials.descriptors.ClassDescriptor;
048:
049: /**
050: * <P>
051: * <B>Purpose</B>: Allow for instances of expression to be created. Expressions are Java object-level representations of SQL "where" clauses.
052: * The expressions attempt to mirror Java code as closely as possible.</p>
053: *
054: * <P>
055: *
056: * <B>Example</B>:
057: * <PRE><BLOCKQUOTE>
058: * ExpressionBuilder employee = new ExpressionBuilder();
059: * employee.get("firstName").equal("Bob").and(employee.get("lastName").equal("Smith"))
060: *
061: * >> equivalent Java code: (employee.getFirstName().equals("Bob")) && (employee.getLastName().equals("Smith"))
062: *
063: * >> equivalent SQL: (F_NAME = 'Bob') AND (L_NAME = 'Smith')
064: * </BLOCKQUOTE></PRE>
065: *
066: * @see Expression
067: */
068: public class ExpressionBuilder extends ObjectExpression {
069: protected transient AbstractSession session;
070: protected Class queryClass;
071: protected SQLSelectStatement statement;
072: protected DatabaseTable viewTable;
073: protected DatabaseTable aliasedViewTable;
074:
075: protected boolean wasQueryClassSetInternally = true;
076:
077: protected boolean wasAdditionJoinCriteriaUsed = false;
078:
079: /**
080: * PUBLIC:
081: * Create a new ExpressionBuilder.
082: */
083: public ExpressionBuilder() {
084: super ();
085: }
086:
087: /**
088: * ADVANCED:
089: * Create a new ExpressionBuilder representing instances of the argument class.
090: * This can be used for the purpose of parallel expressions.
091: * This is a type of query that searches on the relationship between to un-related objects.
092: */
093: public ExpressionBuilder(Class queryClass) {
094: super ();
095: this .queryClass = queryClass;
096: this .wasQueryClassSetInternally = false;
097: }
098:
099: /**
100: * INTERNAL: Find the alias for a given table. Handle the special case where we are bogus
101: * and it should be aliased against our derived tables instead.
102: */
103: public DatabaseTable aliasForTable(DatabaseTable table) {
104: if (hasViewTable()) {
105: return getAliasedViewTable();
106: }
107:
108: if (doesNotRepresentAnObjectInTheQuery()) {
109: for (Enumeration e = derivedTables.elements(); e
110: .hasMoreElements();) {
111: TableExpression t = (TableExpression) e.nextElement();
112: DatabaseTable result = t.aliasForTable(table);
113: if (result != null) {
114: return result;
115: }
116: }
117: } else {
118: return super .aliasForTable(table);
119: }
120: return null;// No alias found in the derived tables
121: }
122:
123: /**
124: * INTERNAL:
125: * Assign aliases to any tables which I own. Start with t<initialValue>,
126: * and return the new value of the counter , i.e. if initialValue is one
127: * and I have tables ADDRESS and EMPLOYEE I will assign them t1 and t2 respectively, and return 3.
128: */
129: public int assignTableAliasesStartingAt(int initialValue) {
130: if (hasBeenAliased()) {
131: return initialValue;
132: }
133:
134: if (doesNotRepresentAnObjectInTheQuery()) {
135: return initialValue;
136: }
137:
138: // This block should be removed I think.
139: // The only reason to clone might be to
140: // preserve the qualifier, but aliases need
141: // qualifiers? That seems strange.
142: // Also this will break AsOf queries. By
143: // inference if has view table the AliasTableLookup
144: // will contain one table, and that will be the
145: // table of the view...
146: if (hasViewTable()) {
147: DatabaseTable aliased = (DatabaseTable) viewTable.clone();
148: String alias = "t" + initialValue;
149: aliased.setName(alias);
150: assignAlias(alias, viewTable);
151: aliasedViewTable = aliased;
152: return initialValue + 1;
153: }
154: return super .assignTableAliasesStartingAt(initialValue);
155: }
156:
157: /**
158: * INTERNAL:
159: * Used for debug printing.
160: */
161: public String descriptionOfNodeType() {
162: return "Base";
163: }
164:
165: /**
166: * INTERNAL:
167: * There are cases (which we might want to eliminate?) where the expression builder
168: * doesn't actually correspond to an object to be read. Mostly this is the case where
169: * it's a data query in terms of tables, and the builder is only there to provide a base.
170: * It might be better to make tables able to serve as their own base, but it's very nice
171: * to have a known unique, shared base. In the meantime, this
172: * is a special case to make sure the builder doesn't get tables assigned.
173: */
174: public boolean doesNotRepresentAnObjectInTheQuery() {
175: return (hasDerivedTables() && !hasDerivedFields() && !hasDerivedExpressions());
176: }
177:
178: /**
179: * INTERNAL:
180: */
181: public DatabaseTable getAliasedViewTable() {
182: return aliasedViewTable;
183:
184: }
185:
186: /**
187: * INTERNAL:
188: * Return the expression builder which is the ultimate base of this expression, or
189: * null if there isn't one (shouldn't happen if we start from a root)
190: */
191: public ExpressionBuilder getBuilder() {
192: return this ;
193: }
194:
195: /**
196: * INTERNAL:
197: * Only usable after the session and class have been set. Return the
198: * descriptor for the class this node represents.
199: */
200: public ClassDescriptor getDescriptor() {
201: if (descriptor == null) {
202: if (getQueryClass() == null) {
203: return null;
204: } else {
205: if (getSession() == null) {
206: throw QueryException.noExpressionBuilderFound(this );
207: }
208: descriptor = getSession()
209: .getDescriptor(getQueryClass());
210: }
211: }
212: return descriptor;
213:
214: }
215:
216: /**
217: * INTERNAL:
218: */
219: public Class getQueryClass() {
220: return queryClass;
221: }
222:
223: /**
224: * INTERNAL:
225: */
226: public AbstractSession getSession() {
227: return session;
228: }
229:
230: /**
231: * INTERNAL:
232: * Return the statement that expression is for.
233: * This is used for the context in subselects.
234: */
235: public SQLSelectStatement getStatement() {
236: return statement;
237: }
238:
239: /**
240: * INTERNAL:
241: */
242: public DatabaseTable getViewTable() {
243: return viewTable;
244: }
245:
246: /**
247: * INTERNAL:
248: */
249: public boolean hasViewTable() {
250: return viewTable != null;
251: }
252:
253: /**
254: * INTERNAL:
255: */
256: public boolean isExpressionBuilder() {
257: return true;
258: }
259:
260: /**
261: * INTERNAL:
262: * Normalize the expression into a printable structure.
263: * Any joins must be added to form a new root.
264: */
265: public Expression normalize(ExpressionNormalizer normalizer) {
266: if (hasBeenNormalized()) {
267: return this ;
268: } else {
269: setHasBeenNormalized(true);
270: }
271:
272: // This is required for parralel selects,
273: // the session must be set and the addtional join expression added.
274: if (this .queryClass != null) {
275: Expression criteria = null;
276:
277: setSession(normalizer.getSession().getRootSession(null));
278: // The descriptor must be defined at this point.
279: if (getDescriptor() == null) {
280: throw QueryException.noExpressionBuilderFound(this );
281: }
282: if (!this .wasAdditionJoinCriteriaUsed) {
283: criteria = getDescriptor().getQueryManager()
284: .getAdditionalJoinExpression();
285: if (criteria != null) {
286: criteria = twist(criteria, this );
287: }
288: }
289:
290: if (isUsingOuterJoinForMultitableInheritance()
291: && getSession().getPlatform()
292: .shouldPrintOuterJoinInWhereClause()) {
293: Expression childrenCriteria = getDescriptor()
294: .getInheritancePolicy()
295: .getChildrenJoinExpression();
296: childrenCriteria = this .twist(childrenCriteria, this );
297: childrenCriteria.convertToUseOuterJoin();
298: if (criteria == null) {
299: criteria = childrenCriteria;
300: } else {
301: criteria = criteria.and(childrenCriteria);
302: }
303: }
304: if (isUsingOuterJoinForMultitableInheritance()
305: && (!getSession().getPlatform()
306: .shouldPrintOuterJoinInWhereClause())) {
307: normalizer.getStatement().getOuterJoinExpressions()
308: .addElement(null);
309: normalizer.getStatement()
310: .getOuterJoinedMappingCriteria().addElement(
311: null);
312: normalizer.getStatement()
313: .getOuterJoinedAdditionalJoinCriteria()
314: .addElement(additionalExpressionCriteriaMap());
315: normalizer.getStatement()
316: .getDescriptorsForMultitableInheritanceOnly()
317: .add(this .getDescriptor());
318: // fall through to the main case
319: }
320: normalizer.addAdditionalExpression(criteria);
321:
322: }
323: setStatement(normalizer.getStatement());
324:
325: return super .normalize(normalizer);
326: }
327:
328: /**
329: * INTERNAL:
330: * Print java
331: */
332: public void printJava(ExpressionJavaPrinter printer) {
333: printer.printString(printer.getBuilderString());
334: }
335:
336: /**
337: * INTERNAL:
338: * This expression is built on a different base than the one we want. Rebuild it and
339: * return the root of the new tree
340: * This assumes that the original expression has only a single builder.
341: */
342: public Expression rebuildOn(Expression newBase) {
343: return newBase;
344: }
345:
346: /**
347: * INTERNAL:
348: * Override Expression.registerIn to check if the new base expression
349: * has already been provided for the clone.
350: * @see oracle.toplink.essentials.expressions.Expression#cloneUsing(Expression)
351: * @bug 2637484 INVALID QUERY KEY EXCEPTION THROWN USING BATCH READS AND PARALLEL EXPRESSIONS
352: */
353: protected Expression registerIn(Dictionary alreadyDone) {
354: // Here do a special check to see if this a cloneUsing(newBase) call.
355: Object value = alreadyDone.get(alreadyDone);
356: if ((value == null) || (value == alreadyDone)) {
357: // This is a normal cloning operation.
358: return super .registerIn(alreadyDone);
359: }
360: ObjectExpression copy = (ObjectExpression) value;
361:
362: // copy is actually the newBase of a cloneUsing.
363: alreadyDone.put(alreadyDone, alreadyDone);
364: alreadyDone.put(this , copy);
365: // Now need to copy over the derived expressions, etc.
366: if (this .derivedExpressions != null) {
367: if (copy.derivedExpressions == null) {
368: copy.derivedExpressions = copyCollection(
369: this .derivedExpressions, alreadyDone);
370: } else {
371: copy.derivedExpressions.addAll(copyCollection(
372: this .derivedExpressions, alreadyDone));
373: }
374: }
375:
376: // Do the same for these protected fields.
377: copy.postCopyIn(alreadyDone, this .derivedFields,
378: this .derivedTables);
379: return copy;
380: }
381:
382: /**
383: * INTERNAL:
384: * Set the class which this node represents.
385: */
386: public void setQueryClass(Class queryClass) {
387: this .queryClass = queryClass;
388: this .descriptor = null;
389: }
390:
391: /**
392: * INTERNAL:
393: * Set the session in which we expect this expression to be translated.
394: */
395: public void setSession(AbstractSession session) {
396: this .session = session;
397: }
398:
399: /**
400: * INTERNAL:
401: * Set the statement that expression is for.
402: * This is used for the context in subselects.
403: */
404: public void setStatement(SQLSelectStatement statement) {
405: this .statement = statement;
406: }
407:
408: /**
409: * INTERNAL:
410: * This expression represents something read through a view table.
411: */
412: public void setViewTable(DatabaseTable theTable) {
413: viewTable = theTable;
414:
415: }
416:
417: /**
418: * INTERNAL:
419: * If the additional Join Criteria for the class this builder represents has
420: * been added to the statement then mark this as true. This will prevent
421: * TopLink from adding it again at normalization
422: */
423: public void setWasAdditionJoinCriteriaUsed(boolean joinCriteriaUsed) {
424: this .wasAdditionJoinCriteriaUsed = joinCriteriaUsed;
425: }
426:
427: /**
428: * INTERNAL:
429: * Rebuild myself against the base, with the values of parameters supplied by the context
430: * expression. This is used for transforming a standalone expression (e.g. the join criteria of a mapping)
431: * into part of some larger expression. You normally would not call this directly, instead calling twist
432: * See the comment there for more details"
433: * @param newBase
434: * @param context
435: * @return
436: */
437: public Expression twistedForBaseAndContext(Expression newBase,
438: Expression context) {
439: return newBase;
440: }
441:
442: /**
443: * INTERNAL:
444: * The expression builder represent the entire object, just return it.
445: */
446: public Object valueFromObject(Object object,
447: AbstractSession session, AbstractRecord translationRow,
448: InMemoryQueryIndirectionPolicy valueHolderPolicy,
449: boolean isObjectUnregistered) {
450: return object;
451: }
452:
453: /**
454: * INTERNAL:
455: * If the additional Join Criteria for the class this builder represents has
456: * been added to the statement this method will return true;
457: */
458: public boolean wasAdditionJoinCriteriaUsed() {
459: return this .wasAdditionJoinCriteriaUsed;
460: }
461:
462: /**
463: * INTERNAL:
464: * Returns true if TopLink set the query class as appoased to the customer. This
465: * is important in determining if this Expression should be treated as a parallel
466: * expression during normalization
467: */
468: public boolean wasQueryClassSetInternally() {
469: return this .wasQueryClassSetInternally;
470: }
471:
472: /**
473: * INTERNAL:
474: * For debug printing purposes.
475: */
476: public void writeDescriptionOn(BufferedWriter writer)
477: throws IOException {
478: String className;
479: if (getQueryClass() == null) {
480: className = "QUERY OBJECT";
481: } else {
482: className = getQueryClass().getName();
483: }
484: writer.write(className + tableAliasesDescription());
485: }
486: }
|