001: /**********************************************************************
002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2003 Andy Jefferson - coding standards
018: 2005 Andy Jefferson - added support for implicit variables
019: 2006 Andy Jefferson - support for using in result clauses
020: ...
021: **********************************************************************/package org.jpox.store.expression;
022:
023: import org.jpox.api.ApiAdapter;
024: import org.jpox.exceptions.JPOXUserException;
025: import org.jpox.metadata.AbstractClassMetaData;
026: import org.jpox.store.DatastoreClass;
027: import org.jpox.store.DatastoreIdentifier;
028: import org.jpox.store.IdentifierFactory;
029: import org.jpox.store.mapping.JavaTypeMapping;
030: import org.jpox.store.query.StatementText;
031:
032: /**
033: * Representation of an unbound variable in a Query.
034: *
035: * @version $Revision: 1.27 $
036: **/
037: public class UnboundVariable extends ScalarExpression {
038: /** Name of the variable */
039: private final String name;
040:
041: /** Type of the variable. This may be assigned later if implicit. */
042: private Class type;
043:
044: /** Reference to the Binder so that we can bind the variable at the appropriate point. */
045: private final UnboundVariable.VariableBinder binder;
046:
047: /**
048: * Constructor.
049: * @param qs The Query Expression
050: * @param name Name of the variable
051: * @param type Type of the variable (if known at this point)
052: * @param binder The Binder that we can bind with this variable when we know which expression to bind to.
053: */
054: public UnboundVariable(QueryExpression qs, String name, Class type,
055: UnboundVariable.VariableBinder binder) {
056: super (qs);
057:
058: this .name = name;
059: this .type = type;
060: this .binder = binder;
061: getExpressionToBindToThis();
062:
063: }
064:
065: /**
066: * Accessor for the variable name.
067: * @return The variable name
068: */
069: public String getVariableName() {
070: return name;
071: }
072:
073: /**
074: * @return may return null, if type is unknown
075: */
076: public LogicSetExpression getLogicSetExpression() {
077: if (te == null) {
078: ScalarExpression boundExpr = getExpressionToBindToThis();
079: if (boundExpr == null) {
080: return null;
081: }
082: return boundExpr.getLogicSetExpression();
083: }
084: return te;
085: }
086:
087: /**
088: * Accessor for the variable type.
089: * @return The variable type
090: */
091: public Class getVariableType() {
092: return type;
093: }
094:
095: /**
096: * Mutator for the variable type in the case where we have an implicit variable and
097: * its type is not known at construction. Only updates the type if it was null at construction.
098: * @param type The type
099: */
100: public void setVariableType(Class type) {
101: this .type = type;
102: getExpressionToBindToThis();
103: }
104:
105: /**
106: * Method to bind this variable to its expression, using the JDOQL compiler.
107: * @param qsc The expression to bind to.
108: */
109: public void bindTo(ScalarExpression qsc) {
110: binder.bindVariable(name, qsc);
111: }
112:
113: /**
114: * Change the output statement text to refer to the bound to expression
115: * since it has real fields and this is only a variable.
116: * @param mode Mode of operation
117: * @return The statement text for this unbound variable
118: */
119: public StatementText toStatementText(int mode) {
120: ScalarExpression exprToBind = getExpressionToBindToThis();
121: if (exprToBind == null) {
122: throw new JPOXUserException(
123: "Unconstrained variable referenced: " + name);
124: }
125: if (!qs.hasCrossJoin(exprToBind.te)) {
126: qs.crossJoin(exprToBind.te, true);
127: }
128:
129: return exprToBind.toStatementText(mode);
130: }
131:
132: public ExpressionList getExpressionList() {
133: ScalarExpression exprToBind = getExpressionToBindToThis();
134: if (exprToBind == null) {
135: throw new JPOXUserException(
136: "Unconstrained variable referenced: " + name);
137: }
138: if (!qs.hasCrossJoin(exprToBind.te)) {
139: qs.crossJoin(exprToBind.te, true);
140: }
141: return exprToBind.getExpressionList();
142: }
143:
144: /**
145: * StatementText representation of this expression. I.E. A Boolean field may be stored in
146: * boolean format like 0 or 1, and it can also be stored in other formats, like Y or N, TRUE or FALSE, and so on.
147: * The projection mode for the boolean field is the real content of the value stored, (e.g. Y or N), and opposed to
148: * that the filter mode for the boolean field is always represented by a boolean expression (e.g. Y=Y or N=N)
149: * In SQL, the projection can be exemplified as "SELECT BOOLEAN_FIELD ... " and the filter as
150: * "SELECT COLUMNS ... WHERE BOOLEAN_FIELD ='Y'"
151: *
152: * @return the StatementText
153: */
154: public StatementText toStatementText() {
155: throw new JPOXUserException(
156: "Unconstrained variable referenced: " + name);
157: }
158:
159: /**
160: * Checks if one expression is equals the other expression
161: * @param expr the expression to check if this is equals to the expression
162: * @return the BooleanExpression
163: */
164: public BooleanExpression eq(ScalarExpression expr) {
165: ScalarExpression boundExpr = getExpressionToBindToThis();
166: bindTo(boundExpr);
167:
168: //qs.innerJoin(expr, boundExpr, boundExpr.te, true, true);
169: if (qs.hasCrossJoin(boundExpr.te)) {
170: //qs.addAlias(boundExpr.te,true);
171: qs.andCondition(expr.eq(boundExpr));
172: } else {
173: qs.innerJoin(expr, boundExpr, boundExpr.te, true, true);
174: }
175: return expr.eq(boundExpr);
176: }
177:
178: /**
179: * Checks if one expression is not equals the other expression
180: * @param expr the expression to check if this is not equals to the expression
181: * @return the BooleanExpression
182: */
183: public BooleanExpression noteq(ScalarExpression expr) {
184: ScalarExpression boundExpr = getExpressionToBindToThis();
185: bindTo(boundExpr);
186:
187: //qs.innerJoin(expr, boundExpr, boundExpr.te, false, true);
188: if (qs.hasCrossJoin(boundExpr.te)) {
189: //qs.addAlias(boundExpr.te,true);
190: // qs.andCondition(expr.noteq(boundExpr));
191: } else {
192: qs.innerJoin(expr, boundExpr, boundExpr.te, false, true);
193: }
194:
195: return expr.noteq(boundExpr);
196: }
197:
198: /**
199: * Method to access a field in the class of the variable.
200: * @param fieldName Name of the field to access
201: * @param innerJoin whether to use an inner join to access this field
202: * @return The scalar expression for the field
203: */
204: public ScalarExpression accessField(String fieldName,
205: boolean innerJoin) {
206: ScalarExpression exprToBind = getExpressionToBindToThis();
207: bindTo(exprToBind);
208: qs.crossJoin(exprToBind.getLogicSetExpression(), true);
209:
210: return exprToBind.accessField(fieldName, innerJoin);
211: }
212:
213: /** Cached expression that we should bind to. */
214: ScalarExpression exprToBind = null;
215:
216: /**
217: * Convenience method to get the expression to bind to for this unbound variable.
218: * Caches the variable it finds so that we dont find it multiple times.
219: * @return The expression to bind to
220: */
221: private ScalarExpression getExpressionToBindToThis() {
222: if (exprToBind != null) {
223: // Use the cached value
224: return exprToBind;
225: }
226:
227: if (type == null) {
228: // No type specified, must be implicit parameter
229: return null;
230: }
231: ApiAdapter api = qs.getStoreManager().getApiAdapter();
232: if (!api.isPersistable(type)) {
233: // Type is not PC so cannot bind to it
234: return null;
235: }
236:
237: AbstractClassMetaData typeCmd = qs.getStoreManager()
238: .getMetaDataManager().getMetaDataForClass(type,
239: qs.getClassLoaderResolver());
240: if (typeCmd.isEmbeddedOnly()) {
241: // No table for this class so cannot bind to it
242: return null;
243: }
244:
245: String jtJavaName = "UNBOUND" + '.' + this .getVariableName();
246:
247: DatastoreClass cbt = qs.getStoreManager().getDatastoreClass(
248: type.getName(), qs.getClassLoaderResolver());
249: DatastoreIdentifier jtRangeVar = qs.getStoreManager()
250: .getIdentifierFactory().newIdentifier(
251: IdentifierFactory.TABLE, jtJavaName);
252: LogicSetExpression jtExpr = qs.getTableExpression(jtRangeVar);
253: if (jtExpr == null) {
254: jtExpr = qs.newTableExpression(cbt, jtRangeVar);
255: //qs.addAlias(jtExpr, true);
256: }
257:
258: // bind unbound variable
259: JavaTypeMapping mb = cbt.getIDMapping();
260: ScalarExpression exprBindTo = mb
261: .newScalarExpression(qs, jtExpr);
262:
263: // Save the mapping in case we are selecting this in the result clause later
264: mapping = mb;
265:
266: exprToBind = exprBindTo;
267:
268: return exprBindTo;
269: }
270:
271: public static interface VariableBinder {
272: /**
273: * Bind a variable to the query.
274: * @param name Name of the variable
275: * @param expr The expression
276: */
277: void bindVariable(String name, ScalarExpression expr);
278: }
279: }
|