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 oracle.toplink.essentials.exceptions.EJBQLException;
040: import oracle.toplink.essentials.expressions.*;
041: import oracle.toplink.essentials.internal.expressions.*;
042: import oracle.toplink.essentials.mappings.DatabaseMapping;
043: import oracle.toplink.essentials.mappings.DirectToFieldMapping;
044: import oracle.toplink.essentials.queryframework.ObjectLevelReadQuery;
045: import oracle.toplink.essentials.queryframework.ReportQuery;
046:
047: /**
048: * INTERNAL
049: * <p><b>Purpose</b>: This node represents an 'DOT' (i.e. '.') on the input
050: * stream. The left and right will depend on the input stream.
051: * <p><b>Responsibilities</b>:<ul>
052: * </ul>
053: * @author Jon Driscoll and Joel Lucuik
054: * @since TopLink 4.0
055: */
056: public class DotNode extends LogicalOperatorNode {
057:
058: private Object enumConstant;
059:
060: /**
061: * INTERNAL
062: * Apply this node to the passed query
063: */
064: public void applyToQuery(ObjectLevelReadQuery theQuery,
065: GenerationContext context) {
066: if (theQuery.isReportQuery()) {
067: ReportQuery reportQuery = (ReportQuery) theQuery;
068: reportQuery.addAttribute(resolveAttribute(),
069: generateExpression(context));
070: reportQuery.dontRetrievePrimaryKeys();
071: }
072: }
073:
074: /**
075: * INTERNAL
076: * Check the left child node for an unqualified field access. The method
077: * delegates to the left most expression of multi-navigation path
078: * expression.
079: */
080: public Node qualifyAttributeAccess(ParseTreeContext context) {
081: if (getLeft() != null) {
082: setLeft(getLeft().qualifyAttributeAccess(context));
083: }
084: return this ;
085: }
086:
087: /**
088: * INTERNAL
089: * Validate node and calculate its type.
090: * Check for enum literals.
091: */
092: public void validate(ParseTreeContext context) {
093: TypeHelper typeHelper = context.getTypeHelper();
094: String name = ((AttributeNode) right).getAttributeName();
095: // check for fully qualified type names
096: Node leftMost = getLeftMostNode();
097: if (isDeclaredVariable(leftMost, context)) {
098: left.validate(context);
099: checkNavigation(left, context);
100: Object type = typeHelper.resolveAttribute(left.getType(),
101: name);
102: if (type == null) {
103: // could not resolve attribute
104: throw EJBQLException.unknownAttribute(context
105: .getQueryInfo(), right.getLine(), right
106: .getColumn(), name, typeHelper.getTypeName(left
107: .getType()));
108: }
109: setType(type);
110: right.setType(type);
111: } else {
112: // Check for enum literal access
113: String typeName = left.getAsString();
114: Object type = resolveEnumTypeName(typeName, typeHelper);
115: if ((type != null) && typeHelper.isEnumType(type)) {
116: enumConstant = typeHelper.resolveEnumConstant(type,
117: name);
118: if (enumConstant == null) {
119: throw EJBQLException.invalidEnumLiteral(context
120: .getQueryInfo(), right.getLine(), right
121: .getColumn(), typeName, name);
122: }
123: } else {
124: // left most node is not an identification variable and
125: // dot expression doe not denote an enum literal access =>
126: // unknown identification variable
127: throw EJBQLException.aliasResolutionException(context
128: .getQueryInfo(), leftMost.getLine(), leftMost
129: .getColumn(), leftMost.getAsString());
130: }
131: setType(type);
132: right.setType(type);
133: }
134: }
135:
136: /**
137: * INTERNAL
138: * Checks whether the left hand side of this dot node is navigable.
139: */
140: private void checkNavigation(Node node, ParseTreeContext context) {
141: TypeHelper typeHelper = context.getTypeHelper();
142: // Checks whether the type of the dot node allows a navigation.
143: Object type = node.getType();
144: if (!typeHelper.isEntityClass(type)
145: && !typeHelper.isEmbeddable(type)
146: && !typeHelper.isEnumType(type)) {
147: throw EJBQLException.invalidNavigation(context
148: .getQueryInfo(), node.getLine(), node.getColumn(),
149: this .getAsString(), node.getAsString(), typeHelper
150: .getTypeName(type));
151: }
152: // Special check to disallow collection valued relationships
153: if (node.isDotNode()) {
154: Node left = node.getLeft();
155: AttributeNode right = (AttributeNode) node.getRight();
156: if (typeHelper.isCollectionValuedRelationship(left
157: .getType(), right.getAttributeName())) {
158: throw EJBQLException.invalidCollectionNavigation(
159: context.getQueryInfo(), right.getLine(), right
160: .getColumn(), this .getAsString(), right
161: .getAttributeName());
162: }
163: }
164: }
165:
166: /** */
167: private boolean isDeclaredVariable(Node node,
168: ParseTreeContext context) {
169: if (node.isVariableNode()) {
170: String name = ((VariableNode) node)
171: .getCanonicalVariableName();
172: return context.isVariable(name);
173: }
174: return false;
175: }
176:
177: /**
178: * INTERNAL
179: * Return a TopLink expression by getting the required variables using the
180: * left and right nodes
181: * "emp.address.city" = builder.get("address").get("city")
182: */
183: public Expression generateExpression(GenerationContext context) {
184: Node left = getLeft();
185: Node right = getRight();
186:
187: if (enumConstant != null) {
188: // enum literal access
189: return new ConstantExpression(enumConstant,
190: new ExpressionBuilder());
191: } else {
192: // Get the left expression
193: Expression whereClause = getLeft().generateExpression(
194: context);
195:
196: // Calculate the mapping and pass it to the right expression
197: if (right.isAttributeNode()) {
198: ((AttributeNode) right)
199: .setMapping(resolveMapping(context));
200: }
201:
202: // Or it with whatever the right expression is
203: whereClause = right.addToExpression(whereClause, context);
204:
205: // and return the expression...
206: return whereClause;
207: }
208: }
209:
210: /**
211: * INTERNAL
212: * Yes, this is a dot node
213: */
214: public boolean isDotNode() {
215: return true;
216: }
217:
218: /**
219: * INTERNAL
220: * ():
221: * Answer true if the SELECTed node has a left and right, and the right represents
222: * a direct-to-field mapping.
223: */
224: public boolean endsWithDirectToField(GenerationContext context) {
225: DatabaseMapping mapping = resolveMapping(context);
226: return (mapping != null) && mapping.isDirectToFieldMapping();
227: }
228:
229: /**
230: * INTERNAL
231: * Returns the attribute type if the right represents a direct-to-field mapping.
232: */
233: public Class getTypeOfDirectToField(GenerationContext context) {
234: DatabaseMapping mapping = resolveMapping(context);
235: if ((mapping != null) && mapping.isDirectToFieldMapping()) {
236: return ((DirectToFieldMapping) mapping)
237: .getAttributeClassification();
238: }
239: return null;
240: }
241:
242: /**
243: * INTERNAL
244: * ():
245: * Answer true if the node has a left and right, and the right represents
246: * a collection mapping.
247: */
248: public boolean endsWithCollectionField(GenerationContext context) {
249: DatabaseMapping mapping = resolveMapping(context);
250: return (mapping != null) && mapping.isCollectionMapping();
251: }
252:
253: /**
254: * INTERNAL
255: * Answer the name of the attribute which is represented by the receiver's
256: * right node.
257: */
258: public String resolveAttribute() {
259: return ((AttributeNode) getRight()).getAttributeName();
260: }
261:
262: /**
263: * INTERNAL
264: * Answer the mapping resulting from traversing the receiver's nodes
265: */
266: public DatabaseMapping resolveMapping(GenerationContext context) {
267: Class leftClass = getLeft().resolveClass(context);
268: return getRight().resolveMapping(context, leftClass);
269: }
270:
271: /**
272: * resolveClass: Answer the class which results from traversing the mappings for the receiver's nodes
273: */
274: public Class resolveClass(GenerationContext context) {
275: Class leftClass = getLeft().resolveClass(context);
276: return getRight().resolveClass(context, leftClass);
277: }
278:
279: /**
280: * INTERNAL
281: * Get the string representation of this node.
282: */
283: public String getAsString() {
284: return left.getAsString() + "." + right.getAsString();
285: }
286:
287: /**
288: * INTERNAL
289: * Return the left most node of a dot expr, so return 'a' for 'a.b.c'.
290: */
291: private Node getLeftMostNode() {
292: return left.isDotNode() ? ((DotNode) left).getLeftMostNode()
293: : left;
294: }
295:
296: /**
297: * INTERNAL
298: * Returns the type representation for the specified type name. The method
299: * looks for inner classes if it cannot resolve the type name.
300: */
301: private Object resolveEnumTypeName(String name, TypeHelper helper) {
302: Object type = helper.resolveTypeName(name);
303: if (type == null) {
304: // check for inner enum type
305: int index = name.lastIndexOf('.');
306: if (index != -1) {
307: name = name.substring(0, index) + '$'
308: + name.substring(index + 1);
309: type = helper.resolveTypeName(name);
310: }
311: }
312: return type;
313: }
314: }
|