001: /**********************************************************************
002: Copyright (c) 2003 Mike Martin 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: 2004 Andy Jefferson - implementation of newScalarExpression, newLiteral
019: 2005 Andy Jefferson - basic serialisation support
020: 2005 Andy Jefferson - updated serialisation using SCOUtils methods
021: ...
022: **********************************************************************/package org.jpox.store.mapping;
023:
024: import java.util.Iterator;
025: import java.util.Set;
026:
027: import org.jpox.StateManager;
028: import org.jpox.api.ApiAdapter;
029: import org.jpox.exceptions.JPOXException;
030: import org.jpox.exceptions.JPOXUserException;
031: import org.jpox.sco.Map;
032: import org.jpox.sco.SCO;
033: import org.jpox.sco.SCOContainer;
034: import org.jpox.sco.SCOMap;
035: import org.jpox.sco.SCOUtils;
036: import org.jpox.store.exceptions.ReachableObjectNotCascadedException;
037: import org.jpox.store.expression.LogicSetExpression;
038: import org.jpox.store.expression.MapExpression;
039: import org.jpox.store.expression.MapLiteral;
040: import org.jpox.store.expression.QueryExpression;
041: import org.jpox.store.expression.ScalarExpression;
042: import org.jpox.store.scostore.MapStore;
043: import org.jpox.util.JPOXLogger;
044:
045: /**
046: * SCO Mapping for Map types.
047: *
048: * @version $Revision: 1.52 $
049: **/
050: public class MapMapping extends AbstractContainerMapping implements
051: MappingCallbacks {
052: /**
053: * Equality operator.
054: * @param obj Object to compare against
055: * @return Whether they are equal
056: */
057: public boolean equals(Object obj) {
058: if (obj == this ) {
059: return true;
060: }
061:
062: if (!obj.getClass().equals(getClass())) {
063: return false;
064: }
065:
066: MapMapping sm = (MapMapping) obj;
067:
068: return fmd.equals(sm.fmd) && storeMgr.equals(sm.storeMgr);
069: }
070:
071: /**
072: * Accessor for the Java type represented here.
073: * @return The java type
074: */
075: public Class getJavaType() {
076: return Map.class;
077: }
078:
079: // ---------------- Implementation of MappingCallbacks --------------------
080:
081: /**
082: * Method to be called after the insert of the owner class element.
083: * @param sm StateManager of the owner
084: */
085: public void postInsert(StateManager sm) {
086: java.util.Map value = (java.util.Map) sm.provideField(fmd
087: .getAbsoluteFieldNumber());
088: if (containerIsStoredInSingleColumn()) {
089: // Do nothing when serialised since we are handled in the main request
090: if (value != null) {
091: // Make sure the keys/values are ok for proceeding
092: SCOUtils.validateObjectsForWriting(sm
093: .getObjectManager(), value.keySet());
094: SCOUtils.validateObjectsForWriting(sm
095: .getObjectManager(), value.values());
096: }
097: return;
098: }
099:
100: if (value == null) {
101: // replace null map with an empty SCO wrapper
102: replaceFieldWithWrapper(sm, null, false, false);
103: return;
104: }
105:
106: if (!fmd.isCascadePersist()) {
107: // Field doesnt support cascade-persist so no reachability
108: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
109: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007006",
110: fmd.getFullFieldName()));
111: }
112:
113: // Check for any persistable keys/values that arent persistent
114: ApiAdapter api = sm.getObjectManager().getApiAdapter();
115: Set entries = value.entrySet();
116: Iterator iter = entries.iterator();
117: while (iter.hasNext()) {
118: Map.Entry entry = (Map.Entry) iter.next();
119: if (api.isPersistable(entry.getKey())) {
120: if (!api.isPersistent(entry.getKey())
121: && !api.isDetached(entry.getKey())) {
122: // Key is not persistent so throw exception
123: throw new ReachableObjectNotCascadedException(
124: fmd.getFullFieldName(), entry.getKey());
125: }
126: }
127: if (api.isPersistable(entry.getValue())) {
128: if (!api.isPersistent(entry.getValue())
129: && !api.isDetached(entry.getValue())) {
130: // Value is not persistent so throw exception
131: throw new ReachableObjectNotCascadedException(
132: fmd.getFullFieldName(), entry
133: .getValue());
134: }
135: }
136: }
137: } else {
138: // Reachability
139: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
140: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007007",
141: fmd.getFullFieldName()));
142: }
143:
144: if (value.size() > 0) {
145: // Add the entries direct to the datastore
146: ((MapStore) storeMgr.getStore(sm.getObjectManager()
147: .getClassLoaderResolver(), fmd, value
148: .getClass())).putAll(sm, value);
149:
150: // Create a SCO wrapper with the entries loaded
151: replaceFieldWithWrapper(sm, value, false, false);
152: } else {
153: // Create a SCO wrapper
154: replaceFieldWithWrapper(sm, null, false, false);
155: }
156: }
157: }
158:
159: /**
160: * Method to be called after any update of the owner class element.
161: * @param sm StateManager of the owner
162: */
163: public void postUpdate(StateManager sm) {
164: java.util.Map value = (java.util.Map) sm.provideField(fmd
165: .getAbsoluteFieldNumber());
166: if (containerIsStoredInSingleColumn()) {
167: // Do nothing when serialised since we are handled in the main request
168: if (value != null) {
169: // Make sure the keys/values are ok for proceeding
170: SCOUtils.validateObjectsForWriting(sm
171: .getObjectManager(), value.keySet());
172: SCOUtils.validateObjectsForWriting(sm
173: .getObjectManager(), value.values());
174: }
175: return;
176: }
177:
178: if (value == null) {
179: // replace null map with empty SCO wrapper
180: replaceFieldWithWrapper(sm, null, false, false);
181: return;
182: }
183:
184: if (value instanceof SCOContainer) {
185: SCOContainer sco = (SCOContainer) value;
186:
187: if (sm.getObject() == sco.getOwner()
188: && fieldName.equals(sco.getFieldName())) {
189: // Flush any outstanding updates
190: sco.flush();
191:
192: return;
193: }
194:
195: if (sco.getOwner() != null) {
196: throw new JPOXException(
197: "Owned second-class object was somehow assigned to a field other than its owner's")
198: .setFatal();
199: }
200: }
201:
202: if (!fmd.isCascadeUpdate()) {
203: // User doesnt want to update by reachability
204: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
205: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007008",
206: fmd.getFullFieldName()));
207: }
208: return;
209: }
210: if (JPOXLogger.REACHABILITY.isDebugEnabled()) {
211: JPOXLogger.REACHABILITY.debug(LOCALISER.msg("007009", fmd
212: .getFullFieldName()));
213: }
214:
215: // Update the datastore with this value of map (clear old entries and add new ones)
216: // TODO Consider making this more efficient picking the ones to remove/add
217: MapStore store = ((MapStore) storeMgr.getStore(sm
218: .getObjectManager().getClassLoaderResolver(), fmd,
219: value.getClass()));
220: store.clear(sm);
221: store.putAll(sm, value);
222:
223: // Replace the field with a wrapper containing these entries
224: replaceFieldWithWrapper(sm, value, false, false);
225: }
226:
227: /**
228: * Method to be called before any delete of the owner class element.
229: * @param sm StateManager of the owner
230: **/
231: public void preDelete(StateManager sm) {
232: // Do nothing - dependent deletion is performed by deleteDependent()
233: if (containerIsStoredInSingleColumn()) {
234: // Do nothing when serialised since we are handled in the main request
235: return;
236: }
237:
238: // makes sure field is loaded
239: sm.getObjectManager().getApiAdapter().isLoaded(sm,
240: fmd.getAbsoluteFieldNumber());
241: java.util.Map value = (java.util.Map) sm.provideField(fmd
242: .getAbsoluteFieldNumber());
243: if (value == null || value.isEmpty()) {
244: return;
245: }
246:
247: if (!(value instanceof SCO)) {
248: // Make sure we have a SCO wrapper so we can clear from the datastore
249: value = (java.util.Map) sm.wrapSCOField(fmd
250: .getAbsoluteFieldNumber(), value, false, false,
251: true);
252: }
253: value.clear();
254: ((SCOMap) value).flush();
255: }
256:
257: // -------------------------------- JDOQL Query Methods --------------------------------------
258:
259: /**
260: * Accessor for a literal representing this type.
261: * @param qs The Query
262: * @param value the value of this object in the literal
263: * @return The literal
264: */
265: public ScalarExpression newLiteral(QueryExpression qs, Object value) {
266: if (containerIsStoredInSingleColumn()) {
267: throw new JPOXUserException(LOCALISER.msg("041025", fmd
268: .getFullFieldName())).setFatal();
269: }
270: return new MapLiteral(qs, this , (java.util.Map) value);
271: }
272:
273: /**
274: * Method to return a scalar expression for the Map.
275: * @param qs The QueryStatement
276: * @param te The TableExpression
277: * @return The ScalarExpression
278: **/
279: public ScalarExpression newScalarExpression(QueryExpression qs,
280: LogicSetExpression te) {
281: if (containerIsStoredInSingleColumn()) {
282: throw new JPOXUserException(LOCALISER.msg("041025", fmd
283: .getFullFieldName())).setFatal();
284: }
285: return new MapExpression(qs, datastoreContainer.getIDMapping(),
286: te, (MapStore) storeMgr.getStore(qs
287: .getClassLoaderResolver(), fmd, null),
288: fieldName);
289: }
290: }
|