KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > parsing > SelectNode


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.internal.parsing;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27
28 import oracle.toplink.essentials.queryframework.*;
29 import oracle.toplink.essentials.expressions.Expression;
30 import oracle.toplink.essentials.expressions.ExpressionBuilder;
31 import oracle.toplink.essentials.descriptors.ClassDescriptor;
32
33 /**
34  * INTERNAL
35  * <p><b>Purpose</b>: Represent a SELECT
36  * <p><b>Responsibilities</b>:<ul>
37  * <li> Hold the distinct status
38  * <li> Modify a query based on the contents
39  *
40  * The SELECT statement determines the return type of an EJBQL query.
41  * The SELECT may also determine the distinct state of a query
42  *
43  * A SELECT can be one of the following:
44  * 1. SELECT OBJECT(someObject)... This query will return a collection of objects
45  * 2. SELECT anObject.anAttribute ... This will return a collection of anAttribute
46  * 3. SELECT &lt;aggregateFunction&gt; ... This will return a single value
47  * The allowable aggregateFunctions are: AVG, COUNT, MAX, MIN, SUM
48  * SELECT AVG(emp.salary)... Returns the average of all the employees salaries
49  * SELECT COUNT(emp)... Returns a count of the employees
50  * SELECT COUNT(emp.firstName)... Returns a count of the employee's firstNames
51  * SELECT MAX(emp.salary)... Returns the maximum employee salary
52  * SELECT MIN(emp.salary)... Returns the minimum employee salary
53  * SELECT SUM(emp.salary)... Returns the sum of all the employees salaries
54  *
55  * </ul>
56  * @author Jon Driscoll
57  * @since TopLink 5.0
58  */

59 public class SelectNode extends QueryNode {
60
61     private List JavaDoc selectExpressions = new ArrayList JavaDoc();
62     private boolean distinct =false;
63     
64     public SelectNode() {
65     }
66
67     /**
68      * INTERNAL
69      * Add an Order By Item to this node
70      */

71     private void addSelectExpression(Object JavaDoc theNode) {
72         selectExpressions.add(theNode);
73     }
74
75     /** */
76     public List JavaDoc getSelectExpressions() {
77         return selectExpressions;
78     }
79
80     /** */
81     public void setSelectExpressions(List JavaDoc exprs) {
82         selectExpressions = exprs;
83     }
84
85     /** */
86     public boolean usesDistinct() {
87         return distinct;
88     }
89     
90     /** */
91     public void setDistinct(boolean distinct) {
92         this.distinct = distinct;
93     }
94
95     /**
96      * INTERNAL
97      * Apply this node to the passed query
98      */

99     public void applyToQuery(DatabaseQuery theQuery, GenerationContext context) {
100         ObjectLevelReadQuery readQuery = (ObjectLevelReadQuery)theQuery;
101         if (selectExpressions.isEmpty()) {
102             return;
103         }
104
105         //set the distinct state
106
//BUG 3168673: Don't set distinct state if we're using Count
107
if (!(isSingleSelectExpression() && getFirstSelectExpressionNode().isCountNode())) {
108             // Set the distinct state for the query
109
if (usesDistinct()) {
110                 getParseTree().setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
111                 readQuery.setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
112             }
113         }
114
115         if (readQuery instanceof ReportQuery) {
116             ReportQuery reportQuery = (ReportQuery)readQuery;
117             reportQuery.returnWithoutReportQueryResult();
118             if (isSingleSelectExpression() &&
119                 !getFirstSelectExpressionNode().isConstructorNode()) {
120                 reportQuery.returnSingleAttribute();
121             }
122         }
123         SelectGenerationContext selectContext = (SelectGenerationContext)context;
124         for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext();) {
125             Node node = (Node)i.next();
126             if (selectingRelationshipField(node, context)) {
127                 selectContext.useOuterJoins();
128             }
129             node.applyToQuery(readQuery, context);
130             selectContext.dontUseOuterJoins();
131         }
132
133         //indicate on the query if "return null if primary key null"
134
//This means we want nulls returned if we expect an outer join
135
if (this.hasOneToOneSelected(context)) {
136             readQuery.setProperty("return null if primary key is null", Boolean.TRUE);
137         } else {
138             readQuery.removeProperty("return null if primary key is null");
139         }
140
141     }
142
143     /**
144      * INTERNAL
145      **/

146     public boolean hasOneToOneSelected(GenerationContext context) {
147         // Iterate the select expression and return true if one of it has a
148
// oneToOne selected.
149
for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext();) {
150             Node node = (Node)i.next();
151             if (hasOneToOneSelected(node, context)) {
152                 return true;
153             }
154         }
155         return false;
156     }
157     
158     /**
159      * INTERNAL
160      * Answer true if there is a one-to-one relationship selected.
161      * This includes a chain of relationships.
162      * True: SELECT employee.address FROM ..... //Simple 1:1
163      * True: SELECT a.b.c.d FROM ..... //where a->b, b->c and c->d are all 1:1.
164      * False: SELECT OBJECT(employee) FROM ..... //simple SELECT
165      * False: SELECT phoneNumber.areaCode FROM ..... //direct-to-field
166      **/

167     private boolean hasOneToOneSelected(Node node, GenerationContext context) {
168         //BUG 3240484: Not SELECTing 1:1 if it's in a COUNT
169
if (node.isCountNode()) {
170             return false;
171         }
172
173         if (node.isAggregateNode()) {
174             // delegate to aggregate expression
175
return hasOneToOneSelected(node.getLeft(), context);
176         }
177          
178         if (node.isVariableNode()){
179             return !nodeRefersToObject(node, context);
180         }
181
182         if (node.isConstructorNode()) {
183             List JavaDoc args = ((ConstructorNode)node).getConstructorItems();
184             for (Iterator JavaDoc i = args.iterator(); i.hasNext();) {
185                 Node arg = (Node)i.next();
186                 if (hasOneToOneSelected(arg, context)) {
187                     return true;
188                 }
189             }
190             return false;
191         }
192       
193         // check whether it is a direct-to-field mapping
194
return !selectingDirectToField(node, context);
195     }
196
197     /**
198     * Verify that the selected alias is a valid alias. If it's not valid,
199     * an Exception will be thrown, likely EJBQLException.aliasResolutionException.
200     *
201     * Valid: SELECT OBJECT(emp) FROM Employee emp WHERE ...
202     * Invalid: SELECT OBJECT(badAlias) FROM Employee emp WHERE ...
203     */

204     public void verifySelectedAlias(GenerationContext context) {
205         for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext();) {
206             Node node = (Node)i.next();
207             //if the node is a DotNode, there is no selected alias
208
if (node.isDotNode()) {
209                 return;
210             }
211             node.resolveClass(context);
212         }
213     }
214
215     /**
216     * Answer true if the variable name given as argument is SELECTed.
217     *
218     * True: "SELECT OBJECT(emp) ...." & variableName = "emp"
219     * False: "SELECT OBJECT(somethingElse) ..." & variableName = "emp"
220     */

221     public boolean isSelected(String JavaDoc variableName) {
222         for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext();) {
223             Node node = (Node)i.next();
224             //Make sure we've SELECted a VariableNode
225
if (node.isVariableNode() &&
226                 ((VariableNode)node).getCanonicalVariableName().equals(variableName)) {
227                 return true;
228             }
229         }
230         return false;
231     }
232
233     public boolean isSelectNode() {
234         return true;
235     }
236
237     /**
238      * INTERNAL
239      * Validate node.
240      */

241     public void validate(ParseTreeContext context) {
242         for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext(); ) {
243             Node item = (Node)i.next();
244             item.validate(context);
245         }
246     }
247
248     /**
249      * resolveClass: Answer the class associated with my left node.
250      */

251     public Class JavaDoc resolveClass(GenerationContext context) {
252         return getReferenceClass(context);
253     }
254     
255     /**
256      * INTERNAL
257      * Return a TopLink expression generated using the left node
258      */

259     public Expression generateExpression(GenerationContext context) {
260         return null;
261     }
262
263   /**
264    * Compute the Reference class for this query
265    * @param context
266    * @return the class this query is querying for
267    */

268     public Class JavaDoc getReferenceClass(GenerationContext context) {
269         return getClassOfFirstVariable(context);
270     }
271     
272     /** */
273     private Class JavaDoc getClassOfFirstVariable(GenerationContext context) {
274         Class JavaDoc clazz = null;
275         String JavaDoc variable = getParseTree().getFromNode().getFirstVariable();
276         ParseTreeContext parseTreeContext = context.getParseTreeContext();
277         if (parseTreeContext.isRangeVariable(variable)) {
278             String JavaDoc schema = parseTreeContext.schemaForVariable(variable);
279             // variables is defines in a range variable declaration, so there
280
// is a schema name for this variable
281
clazz = parseTreeContext.classForSchemaName(schema, context);
282         } else {
283             // variable is defined in a JOIN clause, so there is a a defining
284
// node for the variable
285
Node path = parseTreeContext.pathForVariable(variable);
286             clazz = path.resolveClass(context);
287         }
288         return clazz;
289     }
290
291     /**
292     * INTERNAL
293     * Answer true if a variable in the IN clause is SELECTed
294     */

295     public boolean isVariableInINClauseSelected(GenerationContext context) {
296         for (Iterator JavaDoc i = selectExpressions.iterator(); i.hasNext();) {
297             Node node = (Node)i.next();
298         
299             if (node.isVariableNode()) {
300                 String JavaDoc variableNameForLeft = ((VariableNode)node).getCanonicalVariableName();
301                 if (!context.getParseTreeContext().isRangeVariable(variableNameForLeft)) {
302                     return true;
303                 }
304             }
305         }
306         return false;
307     }
308
309     /**
310      * INTERNAL
311      * Answer true if this node refers to an object described later in the EJBQL
312      * True: SELECT p FROM Project p
313      * False: SELECT p.id FROM Project p
314      **/

315     public boolean nodeRefersToObject(Node node, GenerationContext context) {
316         if (!node.isVariableNode()){
317             return false;
318         }
319         String JavaDoc name = ((VariableNode)node).getCanonicalVariableName();
320         String JavaDoc alias = context.getParseTreeContext().schemaForVariable(name);
321         if (alias != null){
322             ClassDescriptor descriptor = context.getSession().getDescriptorForAlias(alias);
323             if (descriptor != null){
324                 return true;
325             }
326         }
327         return false;
328     }
329
330     /**
331      * INTERNAL
332      */

333     private boolean selectingRelationshipField(Node node, GenerationContext context) {
334         if ((node == null) || !node.isDotNode()) {
335             return false;
336         }
337         return !((DotNode)node).endsWithDirectToField(context);
338     }
339
340     /**
341      * INTERNAL
342      * Answer true if the SELECT ends in a direct-to-field.
343      * true: SELECT phone.areaCode
344      * false: SELECT employee.address
345      */

346     private boolean selectingDirectToField(Node node, GenerationContext context) {
347
348         if ((node == null) || !node.isDotNode()) {
349             return false;
350         }
351         return ((DotNode)node).endsWithDirectToField(context);
352     }
353
354     /**
355      * Returns the first select expression node.
356      */

357     private Node getFirstSelectExpressionNode() {
358         return selectExpressions.size() > 0 ?
359             (Node)selectExpressions.get(0) : null;
360     }
361
362     /** */
363     private boolean isSingleSelectExpression() {
364         return selectExpressions.size() == 1;
365     }
366     
367     
368 }
369
Popular Tags