001: /**********************************************************************
002: Copyright (c) 2004 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:
016: Contributors:
017: 2005 Jorg von Frantzius - updates for fetch-depth
018: 2007 Andy Jefferson - rewritten to process all fields detecting persistable objects at runtime
019: ...
020: **********************************************************************/package org.jpox.store.fieldmanager;
021:
022: import java.lang.reflect.Array;
023: import java.util.Collection;
024: import java.util.Map;
025:
026: import org.jpox.StateManager;
027: import org.jpox.FetchPlan.FetchPlanForClass;
028: import org.jpox.api.ApiAdapter;
029: import org.jpox.metadata.AbstractMemberMetaData;
030: import org.jpox.sco.SCO;
031: import org.jpox.sco.SCOUtils;
032: import org.jpox.state.DetachState;
033: import org.jpox.state.FetchPlanState;
034:
035: /**
036: * FieldManager to handle the detachment of fields with persistable objects.
037: *
038: * @version $Revision: 1.10 $
039: */
040: public class DetachFieldManager extends AbstractFetchFieldManager {
041: /** Whether we should create detached copies, or detach in situ. */
042: boolean copy = true;
043:
044: /**
045: * Constructor for a field manager for detachment.
046: * @param sm the StateManager of the instance being detached. An instance in Persistent or Transactional state
047: * @param secondClassMutableFields The second class mutable fields for the class of this object
048: * @param fpClass Fetch Plan for the class of this instance
049: * @param state State object to hold any pertinent controls for the detachment process
050: * @param copy Whether to create detached COPIES or just detach in situ
051: */
052: public DetachFieldManager(StateManager sm,
053: boolean[] secondClassMutableFields,
054: FetchPlanForClass fpClass, FetchPlanState state,
055: boolean copy) {
056: super (sm, secondClassMutableFields, fpClass, state);
057: this .copy = copy;
058: }
059:
060: /**
061: * Utility method to process the passed persistable object.
062: * @param pc The PC object
063: */
064: protected Object processPersistable(Object pc) {
065: if (pc == null) {
066: return null;
067: }
068:
069: ApiAdapter api = sm.getObjectManager().getApiAdapter();
070: if (!api.isPersistable(pc)) {
071: return pc;
072: }
073:
074: if (!api.isDetached(pc)) {
075: if (api.isPersistent(pc)) {
076: // Persistent object that is not yet detached so detach it
077: if (copy) {
078: // Detach a copy and return the copy
079: return sm.getObjectManager().detachObjectCopy(pc,
080: state);
081: } else {
082: // Detach the object
083: sm.getObjectManager().detachObject(pc, state);
084: }
085: }
086: }
087: return pc;
088: }
089:
090: /**
091: * Method to fetch an object field whether it is collection/map, PC, or whatever for the detachment process.
092: * @param fieldNumber Number of the field
093: * @return The object
094: */
095: protected Object internalFetchObjectField(int fieldNumber) {
096: SingleValueFieldManager sfv = new SingleValueFieldManager();
097: sm.provideFields(new int[] { fieldNumber }, sfv);
098: Object value = sfv.fetchObjectField(fieldNumber);
099: ApiAdapter api = sm.getObjectManager().getApiAdapter();
100:
101: if (value == null) {
102: return null;
103: } else {
104: AbstractMemberMetaData fmd = sm.getClassMetaData()
105: .getMetaDataForManagedMemberAtAbsolutePosition(
106: fieldNumber);
107: if (api.isPersistable(value)) {
108: // Process PC fields
109: return processPersistable(value);
110: } else if (value instanceof Collection
111: || value instanceof Map) {
112: // Process all elements of Collections/Maps that are PC
113: if (!(value instanceof SCO)) {
114: // Replace with SCO so we can work with it
115: value = sm.wrapSCOField(fieldNumber, value, false,
116: false, true);
117: }
118: SCO sco = (SCO) value;
119:
120: if (copy) {
121: return sco.detachCopy(state);
122: }
123:
124: if (sco instanceof Collection) {
125: // Detach all PC elements of the collection
126: SCOUtils.detachForCollection(sm, ((Collection) sco)
127: .toArray(), state);
128: sco.unsetOwner();
129: } else if (sco instanceof Map) {
130: // Detach all PC keys/values of the map
131: SCOUtils.detachForMap(sm, ((Map) sco).entrySet(),
132: state);
133: sco.unsetOwner();
134: }
135: return sco;
136: } else if (value instanceof Object[]) {
137: // Process object array
138: if (!api
139: .isPersistable(fmd.getType().getComponentType())) {
140: // Array element type is not persistable so just return
141: return value;
142: }
143:
144: Object[] arrValue = (Object[]) value;
145: Object[] arrDetached = (Object[]) Array.newInstance(fmd
146: .getType().getComponentType(), arrValue.length);
147: for (int j = 0; j < arrValue.length; j++) {
148: // Detach elements as appropriate
149: arrDetached[j] = processPersistable(arrValue[j]);
150: }
151: return arrDetached;
152: } else if (secondClassMutableFields[fieldNumber]) {
153: // Other SCO - what to do here ? unset owner?
154: SCO sco;
155: if (value instanceof SCO) {
156: sco = (SCO) value;
157: } else {
158: // Replace with a SCO wrapper so that we can detach it
159: sco = (SCO) sm.wrapSCOField(fieldNumber, value,
160: false, false, true);
161: }
162: if (copy) {
163: return sco.detachCopy(state);
164: }
165:
166: return sco;
167: } else {
168: // Primitive, primitive array, or other non-PC field
169: }
170: return value;
171: }
172: }
173:
174: /**
175: * Method to throw and EndOfFetchPlanGraphException since we're at the end of a branch in the tree.
176: * @param fieldNumber Number of the field
177: * @return Object to return
178: */
179: protected Object endOfGraphOperation(int fieldNumber) {
180: // check if the object here is PC and is in the detached cache anyway
181: SingleValueFieldManager sfv = new SingleValueFieldManager();
182: sm.provideFields(new int[] { fieldNumber }, sfv);
183: Object value = sfv.fetchObjectField(fieldNumber);
184: ApiAdapter api = sm.getObjectManager().getApiAdapter();
185:
186: if (api.isPersistable(value)) {
187: Object detached = null;
188: if (copy) {
189: detached = ((DetachState) state)
190: .getDetachedCopyObject(value);
191: }
192: if (detached != null) {
193: // While we are at the end of a branch and this would go beyond the depth limits,
194: // the object here *is* already detached so just return it
195: return detached;
196: }
197: if (!copy
198: && sm.getObjectManager().getApiAdapter()
199: .isDetached(value)) {
200: return value;
201: }
202: }
203:
204: // we reached a leaf of the object graph to detach
205: throw new EndOfFetchPlanGraphException();
206: }
207: }
|