001: /**********************************************************************
002: Copyright (c) 2005 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:
016: Contributors:
017: ...
018: **********************************************************************/package org.jpox.store.mapping;
019:
020: import java.lang.reflect.Array;
021: import java.util.List;
022:
023: import org.jpox.ClassLoaderResolver;
024: import org.jpox.StateManager;
025: import org.jpox.exceptions.JPOXUserException;
026: import org.jpox.metadata.MetaDataUtils;
027: import org.jpox.sco.SCOUtils;
028: import org.jpox.store.exceptions.ReachableObjectNotCascadedException;
029: import org.jpox.store.expression.ArrayExpression;
030: import org.jpox.store.expression.ArrayLiteral;
031: import org.jpox.store.expression.LogicSetExpression;
032: import org.jpox.store.expression.QueryExpression;
033: import org.jpox.store.expression.ScalarExpression;
034: import org.jpox.store.scostore.ArrayStore;
035: import org.jpox.util.JPOXLogger;
036:
037: /**
038: * Mapping for an array.
039: *
040: * @version $Revision: 1.30 $
041: */
042: public class ArrayMapping extends AbstractContainerMapping implements
043: MappingCallbacks {
044: private static Byte mappingSampleValue = new Byte("0");
045:
046: /**
047: * Equality operator.
048: * @param obj Object to compare against
049: * @return Whether they are equal
050: */
051: public boolean equals(Object obj) {
052: if (obj == this ) {
053: return true;
054: }
055:
056: if (!obj.getClass().equals(getClass())) {
057: return false;
058: }
059:
060: ArrayMapping sm = (ArrayMapping) obj;
061:
062: return fmd.equals(sm.fmd) && storeMgr.equals(sm.storeMgr);
063: }
064:
065: /**
066: * Accessor for the Java type represented here.
067: * @return The java type
068: */
069: public Class getJavaType() {
070: if (fmd != null) {
071: return fmd.getType();
072: }
073: return null;
074: }
075:
076: /**
077: * Accessor for a sample value for this mapping.
078: * @return The sample value
079: */
080: public Object getSampleValue(ClassLoaderResolver clr) {
081: return mappingSampleValue;
082: }
083:
084: /**
085: * Convenience method to return if the array is stored in the owning table as a column.
086: * Overrides the superclass since arrays can be stored in a single column also when the no join is
087: * specified and the array is of a primitive/wrapper type.
088: * @return Whether it is stored in a single column in the main table.
089: */
090: protected boolean containerIsStoredInSingleColumn() {
091: if (super .containerIsStoredInSingleColumn()) {
092: return true;
093: }
094: if (fmd != null
095: && fmd.hasArray()
096: && fmd.getJoinMetaData() == null
097: && MetaDataUtils.getInstance()
098: .arrayStorableAsByteArrayInSingleColumn(fmd)) {
099: return true;
100: }
101: return false;
102: }
103:
104: // ---------------------- Implementation of MappingCallbacks ----------------------------------
105:
106: /**
107: * Method to be called after the insert of the owner class element.
108: * @param sm StateManager of the owner
109: **/
110: public void postInsert(StateManager sm) {
111: Object value = sm.provideField(fmd.getAbsoluteFieldNumber());
112: if (containerIsStoredInSingleColumn()) {
113: // Check that the elements are not managed by other PM, or are not yet persisted
114: SCOUtils.validateObjectsForWriting(sm.getObjectManager(),
115: value);
116: return;
117: }
118:
119: if (value == null) {
120: return;
121: }
122:
123: if (!fmd.isCascadePersist()) {
124: // Field doesnt support cascade-persist so no reachability
125: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
126: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007006",
127: fmd.getFullFieldName()));
128: }
129:
130: // Check for any persistable keys/values that arent persistent
131: if (!fmd.getType().getComponentType().isPrimitive()) {
132: Object[] array = (Object[]) value;
133: for (int i = 0; i < array.length; i++) {
134: if (!sm.getObjectManager().getApiAdapter()
135: .isDetached(array[i])
136: && !sm.getObjectManager().getApiAdapter()
137: .isPersistent(array[i])) {
138: // Element is not persistent so throw exception
139: throw new ReachableObjectNotCascadedException(
140: fmd.getFullFieldName(), array[i]);
141: }
142: }
143: }
144: } else {
145: // Reachability
146: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
147: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007007",
148: fmd.getFullFieldName()));
149: }
150:
151: // Insert the array
152: ((ArrayStore) storeMgr.getStore(sm.getObjectManager()
153: .getClassLoaderResolver(), fmd, null)).set(sm,
154: value);
155: }
156: }
157:
158: /**
159: * Method to be called after any fetch of the owner class element.
160: * @param sm StateManager of the owner
161: */
162: public void postFetch(StateManager sm) {
163: if (containerIsStoredInSingleColumn()) {
164: // Do nothing when stored in a single column since we are handled in the main request
165: return;
166: }
167:
168: List elements = ((ArrayStore) storeMgr
169: .getStore(sm.getObjectManager()
170: .getClassLoaderResolver(), fmd, null))
171: .getArray(sm);
172: if (elements != null) {
173: boolean primitiveArray = fmd.getType().getComponentType()
174: .isPrimitive();
175: Object array = Array.newInstance(fmd.getType()
176: .getComponentType(), elements.size());
177: for (int i = 0; i < elements.size(); i++) {
178: Object element = elements.get(i);
179: if (primitiveArray) {
180: // Handle the conversion back to the primitive
181: if (element instanceof Boolean) {
182: Array.setBoolean(array, i, ((Boolean) element)
183: .booleanValue());
184: } else if (element instanceof Byte) {
185: Array.setByte(array, i, ((Byte) element)
186: .byteValue());
187: } else if (element instanceof Character) {
188: Array.setChar(array, i, ((Character) element)
189: .charValue());
190: } else if (element instanceof Double) {
191: Array.setDouble(array, i, ((Double) element)
192: .doubleValue());
193: } else if (element instanceof Float) {
194: Array.setFloat(array, i, ((Float) element)
195: .floatValue());
196: } else if (element instanceof Integer) {
197: Array.setInt(array, i, ((Integer) element)
198: .intValue());
199: } else if (element instanceof Long) {
200: Array.setLong(array, i, ((Long) element)
201: .longValue());
202: } else if (element instanceof Short) {
203: Array.setShort(array, i, ((Short) element)
204: .shortValue());
205: }
206: } else {
207: Array.set(array, i, element);
208: }
209: }
210: if (elements.size() == 0) {
211: sm.replaceField(fmd.getAbsoluteFieldNumber(), null,
212: true);
213: } else {
214: sm.replaceField(fmd.getAbsoluteFieldNumber(), array,
215: true);
216: }
217: } else {
218: sm.replaceField(fmd.getAbsoluteFieldNumber(), null, true);
219: }
220: }
221:
222: /**
223: * Method to be called after any update of the owner class element.
224: * @param sm StateManager of the owner
225: */
226: public void postUpdate(StateManager sm) {
227: Object value = sm.provideField(fmd.getAbsoluteFieldNumber());
228: if (containerIsStoredInSingleColumn()) {
229: // Check that the elements are not managed by other PM, or are not yet persisted
230: SCOUtils.validateObjectsForWriting(sm.getObjectManager(),
231: value);
232: return;
233: }
234:
235: if (value == null) {
236: // array is now null so remove any elements in the array
237: ((ArrayStore) storeMgr.getStore(sm.getObjectManager()
238: .getClassLoaderResolver(), fmd, null)).clear(sm);
239: return;
240: }
241:
242: if (!fmd.isCascadeUpdate()) {
243: // User doesnt want to update by reachability
244: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
245: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007008",
246: fmd.getFullFieldName()));
247: }
248: return;
249: }
250: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
251: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007009", fmd
252: .getFullFieldName()));
253: }
254:
255: // Update the datastore
256: ((ArrayStore) storeMgr.getStore(sm.getObjectManager()
257: .getClassLoaderResolver(), fmd, null)).clear(sm);
258: ((ArrayStore) storeMgr.getStore(sm.getObjectManager()
259: .getClassLoaderResolver(), fmd, null)).set(sm, value);
260: }
261:
262: /**
263: * Method to be called before any delete of the owner class element, if the field in the owner is dependent
264: * @param sm StateManager of the owner
265: */
266: public void preDelete(StateManager sm) {
267: if (containerIsStoredInSingleColumn()) {
268: // Do nothing when stored in a single column since we are handled in the main request
269: return;
270: }
271:
272: // makes sure field is loaded
273: sm.getObjectManager().getApiAdapter().isLoaded(sm,
274: fmd.getAbsoluteFieldNumber());
275: Object value = sm.provideField(fmd.getAbsoluteFieldNumber());
276: if (value == null) {
277: return;
278: }
279:
280: // Clear the array via its backing store
281: ((ArrayStore) storeMgr.getStore(sm.getObjectManager()
282: .getClassLoaderResolver(), fmd, null)).clear(sm);
283: }
284:
285: // --------------------------------------- JDOQL Query Methods ---------------------------------
286:
287: /**
288: * Accessor for a literal representing this type.
289: * @param qs The Query
290: * @param value the value of this object in the literal
291: * @return The literal
292: */
293: public ScalarExpression newLiteral(QueryExpression qs, Object value) {
294: if (containerIsStoredInSingleColumn()) {
295: throw new JPOXUserException(LOCALISER.msg("041026", fmd
296: .getFullFieldName())).setFatal();
297: }
298: return new ArrayLiteral(qs, this , value);
299: }
300:
301: /**
302: * Accessor for a scalar expression involving this object.
303: * @param qs The Query
304: * @param te The table holding this object.
305: * @return The expression
306: */
307: public ScalarExpression newScalarExpression(QueryExpression qs,
308: LogicSetExpression te) {
309: if (containerIsStoredInSingleColumn()) {
310: throw new JPOXUserException(LOCALISER.msg("041026", fmd
311: .getFullFieldName())).setFatal();
312: }
313: return new ArrayExpression(qs, datastoreContainer
314: .getIDMapping(), te, (ArrayStore) storeMgr.getStore(qs
315: .getClassLoaderResolver(), fmd, null), fieldName);
316: }
317: }
|