001: /**********************************************************************
002: Copyright (c) 2004 Andy Jefferson 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: Contributors:
016: 2004 Erik Bengtson - added containsEntry()
017: 2004 Erik Bengtson - added getMethod()
018: ...
019: **********************************************************************/package org.jpox.store.expression;
020:
021: import org.jpox.ClassLoaderResolver;
022: import org.jpox.exceptions.JPOXUserException;
023: import org.jpox.store.DatastoreIdentifier;
024: import org.jpox.store.IdentifierFactory;
025: import org.jpox.store.mapping.JavaTypeMapping;
026: import org.jpox.store.query.StatementText;
027: import org.jpox.store.scostore.MapStore;
028:
029: /**
030: * An expression that represents some Map field in a query candidate
031: * class, or a Map field in an object linked from the candidate class
032: * by navigation.
033: * <p>
034: * When navigated through using containsKey(expr) the keys of the Map are
035: * relationally joined onto the query statement. When navigated through using
036: * containsValue(expr) the values of the Map are relationally joined onto the
037: * query statement. These 2 methods are required for JDO 2.0, whilst the
038: * isEmpty() and contains() are JDO 1.0.1. containsEntry() is a JPOX extension.
039: * </p>
040: *
041: * @version $Revision: 1.33 $
042: */
043: public class MapExpression extends ScalarExpression {
044: private final MapStore mapStore;
045: private final String fieldName;
046:
047: /**
048: * Constructor.
049: * @param qs The Query Statement
050: * @param mapping The java field mapping
051: * @param te The Table Expression
052: * @param mapStore the backing store.
053: * @param fieldName Name of the field for the map.
054: **/
055: public MapExpression(QueryExpression qs, JavaTypeMapping mapping,
056: LogicSetExpression te, MapStore mapStore, String fieldName) {
057: super (qs);
058:
059: this .mapping = mapping;
060: this .te = te;
061: this .mapStore = mapStore;
062: this .fieldName = fieldName;
063: }
064:
065: /**
066: * Executed when the size() method is found in a query filter.
067: * @return The NumericExpression resulting from the size() method.
068: */
069: public NumericExpression sizeMethod() {
070: IdentifierFactory idFactory = qs.getStoreManager()
071: .getIdentifierFactory();
072: String ctIdentifier = idFactory.newIdentifier(te.getAlias(),
073: fieldName).getIdentifier();
074: DatastoreIdentifier ctRangeVar = idFactory.newIdentifier(
075: IdentifierFactory.TABLE, ctIdentifier);
076:
077: return new ContainerSizeExpression(qs, mapStore
078: .getSizeSubquery(qs, mapping, te, ctRangeVar));
079: }
080:
081: /**
082: * Executed when the containsKey() method is found in a query filter.
083: * This is added in JDO 2.0.
084: * @param expr The ScalarExpression param for map.containsKey(...).
085: * @return The BooleanExpression resulting from map.containsKey().
086: **/
087: public BooleanExpression containsKeyMethod(ScalarExpression expr) {
088: // mt... = "map table"
089: // vt... = "value table"
090:
091: IdentifierFactory idFactory = qs.getStoreManager()
092: .getIdentifierFactory();
093: if (expr instanceof UnboundVariable) {
094: UnboundVariable var = (UnboundVariable) expr;
095: if (var.getVariableType() == null) {
096: // Set the variable type to be the element type for this collection
097: var.setVariableType(qs.getClassLoaderResolver()
098: .classForName(mapStore.getKeyType()));
099: }
100: String vtIdentifier = "UNBOUND" + '.'
101: + var.getVariableName();
102: String mtIdentifier = idFactory.newIdentifier(
103: idFactory.newIdentifier(te.getAlias(), fieldName),
104: var.getVariableName()).getIdentifier();
105:
106: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
107: IdentifierFactory.TABLE, mtIdentifier);
108: DatastoreIdentifier vtRangeVar = idFactory.newIdentifier(
109: IdentifierFactory.TABLE, vtIdentifier);
110:
111: QueryExpression qexpr = mapStore.getExistsSubquery(qs,
112: mapping, te, mtRangeVar);
113: var.bindTo(mapStore
114: .joinKeysTo(qexpr, qs, mapping, te, mtRangeVar, var
115: .getVariableType(), expr, vtRangeVar));
116: return new ExistsExpression(qs, qexpr, true);
117: } else if (expr instanceof Literal) {
118: String mtIdentifier = idFactory.newIdentifier(
119: te.getAlias(), fieldName).getIdentifier();
120: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
121: IdentifierFactory.TABLE, mtIdentifier);
122: DatastoreIdentifier vtRangeVar;
123: int n = 0;
124:
125: do {
126: String vtIdentifier = mtIdentifier + '.' + (++n);
127: vtRangeVar = idFactory.newIdentifier(
128: IdentifierFactory.TABLE, vtIdentifier);
129: } while (qs.getTableExpression(vtRangeVar) != null);
130:
131: ClassLoaderResolver clr = qs.getClassLoaderResolver();
132: QueryExpression qexpr = mapStore.getExistsSubquery(qs,
133: mapping, te, mtRangeVar);
134: ScalarExpression joinKeysExpr = mapStore.joinKeysTo(qexpr,
135: qs, mapping, te, mtRangeVar, clr.classForName(expr
136: .getMapping().getType()), expr, vtRangeVar);
137: qexpr.andCondition(expr.eq(joinKeysExpr));
138: return new ExistsExpression(qs, qexpr, true);
139: } else {
140: String mtIdentifier = idFactory.newIdentifier(
141: te.getAlias(), fieldName).getIdentifier();
142: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
143: IdentifierFactory.TABLE, mtIdentifier);
144: DatastoreIdentifier vtRangeVar;
145:
146: if (expr.te == null) // literals
147: {
148: int n = 0;
149:
150: do {
151: String vtIdentifier = mtIdentifier + '.' + (++n);
152: vtRangeVar = idFactory.newIdentifier(
153: IdentifierFactory.TABLE, vtIdentifier);
154: } while (qs.getTableExpression(vtRangeVar) != null);
155: } else {
156: vtRangeVar = expr.te.getAlias();
157: }
158:
159: ClassLoaderResolver clr = qs.getClassLoaderResolver();
160: ScalarExpression joinKeysExpr = mapStore.joinKeysTo(expr
161: .getQueryExpression(), qs, mapping, te, mtRangeVar,
162: clr.classForName(mapStore.getKeyType()), expr,
163: vtRangeVar);
164: return joinKeysExpr.eq(expr);
165: }
166: }
167:
168: /**
169: * Executed when the containsEntry() method is found in a query filter.
170: * This is a JPOX extension to JDOQL 1.0.1/2.0.
171: * @param keyExpr The ScalarExpression param for map.containsEntry(...).
172: * @param valueExpr The ScalarExpression param for map.containsEntry(...).
173: * @return The BooleanExpression resulting from map.containsEntry().
174: **/
175: public BooleanExpression containsEntryMethod(
176: ScalarExpression keyExpr, ScalarExpression valueExpr) {
177: // mt... = "map table"
178: // kt... = "key table"
179: // vt... = "value table"
180: IdentifierFactory idFactory = qs.getStoreManager()
181: .getIdentifierFactory();
182: String mtIdentifier = idFactory.newIdentifier(te.getAlias(),
183: fieldName).getIdentifier();
184: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
185: IdentifierFactory.TABLE, mtIdentifier);
186:
187: // obtains a variable name for key
188: Class keyType;
189: DatastoreIdentifier ktRangeVar;
190: UnboundVariable keyVar = null;
191: ClassLoaderResolver clr = qs.getClassLoaderResolver();
192: int nKey = 0;
193: if (keyExpr instanceof UnboundVariable) {
194: keyVar = (UnboundVariable) keyExpr;
195: String ktJavaName = "UNBOUND" + '.'
196: + keyVar.getVariableName();
197: keyType = keyVar.getVariableType();
198: ktRangeVar = idFactory.newIdentifier(
199: IdentifierFactory.TABLE, ktJavaName);
200: } else {
201: keyType = clr.classForName(mapStore.getKeyType());
202: do {
203: String ktIdentifier = mtIdentifier + '.' + (++nKey);
204: ktRangeVar = idFactory.newIdentifier(
205: IdentifierFactory.TABLE, ktIdentifier);
206: } while (qs.getTableExpression(ktRangeVar) != null);
207: }
208:
209: // obtains a variable name for value
210: Class valueType;
211: DatastoreIdentifier vtRangeVar;
212: UnboundVariable valueVar = null;
213: int nValue = 0;
214: if (valueExpr instanceof UnboundVariable) {
215: valueVar = (UnboundVariable) valueExpr;
216: String vtJavaName = "UNBOUND" + '.'
217: + valueVar.getVariableName();
218: valueType = valueVar.getVariableType();
219: vtRangeVar = idFactory.newIdentifier(
220: IdentifierFactory.TABLE, vtJavaName);
221: } else {
222: valueType = clr.classForName(mapStore.getValueType());
223: do {
224: String vtIdentifier = mtIdentifier + '.' + (++nValue);
225: vtRangeVar = idFactory.newIdentifier(
226: IdentifierFactory.TABLE, vtIdentifier);
227: } while (qs.getTableExpression(vtRangeVar) != null);
228: }
229:
230: QueryExpression qexpr = mapStore.getExistsSubquery(qs, mapping,
231: te, mtRangeVar);
232: ScalarExpression[] qclKeyValues = mapStore.joinKeysValuesTo(
233: qexpr, qs, mapping, te, mtRangeVar, keyType, valueType,
234: keyExpr, valueExpr, ktRangeVar, vtRangeVar);
235:
236: if (keyExpr instanceof UnboundVariable) {
237: keyVar.bindTo(qclKeyValues[0]);
238: }
239: if (valueExpr instanceof UnboundVariable) {
240: valueVar.bindTo(qclKeyValues[1]);
241: }
242:
243: if (!(keyExpr instanceof UnboundVariable)) {
244: qexpr.andCondition(keyExpr.eq(qclKeyValues[0]));
245: }
246: if (!(valueExpr instanceof UnboundVariable)) {
247: qexpr.andCondition(valueExpr.eq(qclKeyValues[1]));
248: }
249: return new ExistsExpression(qs, qexpr, true);
250: }
251:
252: /**
253: * Executed when a contains() method is found in a query filter.
254: * This simply uses the containsValueMethod() to check against values.
255: * @param expr The ScalarExpression param for map.contains(...).
256: * @return The BooleanExpression resulting from map.contains().
257: **/
258: public BooleanExpression containsMethod(ScalarExpression expr) {
259: return containsValueMethod(expr);
260: }
261:
262: /**
263: * Executed when the containsValue() method is found in a query filter.
264: * This is added in JDO 2.0.
265: * @param expr The ScalarExpression param for map.containsValue(...).
266: * @return The BooleanExpression resulting from map.containsValue().
267: **/
268: public BooleanExpression containsValueMethod(ScalarExpression expr) {
269: // mt... = "map table"
270: // vt... = "value table"
271:
272: IdentifierFactory idFactory = qs.getStoreManager()
273: .getIdentifierFactory();
274: if (expr instanceof UnboundVariable) {
275: UnboundVariable var = (UnboundVariable) expr;
276: if (var.getVariableType() == null) {
277: // Set the variable type to be the element type for this collection
278: var.setVariableType(qs.getClassLoaderResolver()
279: .classForName(mapStore.getValueType()));
280: }
281: String vtIdentifier = "UNBOUND" + '.'
282: + var.getVariableName();
283: String mtIdentifier = idFactory.newIdentifier(
284: idFactory.newIdentifier(te.getAlias(), fieldName),
285: var.getVariableName()).getIdentifier();
286:
287: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
288: IdentifierFactory.TABLE, mtIdentifier);
289: DatastoreIdentifier vtRangeVar = idFactory.newIdentifier(
290: IdentifierFactory.TABLE, vtIdentifier);
291:
292: QueryExpression qexpr = mapStore.getExistsSubquery(qs,
293: mapping, te, mtRangeVar);
294: var.bindTo(mapStore
295: .joinValuesTo(qexpr, qs, mapping, te, mtRangeVar,
296: var.getVariableType(), expr, vtRangeVar));
297: return new ExistsExpression(qs, qexpr, true);
298: } else if (expr instanceof Literal) {
299: String mtIdentifier = idFactory.newIdentifier(
300: te.getAlias(), fieldName).getIdentifier();
301: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
302: IdentifierFactory.TABLE, mtIdentifier);
303: DatastoreIdentifier vtRangeVar;
304: int n = 0;
305:
306: do {
307: String vtIdentifier = mtIdentifier + '.' + (++n);
308: vtRangeVar = idFactory.newIdentifier(
309: IdentifierFactory.TABLE, vtIdentifier);
310: } while (qs.getTableExpression(vtRangeVar) != null);
311:
312: ClassLoaderResolver clr = qs.getClassLoaderResolver();
313: QueryExpression qexpr = mapStore.getExistsSubquery(qs,
314: mapping, te, mtRangeVar);
315: ScalarExpression joinValuesExpr = mapStore.joinValuesTo(
316: qexpr, qs, mapping, te, mtRangeVar, clr
317: .classForName(expr.getMapping().getType()),
318: expr, vtRangeVar);
319: qexpr.andCondition(expr.eq(joinValuesExpr));
320: return new ExistsExpression(qs, qexpr, true);
321: } else {
322: String mtIdentifier = idFactory.newIdentifier(
323: te.getAlias(), fieldName).getIdentifier();
324: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
325: IdentifierFactory.TABLE, mtIdentifier);
326: DatastoreIdentifier vtRangeVar;
327:
328: if (expr.te == null) // literals
329: {
330: int n = 0;
331:
332: do {
333: String vtIdentifier = mtIdentifier + '.' + (++n);
334: vtRangeVar = idFactory.newIdentifier(
335: IdentifierFactory.TABLE, vtIdentifier);
336: } while (qs.getTableExpression(vtRangeVar) != null);
337: } else {
338: vtRangeVar = expr.te.getAlias();
339: }
340:
341: ClassLoaderResolver clr = qs.getClassLoaderResolver();
342: ScalarExpression joinValuesExpr = mapStore.joinValuesTo(
343: expr.getQueryExpression(), qs, mapping, te,
344: mtRangeVar, clr.classForName(mapStore
345: .getValueType()), expr, vtRangeVar);
346: return joinValuesExpr.eq(expr);
347: }
348: }
349:
350: /**
351: * Return the BooleanExpression for a query filter in the form
352: * "map.isEmpty()".
353: * @return The BooleanExpression for a query filter in the form
354: * "map.isEmpty()".
355: **/
356: public BooleanExpression isEmptyMethod() {
357: // mt... = "map table"
358: IdentifierFactory idFactory = qs.getStoreManager()
359: .getIdentifierFactory();
360: String mtIdentifier = idFactory.newIdentifier(te.getAlias(),
361: fieldName).getIdentifier();
362: DatastoreIdentifier mtRangeVar = idFactory.newIdentifier(
363: IdentifierFactory.TABLE, mtIdentifier);
364:
365: return new ExistsExpression(qs, mapStore.getExistsSubquery(qs,
366: mapping, te, mtRangeVar), false);
367: }
368:
369: /**
370: * Method to get a value from the Map for a key
371: * @param expr The key argument expression
372: * @return The statement
373: **/
374: public ScalarExpression getMethod(ScalarExpression expr) {
375: // mt... = "map table"
376: // vt... = "value table"
377: IdentifierFactory idFactory = qs.getStoreManager()
378: .getIdentifierFactory();
379: String mtIdentifier = idFactory.newIdentifier(te.getAlias(),
380: fieldName).getIdentifier();
381:
382: DatastoreIdentifier mtRangeVar;
383: DatastoreIdentifier ktRangeVar;
384: DatastoreIdentifier vtRangeVar;
385: int n = 0;
386: if (expr instanceof UnboundVariable) {
387: UnboundVariable var = (UnboundVariable) expr;
388: if (var.getVariableType() == null) {
389: // Set the variable type to be the element type for this collection
390: var.setVariableType(qs.getClassLoaderResolver()
391: .classForName(mapStore.getKeyType()));
392: }
393: String ktIdentifier = "UNBOUND" + '.'
394: + var.getVariableName();
395: String vtIdentifier = "UNBOUNDVALUE" + '.'
396: + var.getVariableName();
397:
398: mtRangeVar = idFactory.newIdentifier(
399: IdentifierFactory.TABLE, mtIdentifier);
400: ktRangeVar = idFactory.newIdentifier(
401: IdentifierFactory.TABLE, ktIdentifier);
402: vtRangeVar = idFactory.newIdentifier(
403: IdentifierFactory.TABLE, vtIdentifier);
404: } else {
405: mtRangeVar = idFactory.newIdentifier(
406: IdentifierFactory.TABLE, mtIdentifier);
407:
408: do {
409: String ktJavaName = mtIdentifier + '.' + (++n);
410: ktRangeVar = idFactory.newIdentifier(
411: IdentifierFactory.TABLE, ktJavaName);
412: } while (qs.getTableExpression(ktRangeVar) != null);
413:
414: n = 0;
415:
416: do {
417: String vtIdentifier = mtIdentifier + '.' + (++n);
418: vtRangeVar = idFactory.newIdentifier(
419: IdentifierFactory.TABLE, vtIdentifier);
420: } while (qs.getTableExpression(vtRangeVar) != null
421: || vtRangeVar.equals(ktRangeVar));
422: }
423:
424: /* QueryExpression qexpr = */mapStore.getExistsSubquery(qs,
425: mapping, te, mtRangeVar);
426: ClassLoaderResolver clr = qs.getClassLoaderResolver();
427: ScalarExpression[] joinKeysExpr = mapStore.joinKeysToGet(qs,
428: qs, mapping, te, mtRangeVar, clr.classForName(mapStore
429: .getKeyType()), ktRangeVar, vtRangeVar);
430: if (expr instanceof UnboundVariable) {
431: UnboundVariable var = (UnboundVariable) expr;
432: var.bindTo(joinKeysExpr[0]);
433: }
434: return new ObjectExpression(qs, joinKeysExpr[1],
435: joinKeysExpr[0].eq(expr), joinKeysExpr[1].te);
436: }
437:
438: /**
439: * Method to return the statement text.
440: * @param mode (0=PROJECTION;1=FILTER)
441: * @return The statement
442: * @throws JPOXUserException since this object is inaccessible directly.
443: **/
444: public StatementText toStatementText(int mode) {
445: throw new JPOXUserException(
446: "Cannot reference Map object directly: field name = "
447: + fieldName);
448: }
449: }
|