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: import java.util.*;
040:
041: // TopLink imports
042: import oracle.toplink.essentials.exceptions.*;
043: import oracle.toplink.essentials.expressions.*;
044: import oracle.toplink.essentials.mappings.DatabaseMapping;
045: import oracle.toplink.essentials.queryframework.ObjectLevelReadQuery;
046: import oracle.toplink.essentials.queryframework.ReportQuery;
047:
048: /**
049: * INTERNAL
050: * <p><b>Purpose</b>: The Superclass for for typed variables, local variables and remote variables
051: * <p><b>Responsibilities</b>:<ul>
052: * <li> Generate the correct expression for an AND in EJBQL
053: * </ul>
054: * @author Jon Driscoll and Joel Lucuik
055: * @since TopLink 4.0
056: */
057: public class VariableNode extends Node {
058:
059: /** */
060: private String variableName;
061:
062: /** */
063: private String canonicalName;
064:
065: /**
066: * VariableNode constructor comment.
067: */
068: public VariableNode() {
069: super ();
070: }
071:
072: public VariableNode(String newVariableName) {
073: setVariableName(newVariableName);
074: }
075:
076: public String getVariableName() {
077: return variableName;
078: }
079:
080: public void setVariableName(String newVariableName) {
081: variableName = newVariableName;
082: canonicalName = IdentificationVariableDeclNode
083: .calculateCanonicalName(newVariableName);
084: }
085:
086: /** */
087: public String getCanonicalVariableName() {
088: return canonicalName;
089: }
090:
091: /**
092: * INTERNAL
093: * Is this node a VariableNode
094: */
095: public boolean isVariableNode() {
096: return true;
097: }
098:
099: /**
100: * INTERNAL
101: * Apply this node to the passed query
102: */
103: public void applyToQuery(ObjectLevelReadQuery theQuery,
104: GenerationContext generationContext) {
105: String name = getCanonicalVariableName();
106: ParseTreeContext context = generationContext
107: .getParseTreeContext();
108: if (theQuery instanceof ReportQuery) {
109: ReportQuery reportQuery = (ReportQuery) theQuery;
110: Expression expression = generationContext
111: .expressionFor(name);
112: if (expression == null) {
113: expression = generateExpression(generationContext);
114: }
115: addAttributeWithFetchJoins(reportQuery, expression,
116: generationContext);
117: } else {
118: addFetchJoins(theQuery, generationContext);
119: }
120: }
121:
122: /**
123: * INTERNAL
124: * Add the variable as ReportQuery item. The method checks for any JOIN
125: * FETCH nodes of the current variable and adds them as part of the
126: * ReportQuery item.
127: */
128: private void addAttributeWithFetchJoins(ReportQuery reportQuery,
129: Expression expression, GenerationContext context) {
130: String name = getCanonicalVariableName();
131: List fetchJoinNodes = context.getParseTreeContext()
132: .getFetchJoins(name);
133: if (fetchJoinNodes == null) {
134: reportQuery.addAttribute(name, expression);
135: } else {
136: List fetchJoinExprs = new ArrayList(fetchJoinNodes.size());
137: for (Iterator i = fetchJoinNodes.iterator(); i.hasNext();) {
138: Node node = (Node) i.next();
139: fetchJoinExprs.add(node.generateExpression(context));
140: }
141: reportQuery.addItem(name, expression, fetchJoinExprs);
142: }
143: }
144:
145: /**
146: * INTERNAL
147: * Check for any JOIN FETCH nodes of the current variable and add them as
148: * joined attributes. This method is called in case of a non ReportQuery
149: * instance.
150: */
151: private void addFetchJoins(ObjectLevelReadQuery theQuery,
152: GenerationContext context) {
153: String name = getCanonicalVariableName();
154: List fetchJoinNodes = context.getParseTreeContext()
155: .getFetchJoins(name);
156: if (fetchJoinNodes != null) {
157: for (Iterator i = fetchJoinNodes.iterator(); i.hasNext();) {
158: Node node = (Node) i.next();
159: theQuery.addJoinedAttribute(node
160: .generateExpression(context));
161: }
162: }
163: }
164:
165: /**
166: * INTERNAL
167: * This node represent an unqualified field access in the case the method
168: * is called and the variableName is not defined as identification variable.
169: * The method returns a DotNode representing a qualified field access with
170: * the base variable as left child node. The right child node is an
171: * AttributeNode using the variableName as field name.
172: */
173: public Node qualifyAttributeAccess(ParseTreeContext context) {
174: return context.isVariable(variableName) ? this : (Node) context
175: .getNodeFactory().newQualifiedAttribute(getLine(),
176: getColumn(), context.getBaseVariable(),
177: variableName);
178: }
179:
180: /**
181: * INTERNAL
182: * Validate node and calculate its type.
183: */
184: public void validate(ParseTreeContext context) {
185: TypeHelper typeHelper = context.getTypeHelper();
186: String name = getCanonicalVariableName();
187: if (context.isRangeVariable(name)) {
188: String schema = context.schemaForVariable(name);
189: setType(typeHelper.resolveSchema(schema));
190: } else {
191: Node path = context.pathForVariable(name);
192: if (path == null) {
193: throw EJBQLException.aliasResolutionException(context
194: .getQueryInfo(), getLine(), getColumn(), name);
195: } else {
196: setType(path.getType());
197: }
198: }
199: context.usedVariable(name);
200: if (context.isDeclaredInOuterScope(name)) {
201: context.registerOuterScopeVariable(name);
202: }
203: }
204:
205: public Expression generateBaseBuilderExpression(
206: GenerationContext context) {
207: //create builder, and add it, and answer it
208: //BUG 3106877: Need to create builder using the actual class (if using parallel expressions)
209: if (context.useParallelExpressions()) {
210: return new ExpressionBuilder(this .resolveClass(context));
211: } else {
212: return new ExpressionBuilder();
213: }
214: }
215:
216: public Expression generateExpression(
217: GenerationContext generationContext) {
218: Expression myExpression = null;
219: String name = getCanonicalVariableName();
220:
221: //is there a cached Expression?
222: myExpression = generationContext.expressionFor(name);
223: if (myExpression != null) {
224: return myExpression;
225: }
226:
227: //Either I have an alias type, or I'm an IN declaration
228: if (generationContext.getParseTreeContext().isRangeVariable(
229: name)) {
230: myExpression = generateBaseBuilderExpression(generationContext);
231: } else {
232: myExpression = generateExpressionForAlias(generationContext);
233: }
234:
235: generationContext.addExpression(myExpression, name);
236: return myExpression;
237: }
238:
239: public Expression generateExpressionForAlias(
240: GenerationContext context) {
241: // BUG 3105651: Verify if we need to resolve this alias, or just use
242: // an empty ExpressionBuilder. See OrderByItemNode.generateExpression()
243: // for more details
244: if (context.getParseTree().getQueryNode().isSelectNode()
245: && context.shouldCheckSelectNodeBeforeResolving()
246: && (((SelectNode) context.getParseTree().getQueryNode())
247: .isSelected(this .getCanonicalVariableName()))) {
248: return new ExpressionBuilder();
249: }
250:
251: Node nodeForAlias = getNodeForAlias(context);
252:
253: //assume that if there is no node available for the given variable, then
254: //there must be an alias mismatch. Assume they know their attribute names better
255: //than their alias names. - JGL
256: if (nodeForAlias == null) {
257: throw EJBQLException.aliasResolutionException(context
258: .getParseTreeContext().getQueryInfo(), getLine(),
259: getColumn(), getVariableName());
260: }
261:
262: //create builder, and answer it
263: return nodeForAlias.generateExpression(context);
264: }
265:
266: public Node getNodeForAlias(GenerationContext context) {
267: //Node node = context.getParseTreeContext().nodeForIdentifier(getCanonicalVariableName());
268: //return node != null ? ((IdentificationVariableDeclNode)node).getPath() : null;
269: return context.getParseTreeContext().pathForVariable(
270: getCanonicalVariableName());
271: }
272:
273: /**
274: * isAlias: Answer true if this variable represents an alias in the FROM clause.
275: * i.e. "FROM Employee emp" declares "emp" as an alias
276: */
277: public boolean isAlias(GenerationContext context) {
278: return isAlias(context.getParseTreeContext());
279: }
280:
281: public boolean isAlias(ParseTreeContext context) {
282: String classNameForAlias = context
283: .schemaForVariable(getCanonicalVariableName());
284: return classNameForAlias != null;
285: }
286:
287: /**
288: * resolveClass: Answer the class which corresponds to my variableName. This is the class for
289: * an alias, where the variableName is registered to an alias.
290: */
291: public Class resolveClass(GenerationContext generationContext) {
292: Class clazz = null;
293: String name = getCanonicalVariableName();
294: ParseTreeContext context = generationContext
295: .getParseTreeContext();
296: if (context.isRangeVariable(name)) {
297: String schema = context.schemaForVariable(name);
298: clazz = context.classForSchemaName(schema,
299: generationContext);
300: } else {
301: DotNode path = (DotNode) context.pathForVariable(name);
302: if (path == null) {
303: throw EJBQLException.aliasResolutionException(context
304: .getQueryInfo(), getLine(), getColumn(), name);
305: } else {
306: clazz = path.resolveClass(generationContext);
307: }
308: }
309: return clazz;
310: }
311:
312: public String toString(int indent) {
313: StringBuffer buffer = new StringBuffer();
314: toStringIndent(indent, buffer);
315: buffer.append(toStringDisplayName() + "[" + getVariableName()
316: + "]");
317: return buffer.toString();
318: }
319:
320: /**
321: * INTERNAL
322: * Get the string representation of this node.
323: */
324: public String getAsString() {
325: return getVariableName();
326: }
327:
328: }
|