001: /**********************************************************************
002: Copyright (c) 2003 Erik Bengtson 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: 2003 Andy Jefferson - coding standards
017: 2007 Andy Jefferson - rewritten equality expression to allow for PC mapping recursion
018: ...
019: **********************************************************************/package org.jpox.store.expression;
020:
021: import java.lang.reflect.Field;
022: import java.security.AccessController;
023: import java.security.PrivilegedAction;
024:
025: import org.jpox.ClassLoaderResolver;
026: import org.jpox.ObjectManager;
027: import org.jpox.ObjectManagerHelper;
028: import org.jpox.StateManager;
029: import org.jpox.api.ApiAdapter;
030: import org.jpox.exceptions.JPOXException;
031: import org.jpox.exceptions.JPOXUserException;
032: import org.jpox.identity.OID;
033: import org.jpox.metadata.AbstractClassMetaData;
034: import org.jpox.metadata.AbstractMemberMetaData;
035: import org.jpox.metadata.IdentityType;
036: import org.jpox.store.DatastoreAdapter;
037: import org.jpox.store.DatastoreClass;
038: import org.jpox.store.StoreManager;
039: import org.jpox.store.fieldmanager.FieldManager;
040: import org.jpox.store.fieldmanager.SingleValueFieldManager;
041: import org.jpox.store.mapping.EmbeddedMapping;
042: import org.jpox.store.mapping.JavaTypeMapping;
043: import org.jpox.store.mapping.PersistenceCapableMapping;
044: import org.jpox.util.JPOXLogger;
045:
046: /**
047: * Representation of an Object literal in a query.
048: *
049: * @version $Revision: 1.43 $
050: **/
051: public class ObjectLiteral extends ObjectExpression implements Literal {
052: private Object value;
053:
054: /** Raw value that this literal represents. */
055: Object rawValue;
056:
057: // passed by parameters or variable in a query
058: private String mappingClass;
059:
060: /**
061: * Creates an Object literal
062: * @param qs the QueryExpression
063: * @param mapping the mapping
064: * @param value the Object value
065: */
066: public ObjectLiteral(QueryExpression qs, JavaTypeMapping mapping,
067: Object value) {
068: super (qs);
069:
070: st.appendParameter(mapping, value);
071: this .mapping = mapping;
072: this .value = value;
073: }
074:
075: /**
076: * Creates an Object literal
077: * @param qs the QueryExpression
078: * @param mapping the mapping
079: * @param value the Object value
080: * @param mappingClass the declared mapped class
081: */
082: public ObjectLiteral(QueryExpression qs, JavaTypeMapping mapping,
083: Object value, String mappingClass) {
084: super (qs);
085:
086: st.appendParameter(mapping, value);
087: this .value = value;
088: this .mapping = mapping;
089: this .mappingClass = mappingClass;
090: }
091:
092: public Object getValue() {
093: return value;
094: }
095:
096: /**
097: * Method called when the query contains "object == value".
098: * @param expr The expression
099: * @return The resultant expression for this query relation
100: */
101: public BooleanExpression eq(ScalarExpression expr) {
102: if (expr instanceof ObjectLiteral) {
103: // Other side is a literal so just do an object comparison
104: return new BooleanLiteral(qs, mapping, value
105: .equals(((ObjectLiteral) expr).value));
106: } else if (expr instanceof BooleanBitColumnExpression) {
107: return null;
108: } else if (expr instanceof ObjectExpression) {
109: BooleanExpression bExpr = getEqualityExpressionForObjectExpression((ObjectExpression) expr);
110: return bExpr;
111: } else {
112: return super .eq(expr);
113: }
114: }
115:
116: /**
117: * Method called when the query contains "object NOTEQUALS value".
118: * @param expr The expression
119: * @return The resultant expression for this query relation
120: */
121: public BooleanExpression noteq(ScalarExpression expr) {
122: if (expr instanceof ObjectLiteral) {
123: return new BooleanLiteral(qs, mapping, !value
124: .equals(((ObjectLiteral) expr).value));
125: } else if (expr instanceof ObjectExpression) {
126: BooleanExpression bExpr = getEqualityExpressionForObjectExpression((ObjectExpression) expr);
127: bExpr = new BooleanExpression(this , OP_NOTEQ, bExpr);
128: return bExpr;
129: } else {
130: return super .noteq(expr);
131: }
132: }
133:
134: /**
135: * Convenience method to generate an equality expression with the passed ObjectExpression.
136: * Used by the eq() and noteq() methods.
137: * @param expr The ObjectExpression
138: * @return The BooleanExpression for equality.
139: */
140: private BooleanExpression getEqualityExpressionForObjectExpression(
141: ObjectExpression expr) {
142: BooleanExpression bExpr = null;
143: if (value == null) {
144: bExpr = expr.eq(new NullLiteral(qs));
145: } else {
146: StoreManager storeMgr = qs.getStoreManager();
147: DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
148: ClassLoaderResolver clr = qs.getClassLoaderResolver();
149: if (value instanceof OID) {
150: // Object is an OID
151: JavaTypeMapping m = dba.getMapping(((OID) value)
152: .getKeyValue().getClass(), storeMgr, clr);
153: ScalarExpression oidExpr = m.newLiteral(qs,
154: ((OID) value).getKeyValue());
155: bExpr = expr.expressionList.getExpression(0)
156: .eq(oidExpr);
157: } else {
158: ApiAdapter api = qs.getStoreManager().getApiAdapter();
159: AbstractClassMetaData cmd = storeMgr
160: .getMetaDataManager().getMetaDataForClass(
161: value.getClass(), clr);
162: if (cmd == null) {
163: // if there is no metadata, we either have an SingleFieldIdentity, application identity, or any object
164: if (storeMgr.getApiAdapter()
165: .isSingleFieldIdentityClass(
166: value.getClass().getName())) {
167: // Object is SingleFieldIdentity
168: JavaTypeMapping m = dba
169: .getMapping(
170: api
171: .getTargetClassForSingleFieldIdentity(value),
172: storeMgr, clr);
173: ScalarExpression oidExpr = m
174: .newLiteral(
175: qs,
176: api
177: .getTargetKeyForSingleFieldIdentity(value));
178: bExpr = expr.expressionList.getExpression(0)
179: .eq(oidExpr);
180: } else {
181: String pcClassName = storeMgr
182: .getClassNameForObjectID(value, clr,
183: null);
184: if (pcClassName != null) {
185: // Object is an application identity
186: cmd = storeMgr.getMetaDataManager()
187: .getMetaDataForClass(pcClassName,
188: clr);
189: bExpr = eqApplicationIdentity(value, null,
190: expr, null, storeMgr, clr);
191: } else {
192: // Value not PersistenceCapable nor an identity, so return nothing "(1 = 0)"
193: bExpr = new BooleanLiteral(qs, mapping,
194: false).eq(new BooleanLiteral(qs,
195: mapping, true));
196: }
197: }
198: } else {
199: // Value is a PersistenceCapable
200: if (cmd.getIdentityType() == IdentityType.APPLICATION) {
201: // Application identity
202: if (api.getIdForObject(value) != null) {
203: // Persistent PC object (FCO)
204: // Cater for composite PKs and parts of PK being PC mappings, and recursion
205: JavaTypeMapping[] pkMappingsApp = new JavaTypeMapping[expr.expressionList
206: .size()];
207: Object[] pkFieldValues = new Object[expr.expressionList
208: .size()];
209: int position = 0;
210: for (int i = 0; i < cmd
211: .getNoOfPrimaryKeyMembers(); i++) {
212: AbstractMemberMetaData mmd = cmd
213: .getMetaDataForManagedMemberAtAbsolutePosition(cmd
214: .getPKMemberPositions()[i]);
215: Object fieldValue = getFieldValue(mmd,
216: value);
217: JavaTypeMapping mapping = dba
218: .getMapping(fieldValue
219: .getClass(), storeMgr,
220: clr);
221: if (mapping instanceof PersistenceCapableMapping) {
222: position = populatePrimaryKeyMappingsValuesForPCMapping(
223: pkMappingsApp,
224: pkFieldValues,
225: position,
226: (PersistenceCapableMapping) mapping,
227: cmd, mmd, fieldValue,
228: storeMgr, clr);
229: } else {
230: pkMappingsApp[position] = mapping;
231: pkFieldValues[position] = fieldValue;
232: position++;
233: }
234: }
235:
236: for (int i = 0; i < expr.expressionList
237: .size(); i++) {
238: ScalarExpression source = expr.expressionList
239: .getExpression(i);
240: ScalarExpression target = pkMappingsApp[i]
241: .newLiteral(qs,
242: pkFieldValues[i]);
243: if (bExpr == null) {
244: bExpr = source.eq(target);
245: } else {
246: bExpr = bExpr
247: .and(source.eq(target));
248: }
249: }
250: } else {
251: // PC object with no id (embedded, or transient maybe)
252: for (int i = 0; i < expr.expressionList
253: .size(); i++) {
254: // Query should return nothing (so just do "(1 = 0)")
255: JPOXLogger.QUERY.warn(LOCALISER.msg(
256: "037003", value));
257: bExpr = new BooleanLiteral(qs, mapping,
258: false).eq(new BooleanLiteral(
259: qs, mapping, true));
260: // It is arguable that we should compare the id with null (as below)
261: /*bExpr = expr.eq(new NullLiteral(qs));*/
262: }
263: }
264: } else {
265: // Datastore identity
266: for (int i = 0; i < expr.expressionList.size(); i++) {
267: ScalarExpression source = expr.expressionList
268: .getExpression(i);
269: OID objectId = (OID) api
270: .getIdForObject(value);
271: if (objectId == null) {
272: // PC object with no id (embedded, or transient maybe)
273: // Query should return nothing (so just do "(1 = 0)")
274: JPOXLogger.QUERY.warn(LOCALISER.msg(
275: "037003", value));
276: bExpr = new BooleanLiteral(qs, mapping,
277: false).eq(new BooleanLiteral(
278: qs, mapping, true));
279: // It is arguable that we should compare the id with null (as below)
280: /*bExpr = expr.eq(new NullLiteral(qs));*/
281: } else {
282: JavaTypeMapping m = dba.getMapping(
283: objectId.getKeyValue()
284: .getClass(), storeMgr,
285: clr);
286: ScalarExpression oidExpr = m
287: .newLiteral(qs, objectId
288: .getKeyValue());
289: bExpr = source.eq(oidExpr);
290: }
291: }
292: }
293: }
294: if (expr.conditionExpr != null) {
295: bExpr = new BooleanExpression(expr.conditionExpr,
296: OP_AND, bExpr);
297: }
298: }
299: }
300: return bExpr;
301: }
302:
303: /**
304: * Convenience method to populate PK mappings/values allowing for recursion where a PK field is itself
305: * a PCMapping, that itself has PK mappings, which in turn may include PCMappings.
306: * The pkMappings/pkFieldValues arrays are already created and we populate from "position".
307: * @param pkMappings The array of pk mappings to be populated
308: * @param pkFieldValues The array of pk field values to be populated
309: * @param position The current position needing populating
310: * @param pcMapping The PC mapping we are processing
311: * @param cmd ClassMetaData for the owning class with this PCMapping field
312: * @param mmd Field metadata for the field that this PCMapping represents
313: * @param fieldValue The value for the PCMapping field in the owning object
314: * @param storeMgr Store Manager
315: * @param clr ClassLoader resolver
316: * @return The current position (after our processing)
317: */
318: private int populatePrimaryKeyMappingsValuesForPCMapping(
319: JavaTypeMapping[] pkMappings, Object[] pkFieldValues,
320: int position, PersistenceCapableMapping pcMapping,
321: AbstractClassMetaData cmd, AbstractMemberMetaData mmd,
322: Object fieldValue, StoreManager storeMgr,
323: ClassLoaderResolver clr) {
324: JavaTypeMapping[] subMappings = pcMapping.getJavaTypeMapping();
325: if (subMappings.length == 0) {
326: // Embedded PC has no PK so must be embedded-only so use mapping from owner table
327: DatastoreClass table = storeMgr.getDatastoreClass(cmd
328: .getFullClassName(), clr);
329: JavaTypeMapping ownerMapping = table.getFieldMapping(mmd);
330: EmbeddedMapping embMapping = (EmbeddedMapping) ownerMapping;
331: for (int k = 0; k < embMapping
332: .getNumberOfJavaTypeMappings(); k++) {
333: JavaTypeMapping subMapping = embMapping
334: .getJavaTypeMapping(k);
335: pkMappings[position] = subMapping;
336: pkFieldValues[position] = getFieldValue(subMapping
337: .getFieldMetaData(), fieldValue);
338: position++;
339: }
340: } else {
341: AbstractClassMetaData pcCmd = storeMgr.getMetaDataManager()
342: .getMetaDataForClass(pcMapping.getType(), clr);
343: int[] pcPkPositions = pcCmd.getPKMemberPositions();
344: for (int k = 0; k < subMappings.length; k++) {
345: AbstractMemberMetaData pcMmd = pcCmd
346: .getMetaDataForManagedMemberAtAbsolutePosition(pcPkPositions[k]);
347: if (subMappings[k] instanceof PersistenceCapableMapping) {
348: Object val = getFieldValue(pcMmd, fieldValue);
349: position = populatePrimaryKeyMappingsValuesForPCMapping(
350: pkMappings, pkFieldValues, position,
351: (PersistenceCapableMapping) subMappings[k],
352: pcCmd, pcMmd, val, storeMgr, clr);
353: } else {
354: Object val = getFieldValue(pcMmd, fieldValue);
355: pkMappings[position] = subMappings[k];
356: pkFieldValues[position] = val;
357: position++;
358: }
359: }
360: }
361: return position;
362: }
363:
364: /**
365: * Internal class to track indexes. Just used to pass references between calls
366: * and increment the index value
367: */
368: private static class Index {
369: int index;
370: }
371:
372: /**
373: * Create an equality expression for an application identity using reflection to retrieve values
374: * and generate the mappings
375: * @param id the id
376: * @param bExpr the boolean equals expression
377: * @param expr the object expression
378: * @param index the current index in the source expression
379: * @param storeMgr the StoreManager
380: * @param clr the ClassLoaderResolver
381: * @return the equals expression
382: */
383: private BooleanExpression eqApplicationIdentity(Object id,
384: BooleanExpression bExpr, ScalarExpression expr,
385: Index index, StoreManager storeMgr, ClassLoaderResolver clr) {
386: if (index == null) {
387: index = new Index();
388: }
389: for (int i = 0; i < id.getClass().getDeclaredFields().length; i++) {
390: Object value = getFieldValue(id, id.getClass()
391: .getDeclaredFields()[i].getName());
392: String pcClassName = storeMgr.getClassNameForObjectID(
393: value, clr, null);
394: if (pcClassName != null) {
395: // if it is a PersistenceCapable ID, we run recursivelly this operation
396: if (bExpr == null) {
397: bExpr = eqApplicationIdentity(value, bExpr, expr,
398: index, storeMgr, clr);
399: } else {
400: bExpr = bExpr.and(eqApplicationIdentity(value,
401: bExpr, expr, index, storeMgr, clr));
402: }
403: } else {
404: //if a simple value, we simply apply the equals
405: ScalarExpression source = expr.getExpressionList()
406: .getExpression(index.index);
407: JavaTypeMapping mapping = storeMgr
408: .getDatastoreAdapter().getMapping(
409: value.getClass(), storeMgr, clr);
410: ScalarExpression target = mapping.newLiteral(qs, value);
411: if (bExpr == null) {
412: bExpr = source.eq(target);
413: } else {
414: bExpr = bExpr.and(source.eq(target));
415: }
416: if (target.getExpressionList().size() == 0) {
417: //TODO why getExpressionList().size() == 0
418: index.index++;
419: } else {
420: index.index += target.getExpressionList().size();
421: }
422: }
423: }
424: return bExpr;
425: }
426:
427: /**
428: * Reads a field of an object passed by parameter to the query
429: * @param subfieldName Name of the subfield
430: * @param innerJoin whether to inner join
431: * @return a new ScalarExpression from the field value
432: */
433: public ScalarExpression accessField(String subfieldName,
434: boolean innerJoin) {
435: Object fieldValue = null;
436: try {
437: AbstractClassMetaData acmd = qs.getStoreManager()
438: .getMetaDataManager().getMetaDataForClass(
439: this .mappingClass,
440: qs.getClassLoaderResolver());
441: if (acmd == null) {
442: //no pc classes
443: fieldValue = getFieldValue(value, subfieldName);
444: } else {
445: AbstractMemberMetaData fmd = acmd
446: .getMetaDataForMember(subfieldName);
447: if (fmd == null) {
448: throw new JPOXUserException("Cannot access field "
449: + subfieldName + " on type "
450: + this .mappingClass);
451: }
452: fieldValue = getFieldValue(fmd, value);
453: }
454: } catch (Exception e) {
455: fieldValue = getFieldValue(value, subfieldName);
456: }
457:
458: try {
459: if (fieldValue == null) {
460: return new NullLiteral(this .qs);
461: }
462: DatastoreAdapter dba = qs.getStoreManager()
463: .getDatastoreAdapter();
464: JavaTypeMapping mapping;
465: if (mappingClass != null && subfieldName == null) {
466: mapping = dba
467: .getMapping(qs.getClassLoaderResolver()
468: .classForName(mappingClass), qs
469: .getStoreManager(), qs
470: .getClassLoaderResolver());
471: } else {
472: mapping = dba
473: .getMapping(fieldValue.getClass(), qs
474: .getStoreManager(), qs
475: .getClassLoaderResolver());
476: }
477: return mapping.newLiteral(qs, fieldValue);
478: } catch (SecurityException e) {
479: throw new JPOXUserException("Cannot access field: "
480: + subfieldName, e);
481: } catch (IllegalArgumentException e) {
482: throw new JPOXUserException("Cannot access field: "
483: + subfieldName, e);
484: } catch (Exception e) {
485: throw new JPOXUserException("Cannot access field: "
486: + subfieldName + " " + e, e);
487: }
488: }
489:
490: public String toString() {
491: return super .toString() + " = " + value.toString();
492: }
493:
494: /**
495: * Helper method to retrieve the java.lang.reflect.Field
496: * @param clazz the Class instance of the declaring class or interface
497: * @param fieldName the field name
498: * @return The field
499: */
500: private Field getDeclaredFieldPrivileged(final Class clazz,
501: final String fieldName) {
502: if ((clazz == null) || (fieldName == null)) {
503: return null;
504: }
505:
506: return (Field) AccessController
507: .doPrivileged(new PrivilegedAction() {
508: public Object run() {
509: Class seekingClass = clazz;
510: do {
511: try {
512: return seekingClass
513: .getDeclaredField(fieldName);
514: } catch (SecurityException ex) {
515: throw new JPOXException(
516: "CannotGetDeclaredField", ex)
517: .setFatal();
518: } catch (NoSuchFieldException ex) {
519: // do nothing, we will return null later if no
520: // field is found in this class or superclasses
521: } catch (LinkageError ex) {
522: throw new JPOXException(
523: "ClassLoadingError", ex)
524: .setFatal();
525: }
526: seekingClass = seekingClass.getSuperclass();
527: } while (seekingClass != null);
528:
529: //no field found
530: return null;
531: }
532: });
533: }
534:
535: /**
536: * Get the field value of a managed field/property.
537: * @param fmd metadata for the field/property
538: * @param object the pc object
539: * @return The field value
540: */
541: private Object getFieldValue(AbstractMemberMetaData fmd,
542: Object object) {
543: ObjectManager om = ObjectManagerHelper.getObjectManager(object);
544: if (om == null) {
545: return getFieldValue(object, fmd.getName());
546: }
547: StateManager sm = om.findStateManager(object);
548: FieldManager fm = new SingleValueFieldManager();
549:
550: if (!fmd.isPrimaryKey()) {
551: // we expect that primary key field are non null
552: om.getApiAdapter().isLoaded(sm,
553: fmd.getAbsoluteFieldNumber());
554: }
555: sm
556: .provideFields(
557: new int[] { fmd.getAbsoluteFieldNumber() }, fm);
558:
559: return fm.fetchObjectField(fmd.getAbsoluteFieldNumber());
560: }
561:
562: private Object getFieldValue(Object object, String fieldName) {
563: Object fieldValue;
564: final Field field = getDeclaredFieldPrivileged(object
565: .getClass(), fieldName);
566: if (field == null) {
567: throw new JPOXUserException("Cannot access field: "
568: + fieldName + " in type " + object.getClass());
569: }
570:
571: try {
572: // if the field is not accessible, try to set the accessible flag.
573: if (!field.isAccessible()) {
574: try {
575: AccessController
576: .doPrivileged(new PrivilegedAction() {
577: public Object run() {
578: field.setAccessible(true);
579: return null;
580: }
581: });
582: } catch (SecurityException ex) {
583: throw new JPOXException("Cannot access field: "
584: + fieldName, ex).setFatal();
585: }
586: }
587: fieldValue = field.get(object);
588: } catch (IllegalArgumentException e2) {
589: throw new JPOXUserException("Cannot access field: "
590: + fieldName, e2);
591: } catch (IllegalAccessException e2) {
592: throw new JPOXUserException("Cannot access field: "
593: + fieldName, e2);
594: }
595: return fieldValue;
596: }
597:
598: /**
599: * Method to save a "raw" value that this literal represents.
600: * This value differs from the literal value since that is of the same type as this literal.
601: * @param val The raw value
602: */
603: public void setRawValue(Object val) {
604: this .rawValue = val;
605: }
606:
607: /**
608: * Accessor for the "raw" value that this literal represents.
609: * This value differs from the literal value since that is of the same type as this literal.
610: * @return The raw value
611: */
612: public Object getRawValue() {
613: return rawValue;
614: }
615: }
|