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.internal.parsing;
038:
039: // Java stuff
040: import java.util.*;
041:
042: // TopLink stuff
043: import oracle.toplink.essentials.exceptions.EJBQLException;
044: import oracle.toplink.essentials.expressions.*;
045: import oracle.toplink.essentials.queryframework.*;
046: import oracle.toplink.essentials.internal.localization.*;
047: import oracle.toplink.essentials.internal.sessions.AbstractSession;
048:
049: /**
050: * INTERNAL
051: * <p><b>Purpose</b>: A ParseTree contains Node(s). This contains a root Node and provides traversal utilities.
052: * <p><b>Responsibilities</b>:<ul>
053: * <li> Add parameters to the query
054: * <li> Generate an expression for the query
055: * <li> Answer true if the tree has parameters
056: * <li> Maintain the primary class name for the query
057: * <li> Maintain the root of the parse tree
058: * <li> Maintain the context for the parse tree
059: * <li> Maintainthe distinct state for the parse tree
060: * <li> Print the contents of the parse tree on a string
061: * </ul>
062: * @author Jon Driscoll and Joel Lucuik
063: * @since TopLink 4.0
064: */
065: public class ParseTree {
066: private ParseTreeContext context;
067: private QueryNode queryNode;
068: private FromNode fromNode;
069: private SetNode setNode;
070: private WhereNode whereNode;
071: private OrderByNode orderByNode = null;
072: private GroupByNode groupByNode = null;
073: private HavingNode havingNode = null;
074: private ClassLoader classLoader = null;
075: private short distinctState = ObjectLevelReadQuery.UNCOMPUTED_DISTINCT;
076: private boolean validated = false;
077: private Set unusedVariables = null;
078:
079: /**
080: * Return a new ParseTree.
081: */
082: public ParseTree() {
083: super ();
084: }
085:
086: /**
087: * INTERNAL
088: * Returns a DatabaseQuery instance for this ParseTree.
089: */
090: public DatabaseQuery createDatabaseQuery() {
091: return (queryNode == null) ? null : queryNode
092: .createDatabaseQuery(context);
093: }
094:
095: /**
096: * INTERNAL
097: * Adjust the reference class of the passed query if necessary
098: *
099: * Need to test this for Employee, employee.getAddress(), report query
100: */
101: public void adjustReferenceClassForQuery(DatabaseQuery theQuery,
102: GenerationContext generationContext) {
103: Class referenceClass = getReferenceClass(theQuery,
104: generationContext);
105: if ((referenceClass != null)
106: && (referenceClass != theQuery.getReferenceClass())) {
107: if (theQuery.isObjectLevelReadQuery()) {
108: // The referenceClass needs to be changed.
109: // This should only happen in an ejbSelect...
110: ((ObjectLevelReadQuery) theQuery)
111: .setReferenceClass(referenceClass);
112: generationContext.setBaseQueryClass(referenceClass);
113: ((ObjectLevelReadQuery) theQuery)
114: .changeDescriptor(generationContext
115: .getSession());
116: } else if (theQuery.isUpdateAllQuery()) {
117: ((UpdateAllQuery) theQuery)
118: .setReferenceClass(referenceClass);
119: } else if (theQuery.isDeleteAllQuery()) {
120: ((DeleteAllQuery) theQuery)
121: .setReferenceClass(referenceClass);
122: }
123: }
124: }
125:
126: /**
127: * INTERNAL
128: * Initialize the base expression in the generation context.
129: */
130: public void initBaseExpression(ObjectLevelReadQuery theQuery,
131: GenerationContext generationContext) {
132: String variable = getFromNode().getFirstVariable();
133: ParseTreeContext context = generationContext
134: .getParseTreeContext();
135: if (context.isRangeVariable(variable)) {
136: Class referenceClass = theQuery.getReferenceClass();
137: // Create a new expression builder for the reference class
138: ExpressionBuilder builder = new ExpressionBuilder(
139: referenceClass);
140: // Use the expression builder as the default expression builder for the query
141: theQuery.setExpressionBuilder(builder);
142: // Add the expression builder to the expression cache in the context
143: generationContext.setBaseExpression(variable, builder);
144: } else {
145: // Get the declaring node for the variable
146: Node path = context.pathForVariable(variable);
147: // Get the ExpressionBuilder of the range variable for the path
148: Class baseClass = getBaseExpressionClass(path,
149: generationContext);
150: // Use the base ExpressionBuilder as the default for the query
151: theQuery.setExpressionBuilder(new ExpressionBuilder(
152: baseClass));
153: // and change the reference class accordingly
154: theQuery.setReferenceClass(baseClass);
155: theQuery.changeDescriptor(generationContext.getSession());
156: generationContext.setBaseQueryClass(baseClass);
157: // Set the node expression as base expression
158: generationContext.setBaseExpression(variable, path
159: .generateExpression(generationContext));
160: }
161: }
162:
163: /**
164: * INTERNAL
165: * Initialize the base expression in the generation context.
166: */
167: public void initBaseExpression(ModifyAllQuery theQuery,
168: GenerationContext generationContext) {
169: ModifyNode queryNode = (ModifyNode) getQueryNode();
170: String variable = queryNode
171: .getCanonicalAbstractSchemaIdentifier();
172: Class referenceClass = theQuery.getReferenceClass();
173: // Create a new expression builder for the reference class
174: ExpressionBuilder builder = new ExpressionBuilder(
175: referenceClass);
176: // Use the expression builder as the default expression builder for the query
177: theQuery.setExpressionBuilder(builder);
178: // Add the expression builder to the expression cache in the context
179: generationContext.setBaseExpression(variable, builder);
180: }
181:
182: /** */
183: private Class getBaseExpressionClass(Node node,
184: GenerationContext generationContext) {
185: ParseTreeContext context = generationContext
186: .getParseTreeContext();
187: Class clazz = null;
188: if (node == null) {
189: clazz = null;
190: } else if (node.isDotNode()) {
191: // DotNode: delegate to left
192: clazz = getBaseExpressionClass(node.getLeft(),
193: generationContext);
194: } else if (node.isVariableNode()) {
195: // VariableNode
196: String variable = ((VariableNode) node).getVariableName();
197: if (!context.isRangeVariable(variable)) {
198: Node path = context.pathForVariable(variable);
199: // Variable is defined in JOIN/IN clause =>
200: // return the Class from its definition
201: clazz = getBaseExpressionClass(path, generationContext);
202: } else {
203: // Variable is defined in range variable decl =>
204: // return its class
205: String schema = context.schemaForVariable(variable);
206: if (schema != null) {
207: clazz = context.classForSchemaName(schema,
208: generationContext);
209: }
210: }
211: }
212: return clazz;
213: }
214:
215: /**
216: * INTERNAL
217: * Validate the parse tree.
218: */
219: protected void validate(AbstractSession session,
220: ClassLoader classLoader) {
221: validate(new TypeHelperImpl(session, classLoader));
222: }
223:
224: /**
225: * INTERNAL
226: * Validate the parse tree.
227: */
228: public void validate(TypeHelper typeHelper) {
229: ParseTreeContext context = getContext();
230: context.setTypeHelper(typeHelper);
231: validate(context);
232: }
233:
234: /**
235: * INTERNAL
236: * Validate the parse tree.
237: */
238: public void validate(ParseTreeContext context) {
239: if (validated) {
240: // already validated => return
241: return;
242: }
243:
244: validated = true;
245: context.enterScope();
246: if (fromNode != null) {
247: fromNode.validate(context);
248: }
249: queryNode.validate(context);
250: qualifyAttributeAccess(context);
251: if (setNode != null) {
252: setNode.validate(context);
253: }
254: if (whereNode != null) {
255: whereNode.validate(context);
256: }
257: if (hasOrderBy()) {
258: orderByNode.validate(context, (SelectNode) queryNode);
259: }
260: if (hasGroupBy()) {
261: groupByNode.validate(context, (SelectNode) queryNode);
262: }
263: if (hasHaving()) {
264: havingNode.validate(context, groupByNode);
265: }
266: // store the set od unused variable for later use
267: unusedVariables = context.getUnusedVariables();
268: context.leaveScope();
269: }
270:
271: /**
272: * INTERNAL
273: * This method handles any unqualified field access in bulk UPDATE and
274: * DELETE statements. A UPDATE or DELETE statement may not define an
275: * identification variable. In this case any field accessed from the
276: * current class is not qualified with an identification variable, e.g.
277: * UPDATE Customer SET name = :newname
278: * The method goes through the expressions of the SET clause and the WHERE
279: * clause of such an DELETE and UPDATE statement and qualifies the field
280: * access using the abstract schema name as qualifier.
281: */
282: protected void qualifyAttributeAccess(ParseTreeContext context) {
283: if ((queryNode.isUpdateNode() || queryNode.isDeleteNode())
284: && ((ModifyNode) queryNode)
285: .getAbstractSchemaIdentifier() == null) {
286: if (setNode != null) {
287: setNode.qualifyAttributeAccess(context);
288: }
289: if (whereNode != null) {
290: whereNode.qualifyAttributeAccess(context);
291: }
292: }
293: }
294:
295: /**
296: * INTERNAL
297: * Add the ordering to the passed query
298: */
299: public void addOrderingToQuery(ObjectLevelReadQuery theQuery,
300: GenerationContext generationContext) {
301: if (hasOrderBy()) {
302: ((OrderByNode) getOrderByNode()).addOrderingToQuery(
303: theQuery, generationContext);
304: }
305: }
306:
307: /**
308: * INTERNAL
309: * Add the grouping to the passed query
310: */
311: public void addGroupingToQuery(ObjectLevelReadQuery theQuery,
312: GenerationContext generationContext) {
313: if (hasGroupBy()) {
314: ((GroupByNode) getGroupByNode()).addGroupingToQuery(
315: theQuery, generationContext);
316: }
317: }
318:
319: /**
320: * INTERNAL
321: * Add the having to the passed query
322: */
323: public void addHavingToQuery(ObjectLevelReadQuery theQuery,
324: GenerationContext generationContext) {
325: if (hasHaving()) {
326: ((HavingNode) getHavingNode()).addHavingToQuery(theQuery,
327: generationContext);
328: }
329: }
330:
331: /**
332: * INTERNAL
333: */
334: public void addNonFetchJoinAttributes(
335: ObjectLevelReadQuery theQuery,
336: GenerationContext generationContext) {
337: ParseTreeContext context = generationContext
338: .getParseTreeContext();
339: for (Iterator i = unusedVariables.iterator(); i.hasNext();) {
340: String variable = (String) i.next();
341: Expression expr = null;
342: if (!context.isRangeVariable(variable)) {
343: Node path = context.pathForVariable(variable);
344: expr = path.generateExpression(generationContext);
345: theQuery.addNonFetchJoinedAttribute(expr);
346: } else {
347: // unused range variable => not supported yet
348: throw EJBQLException
349: .notYetImplemented(
350: context.getQueryInfo(),
351: "Variable ["
352: + variable
353: + "] is defined in a range variable declaration, but not used in the rest of the query.");
354: }
355: }
356: }
357:
358: /**
359: * INTERNAL
360: * Add the updates to the passed query
361: */
362: public void addUpdatesToQuery(UpdateAllQuery theQuery,
363: GenerationContext generationContext) {
364: if (getSetNode() != null) {
365: ((SetNode) getSetNode()).addUpdatesToQuery(theQuery,
366: generationContext);
367: }
368: }
369:
370: /**
371: * INTERNAL
372: * Add parameters to the query
373: */
374: public void addParametersToQuery(DatabaseQuery query) {
375: //Bug#4646580 Add arguments to query
376: if (context.hasParameters()) {
377: TypeHelper typeHelper = context.getTypeHelper();
378: for (Iterator i = context.getParameterNames().iterator(); i
379: .hasNext();) {
380: String param = (String) i.next();
381: Object type = context.getParameterType(param);
382: Class clazz = typeHelper.getJavaClass(type);
383: if (clazz == null) {
384: clazz = Object.class;
385: }
386: query.addArgument(param, clazz);
387: }
388: }
389: }
390:
391: /**
392: * INTERNAL
393: * Apply the select or update to the passed query.
394: * If there is a single attribute being selected, add it to the query result set
395: * If an aggregate is being used, add it to the query result set
396: */
397: public void applyQueryNodeToQuery(DatabaseQuery theQuery,
398: GenerationContext generationContext) {
399: getQueryNode().applyToQuery(theQuery, generationContext);
400: }
401:
402: /**
403: * INTERNAL
404: * Build the context to be used when generating the expression from the parse tree
405: */
406: public GenerationContext buildContext(DatabaseQuery query,
407: AbstractSession sessionForContext) {
408: if (query.isObjectLevelReadQuery()) {
409: return buildContextForReadQuery(sessionForContext);
410: } else if (query.isUpdateAllQuery() || query.isDeleteAllQuery()) {
411: return new GenerationContext(getContext(),
412: sessionForContext, this );
413: }
414: return null;
415: }
416:
417: /**
418: * INTERNAL
419: * Build the context to be used when generating the expression from the parse tree
420: */
421: public GenerationContext buildContextForReadQuery(
422: AbstractSession sessionForContext) {
423: return new SelectGenerationContext(getContext(),
424: sessionForContext, this );
425: }
426:
427: /**
428: * INTERNAL
429: * Build a context for the expression generation
430: */
431: public Expression generateExpression(DatabaseQuery readQuery,
432: GenerationContext generationContext) {
433: Expression selectExpression = getQueryNode()
434: .generateExpression(generationContext);
435: if (getWhereNode() == null) {
436: return selectExpression;
437: }
438: Expression whereExpression = getWhereNode().generateExpression(
439: generationContext);
440:
441: selectExpression = getQueryNode().generateExpression(
442: generationContext);
443: if (selectExpression != null) {
444: whereExpression = selectExpression.and(whereExpression);
445: }
446: return whereExpression;
447: }
448:
449: /**
450: * Return the context for this parse tree
451: */
452: public ParseTreeContext getContext() {
453: return context;
454: }
455:
456: /**
457: * INTERNAL
458: * Return the FROM Node
459: */
460: public FromNode getFromNode() {
461: return fromNode;
462: }
463:
464: /**
465: * INTERNAL
466: * Return a class loader
467: * @return java.lang.ClassLoader
468: */
469: public ClassLoader getClassLoader() {
470: if (classLoader == null) {
471: return oracle.toplink.essentials.internal.helper.ConversionManager
472: .getDefaultManager().getLoader();
473: } else {
474: return classLoader;
475: }
476: }
477:
478: /**
479: * INTERNAL
480: * Return the OrderByNode
481: */
482: public OrderByNode getOrderByNode() {
483: return orderByNode;
484: }
485:
486: /**
487: * INTERNAL
488: * Return the GroupByNode
489: */
490: public GroupByNode getGroupByNode() {
491: return groupByNode;
492: }
493:
494: /**
495: * INTERNAL
496: * Return the HavingNode
497: */
498: public HavingNode getHavingNode() {
499: return havingNode;
500: }
501:
502: /**
503: * getReferenceClass(): Answer the class which will be the reference class for the query.
504: * Resolve this using the node parsed from the "SELECT" of the EJBQL query string
505: */
506: public Class getReferenceClass(DatabaseQuery query,
507: GenerationContext generationContext) {
508: if (getQueryNode() == null) {
509: return null;
510: }
511: return getQueryNode().getReferenceClass(generationContext);
512: }
513:
514: /**
515: * INTERNAL
516: * Return the root node for the tree
517: */
518: public QueryNode getQueryNode() {
519: return queryNode;
520: }
521:
522: /**
523: * INTERNAL
524: * Return the set node for the tree
525: */
526: public SetNode getSetNode() {
527: return setNode;
528: }
529:
530: /**
531: * INTERNAL
532: * Return the Where node
533: */
534: public WhereNode getWhereNode() {
535: return whereNode;
536: }
537:
538: /**
539: * INTERNAL
540: * Return the DISTINCT state for the tree
541: */
542: public short getDistinctState() {
543: return distinctState;
544: }
545:
546: /**
547: * INTERNAL
548: * Does this EJBQL have an Ordering Clause
549: */
550: public boolean hasOrderBy() {
551: return getOrderByNode() != null;
552: }
553:
554: /**
555: * INTERNAL
556: * Does this EJBQL have a Grouping Clause
557: */
558: public boolean hasGroupBy() {
559: return getGroupByNode() != null;
560: }
561:
562: /**
563: * INTERNAL
564: * Does this EJBQL have a Having Clause
565: */
566: public boolean hasHaving() {
567: return getHavingNode() != null;
568: }
569:
570: /**
571: * INTERNAL:
572: * Set the class loader for this parse tree
573: * @param loader
574: */
575: public void setClassLoader(ClassLoader loader) {
576: this .classLoader = loader;
577: }
578:
579: /**
580: * INTERNAL
581: * Set the context for this parse tree
582: */
583: public void setContext(ParseTreeContext newContext) {
584: context = newContext;
585: }
586:
587: /**
588: * INTERNAL
589: * Set the FROM node for the query
590: */
591: public void setFromNode(FromNode fromNode) {
592: this .fromNode = fromNode;
593: }
594:
595: /**
596: * INTERNAL
597: * Set the Order by node
598: */
599: public void setOrderByNode(OrderByNode newOrderByNode) {
600: orderByNode = newOrderByNode;
601: }
602:
603: /**
604: * INTERNAL
605: * Set the Group by node
606: */
607: public void setGroupByNode(GroupByNode newGroupByNode) {
608: groupByNode = newGroupByNode;
609: }
610:
611: /**
612: * INTERNAL
613: * Set the Having node
614: */
615: public void setHavingNode(HavingNode newHavingNode) {
616: havingNode = newHavingNode;
617: }
618:
619: public void setSelectionCriteriaForQuery(DatabaseQuery theQuery,
620: GenerationContext generationContext) {
621: theQuery.setSelectionCriteria(generateExpression(theQuery,
622: generationContext));
623: }
624:
625: /**
626: * INTERNAL
627: * Set the Select node
628: */
629: public void setQueryNode(QueryNode newQueryNode) {
630: queryNode = newQueryNode;
631: }
632:
633: /**
634: * INTERNAL
635: * Set the Where node
636: */
637: public void setSetNode(SetNode newSetNode) {
638: setNode = newSetNode;
639: }
640:
641: /**
642: * INTERNAL
643: * Set the Where node
644: */
645: public void setWhereNode(WhereNode newWhereNode) {
646: whereNode = newWhereNode;
647: }
648:
649: /**
650: * INTERNAL
651: * Set the DISTINCT state for the tree
652: */
653: public void setDistinctState(short newDistinctState) {
654: distinctState = newDistinctState;
655: }
656:
657: /**
658: * INTERNAL
659: * Print the contents of the parse tree on a string
660: */
661: public String toString() {
662: StringBuffer buffer = new StringBuffer();
663: buffer.append(getContext().toString());
664: return ToStringLocalization.buildMessage("context",
665: (Object[]) null)
666: + " " + buffer.toString();
667: }
668:
669: /**
670: * INTERNAL
671: * Verify that the alias in the SELECT is valid.
672: * Invalid: SELECT OBJECT(badAlias) FROM Employee employee....
673: * Valid: SELECT OBJECT(employee) FROM Employee employee....
674: */
675: public void verifySelect(DatabaseQuery theQuery,
676: GenerationContext generationContext) {
677: if (theQuery.isObjectLevelReadQuery()) {
678: //verify the selected alias,
679: //this will throw an error if the alias is bad
680: ((SelectNode) getQueryNode())
681: .verifySelectedAlias(generationContext);
682: }
683: }
684:
685: /**
686: * INTERNAL
687: * Answer true if DISTINCT has been chosen.
688: */
689: public boolean usesDistinct() {
690: return distinctState == ObjectLevelReadQuery.USE_DISTINCT;
691: }
692: }
|