001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.kernel;
020:
021: import java.io.IOException;
022: import java.io.ObjectOutput;
023: import java.lang.reflect.Array;
024: import java.util.ArrayList;
025: import java.util.BitSet;
026: import java.util.Calendar;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.Comparator;
030: import java.util.Date;
031: import java.util.Iterator;
032: import java.util.LinkedList;
033: import java.util.List;
034: import java.util.Map;
035:
036: import org.apache.commons.collections.map.IdentityMap;
037: import org.apache.openjpa.conf.DetachOptions;
038: import org.apache.openjpa.enhance.PersistenceCapable;
039: import org.apache.openjpa.event.CallbackModes;
040: import org.apache.openjpa.event.LifecycleEvent;
041: import org.apache.openjpa.lib.util.Localizer;
042: import org.apache.openjpa.meta.ClassMetaData;
043: import org.apache.openjpa.meta.FieldMetaData;
044: import org.apache.openjpa.meta.JavaTypes;
045: import org.apache.openjpa.util.CallbackException;
046: import org.apache.openjpa.util.ObjectNotFoundException;
047: import org.apache.openjpa.util.Proxy;
048: import org.apache.openjpa.util.ProxyManager;
049: import org.apache.openjpa.util.UserException;
050:
051: /**
052: * Handles detaching instances.
053: *
054: * @author Marc Prud'hommeaux
055: * @nojavadoc
056: */
057: public class DetachManager implements DetachState {
058:
059: private static Localizer _loc = Localizer
060: .forPackage(DetachManager.class);
061:
062: private final BrokerImpl _broker;
063: private final boolean _copy;
064: private final boolean _full;
065: private final ProxyManager _proxy;
066: private final DetachOptions _opts;
067: private final OpCallbacks _call;
068: private final boolean _failFast;
069: private boolean _flushed = false;
070:
071: // if we're not detaching full, we need to track all detached objects;
072: // if we are, then we use a special field manager for more efficient
073: // detachment than the standard one
074: private final IdentityMap _detached;
075: private final DetachFieldManager _fullFM;
076:
077: /**
078: * Used to prepare a detachable instance that does not externalize
079: * detached state.
080: */
081: static boolean preSerialize(StateManagerImpl sm) {
082: if (!sm.isPersistent())
083: return false;
084:
085: flushDirty(sm);
086:
087: ClassMetaData meta = sm.getMetaData();
088: boolean setState = meta.getDetachedState() != null
089: && !ClassMetaData.SYNTHETIC.equals(meta
090: .getDetachedState());
091: BitSet idxs = (setState) ? new BitSet(meta.getFields().length)
092: : null;
093: preDetach(sm.getBroker(), sm, idxs);
094:
095: if (setState) {
096: sm.getPersistenceCapable().pcSetDetachedState(
097: getDetachedState(sm, idxs));
098: return false; // don't null state
099: }
100: return true;
101: }
102:
103: /**
104: * Used by classes that externalize detached state.
105: *
106: * @return whether to use a detached state manager
107: */
108: static boolean writeDetachedState(StateManagerImpl sm,
109: ObjectOutput out, BitSet idxs) throws IOException {
110: if (!sm.isPersistent()) {
111: out.writeObject(null); // state
112: out.writeObject(null); // sm
113: return false;
114: }
115:
116: // dirty state causes flush
117: flushDirty(sm);
118:
119: Broker broker = sm.getBroker();
120: preDetach(broker, sm, idxs);
121:
122: // write detached state object and state manager
123: DetachOptions opts = broker.getConfiguration()
124: .getDetachStateInstance();
125: if (!opts.getDetachedStateManager()
126: || !useDetachedStateManager(sm, opts)) {
127: out.writeObject(getDetachedState(sm, idxs));
128: out.writeObject(null);
129: return false;
130: }
131: out.writeObject(null);
132: out.writeObject(new DetachedStateManager(sm
133: .getPersistenceCapable(), sm, idxs, opts
134: .getAccessUnloaded(), broker.getMultithreaded()));
135: return true;
136: }
137:
138: /**
139: * Ready the object for detachment, including loading the fields to be
140: * detached and updating version information.
141: *
142: * @param idxs the indexes of fields to detach will be set as a side
143: * effect of this method
144: */
145: private static void preDetach(Broker broker, StateManagerImpl sm,
146: BitSet idxs) {
147: // make sure the existing object has the right fields fetched; call
148: // even if using currently-loaded fields for detach to make sure
149: // version is set
150: int detachMode = broker.getDetachState();
151: int loadMode = StateManagerImpl.LOAD_FGS;
152: BitSet exclude = null;
153: if (detachMode == DETACH_LOADED)
154: exclude = StoreContext.EXCLUDE_ALL;
155: else if (detachMode == DETACH_ALL)
156: loadMode = StateManagerImpl.LOAD_ALL;
157: try {
158: sm.load(broker.getFetchConfiguration(), loadMode, exclude,
159: null, false);
160: } catch (ObjectNotFoundException onfe) {
161: // consume the exception
162: }
163:
164: // create bitset of fields to detach; if mode is all we can use
165: // currently loaded bitset clone, since we know all fields are loaded
166: if (idxs != null) {
167: if (detachMode == DETACH_FETCH_GROUPS)
168: setFetchGroupFields(broker, sm, idxs);
169: else
170: idxs.or(sm.getLoaded());
171:
172: // clear lrs fields
173: FieldMetaData[] fmds = sm.getMetaData().getFields();
174: for (int i = 0; i < fmds.length; i++)
175: if (fmds[i].isLRS())
176: idxs.clear(i);
177: }
178: }
179:
180: /**
181: * Generate the detached state for the given instance.
182: */
183: private static Object getDetachedState(StateManagerImpl sm,
184: BitSet fields) {
185: // if datastore, store id in first element
186: int offset = (sm.getMetaData().getIdentityType() == ClassMetaData.ID_DATASTORE) ? 1
187: : 0;
188:
189: // make version state array one larger for new instances; marks new
190: // instances without affecting serialization size much
191: Object[] state;
192: if (sm.isNew())
193: state = new Object[3 + offset];
194: else
195: state = new Object[2 + offset];
196:
197: if (offset > 0) {
198: Object id;
199: if (sm.isEmbedded() || sm.getObjectId() == null)
200: id = sm.getId();
201: else
202: id = sm.getObjectId();
203: state[0] = id.toString();
204: }
205: state[offset] = sm.getVersion();
206: state[offset + 1] = fields;
207: return state;
208: }
209:
210: /**
211: * Flush or invoke pre-store callbacks on the given broker if
212: * needed. Return true if flushed/stored, false otherwise.
213: */
214: private static boolean flushDirty(StateManagerImpl sm) {
215: if (!sm.isDirty() || !sm.getBroker().isActive())
216: return false;
217:
218: // only flush if there are actually any dirty non-flushed fields
219: BitSet dirtyFields = sm.getDirty();
220: BitSet flushedFields = sm.getFlushed();
221: for (int i = 0; i < dirtyFields.size(); i++) {
222: if (dirtyFields.get(i) && !flushedFields.get(i)) {
223: if (sm.getBroker().getRollbackOnly())
224: sm.getBroker().preFlush();
225: else
226: sm.getBroker().flush();
227: return true;
228: }
229: }
230: return false;
231: }
232:
233: /**
234: * Create a bit set for the fields in the current fetch groups.
235: */
236: private static void setFetchGroupFields(Broker broker,
237: StateManagerImpl sm, BitSet idxs) {
238: FetchConfiguration fetch = broker.getFetchConfiguration();
239: FieldMetaData[] fmds = sm.getMetaData().getFields();
240: for (int i = 0; i < fmds.length; i++) {
241: if (fmds[i].isPrimaryKey()
242: || fetch.requiresFetch(fmds[i]) != FetchConfiguration.FETCH_NONE)
243: idxs.set(i);
244: }
245: }
246:
247: /**
248: * Constructor.
249: *
250: * @param broker owning broker
251: * @param full whether the entire broker cache is being detached; if
252: * this is the case, we assume the broker has already
253: * flushed if needed, and that we're detaching in-place
254: */
255: public DetachManager(BrokerImpl broker, boolean full,
256: OpCallbacks call) {
257: _broker = broker;
258: _proxy = broker.getConfiguration().getProxyManagerInstance();
259: _opts = broker.getConfiguration().getDetachStateInstance();
260: _copy = !full;
261: _flushed = full;
262: _call = call;
263: _failFast = (broker.getConfiguration()
264: .getMetaDataRepositoryInstance().getMetaDataFactory()
265: .getDefaults().getCallbackMode() & CallbackModes.CALLBACK_FAIL_FAST) != 0;
266:
267: // we can only rely on our "all" shortcuts if we know we won't be
268: // loading any more data
269: _full = full
270: && broker.getDetachState() == DetachState.DETACH_LOADED;
271: if (_full) {
272: _detached = null;
273: _fullFM = new DetachFieldManager();
274: } else {
275: _detached = new IdentityMap();
276: _fullFM = null;
277: }
278: }
279:
280: /**
281: * Return a detached version of the given instance.
282: */
283: public Object detach(Object toDetach) {
284: List exceps = null;
285: try {
286: return detachInternal(toDetach);
287: } catch (CallbackException ce) {
288: exceps = new ArrayList(1);
289: exceps.add(ce);
290: return null; // won't be reached as exception will be rethrown
291: } finally {
292: if (exceps == null || !_failFast)
293: exceps = invokeAfterDetach(Collections
294: .singleton(toDetach), exceps);
295: if (_detached != null)
296: _detached.clear();
297: throwExceptions(exceps);
298: }
299: }
300:
301: /**
302: * Return detached versions of all the given instances. If not copying,
303: * null will be returned.
304: */
305: public Object[] detachAll(Collection instances) {
306: List exceps = null;
307: List detached = null;
308: if (_copy)
309: detached = new ArrayList(instances.size());
310:
311: boolean failFast = false;
312: try {
313: Object detach;
314: for (Iterator itr = instances.iterator(); itr.hasNext();) {
315: detach = detachInternal(itr.next());
316: if (_copy)
317: detached.add(detach);
318: }
319: } catch (RuntimeException re) {
320: if (re instanceof CallbackException && _failFast)
321: failFast = true;
322: exceps = add(exceps, re);
323: } finally {
324: if (!failFast)
325: exceps = invokeAfterDetach(instances, exceps);
326: if (_detached != null)
327: _detached.clear();
328: }
329: throwExceptions(exceps);
330:
331: if (_copy)
332: return detached.toArray();
333: return null;
334: }
335:
336: /**
337: * Invoke postDetach() on any detached instances that implement
338: * PostDetachCallback. This will be done after the entire graph has
339: * been detached. This method has the side-effect of also clearing
340: * out the map of all detached instances.
341: */
342: private List invokeAfterDetach(Collection objs, List exceps) {
343: Iterator itr = (_full) ? objs.iterator() : _detached.entrySet()
344: .iterator();
345:
346: Object orig, detached;
347: Map.Entry entry;
348: while (itr.hasNext()) {
349: if (_full) {
350: orig = itr.next();
351: detached = orig;
352: } else {
353: entry = (Map.Entry) itr.next();
354: orig = entry.getKey();
355: detached = entry.getValue();
356: }
357:
358: StateManagerImpl sm = _broker.getStateManagerImpl(orig,
359: true);
360: try {
361: if (sm != null)
362: _broker
363: .fireLifecycleEvent(detached, orig, sm
364: .getMetaData(),
365: LifecycleEvent.AFTER_DETACH);
366: } catch (CallbackException ce) {
367: exceps = add(exceps, ce);
368: if (_failFast)
369: break; // don't continue processing
370: }
371: }
372: return exceps;
373: }
374:
375: /**
376: * Add an exception to the list.
377: */
378: private List add(List exceps, RuntimeException re) {
379: if (exceps == null)
380: exceps = new LinkedList();
381: exceps.add(re);
382: return exceps;
383: }
384:
385: /**
386: * Throw all gathered exceptions.
387: */
388: private void throwExceptions(List exceps) {
389: if (exceps == null)
390: return;
391:
392: if (exceps.size() == 1)
393: throw (RuntimeException) exceps.get(0);
394: throw new UserException(_loc.get("nested-exceps"))
395: .setNestedThrowables((Throwable[]) exceps
396: .toArray(new Throwable[exceps.size()]));
397: }
398:
399: /**
400: * Detach.
401: */
402: private Object detachInternal(Object toDetach) {
403: if (toDetach == null)
404: return null;
405:
406: // already detached?
407: if (_detached != null) {
408: Object detached = _detached.get(toDetach);
409: if (detached != null)
410: return detached;
411: }
412:
413: StateManagerImpl sm = _broker.getStateManagerImpl(toDetach,
414: true);
415: if (_call != null
416: && (_call.processArgument(OpCallbacks.OP_DETACH,
417: toDetach, sm) & OpCallbacks.ACT_RUN) == 0)
418: return toDetach;
419: if (sm == null)
420: return toDetach;
421:
422: // Call PreDetach first as we can't tell if the new system
423: // fired an event or just did not fail.
424: _broker.fireLifecycleEvent(toDetach, null, sm.getMetaData(),
425: LifecycleEvent.BEFORE_DETACH);
426:
427: // any dirty instances cause a flush to occur
428: _flushed = _flushed || flushDirty(sm);
429: BitSet fields = new BitSet();
430: preDetach(_broker, sm, fields);
431:
432: // create and store new object before copy to avoid endless recursion
433: PersistenceCapable pc = sm.getPersistenceCapable();
434: PersistenceCapable detachedPC;
435: if (_copy)
436: detachedPC = pc.pcNewInstance(null, true);
437: else
438: detachedPC = pc;
439: if (_detached != null)
440: _detached.put(toDetach, detachedPC);
441:
442: // detach fields and set detached variables
443: DetachedStateManager detSM = null;
444: if (_opts.getDetachedStateManager()
445: && useDetachedStateManager(sm, _opts))
446: detSM = new DetachedStateManager(detachedPC, sm, fields,
447: _opts.getAccessUnloaded(), _broker
448: .getMultithreaded());
449: if (_full) {
450: _fullFM.setStateManager(sm);
451: _fullFM.detachVersion();
452: _fullFM.reproxy(detSM);
453: _fullFM.setStateManager(null);
454: } else {
455: InstanceDetachFieldManager fm = new InstanceDetachFieldManager(
456: detachedPC, detSM);
457: fm.setStateManager(sm);
458: fm.detachFields(fields);
459: }
460:
461: if (!Boolean.FALSE.equals(sm.getMetaData().usesDetachedState()))
462: detachedPC.pcSetDetachedState(getDetachedState(sm, fields));
463: if (!_copy)
464: sm.release(false, !_copy);
465: if (detSM != null)
466: detachedPC.pcReplaceStateManager(detSM);
467: return detachedPC;
468: }
469:
470: private static boolean useDetachedStateManager(StateManagerImpl sm,
471: DetachOptions opts) {
472: ClassMetaData meta = sm.getMetaData();
473: return !Boolean.FALSE.equals(meta.usesDetachedState())
474: && ClassMetaData.SYNTHETIC.equals(meta
475: .getDetachedState())
476: && opts.getDetachedStateManager();
477: }
478:
479: /**
480: * Base detach field manager.
481: */
482: private static class DetachFieldManager extends
483: TransferFieldManager {
484:
485: protected StateManagerImpl sm;
486:
487: /**
488: * Set the source state manager.
489: */
490: public void setStateManager(StateManagerImpl sm) {
491: this .sm = sm;
492: }
493:
494: /**
495: * Transfer the current version object from the state manager to the
496: * detached instance.
497: */
498: public void detachVersion() {
499: FieldMetaData fmd = sm.getMetaData().getVersionField();
500: if (fmd == null)
501: return;
502:
503: Object val = JavaTypes.convert(sm.getVersion(), fmd
504: .getTypeCode());
505: val = fmd.getFieldValue(val, sm.getBroker());
506: switch (fmd.getDeclaredTypeCode()) {
507: case JavaTypes.LONG:
508: case JavaTypes.SHORT:
509: case JavaTypes.INT:
510: case JavaTypes.BYTE:
511: longval = (val == null) ? 0L : ((Number) val)
512: .longValue();
513: break;
514: case JavaTypes.DOUBLE:
515: case JavaTypes.FLOAT:
516: dblval = (val == null) ? 0D : ((Number) val)
517: .doubleValue();
518: break;
519: default:
520: objval = val;
521: }
522: sm.replaceField(getDetachedPersistenceCapable(), this , fmd
523: .getIndex());
524: }
525:
526: /**
527: * Unproxies second class object fields.
528: */
529: public void reproxy(DetachedStateManager dsm) {
530: FieldMetaData[] fmds = sm.getMetaData().getFields();
531: for (int i = 0; i < fmds.length; i++) {
532: switch (fmds[i].getDeclaredTypeCode()) {
533: case JavaTypes.COLLECTION:
534: case JavaTypes.MAP:
535: // lrs proxies not detached
536: if (fmds[i].isLRS()) {
537: objval = null;
538: sm.replaceField(
539: getDetachedPersistenceCapable(), this ,
540: i);
541: break;
542: }
543: // no break
544: case JavaTypes.CALENDAR:
545: case JavaTypes.DATE:
546: case JavaTypes.OBJECT:
547: sm.provideField(getDetachedPersistenceCapable(),
548: this , i);
549: if (objval instanceof Proxy) {
550: Proxy proxy = (Proxy) objval;
551: if (proxy.getChangeTracker() != null)
552: proxy.getChangeTracker().stopTracking();
553: proxy.setOwner(dsm, (dsm == null) ? -1 : i);
554: }
555: }
556: }
557: clear();
558: }
559:
560: /**
561: * Return the instance being detached.
562: */
563: protected PersistenceCapable getDetachedPersistenceCapable() {
564: return sm.getPersistenceCapable();
565: }
566: }
567:
568: /**
569: * FieldManager that can copy all the fields from one
570: * PersistenceCapable instance to another. One of the
571: * instances must be managed by a StateManager, and the
572: * other must be unmanaged.
573: *
574: * @author Marc Prud'hommeaux
575: */
576: private class InstanceDetachFieldManager extends DetachFieldManager {
577:
578: private final PersistenceCapable _to;
579: private final DetachedStateManager _detSM;
580:
581: /**
582: * Constructor. Supply instance to to copy to.
583: */
584: public InstanceDetachFieldManager(PersistenceCapable to,
585: DetachedStateManager detSM) {
586: _to = to;
587: _detSM = detSM;
588: }
589:
590: protected PersistenceCapable getDetachedPersistenceCapable() {
591: return _to;
592: }
593:
594: /**
595: * Detach the fields of the state manager given on construction to
596: * the persistence capable given on construction.
597: * Only the fields in the given bit set will be copied.
598: */
599: public void detachFields(BitSet fgfields) {
600: PersistenceCapable from = sm.getPersistenceCapable();
601: FieldMetaData[] pks = sm.getMetaData()
602: .getPrimaryKeyFields();
603: FieldMetaData[] fmds = sm.getMetaData().getFields();
604:
605: if (_copy)
606: _to.pcReplaceStateManager(sm);
607: try {
608: // we start with pk fields: objects might rely on pk fields for
609: // equals and hashCode methods, and this ensures that pk fields
610: // are set properly if we return any partially-detached objects
611: // due to reentrant calls when traversing relations
612: for (int i = 0; i < pks.length; i++)
613: detachField(from, pks[i].getIndex(), true);
614: detachVersion();
615: for (int i = 0; i < fmds.length; i++)
616: if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion())
617: detachField(from, i, fgfields.get(i));
618: } finally {
619: // clear the StateManager from the target object
620: if (_copy)
621: _to.pcReplaceStateManager(null);
622: }
623: }
624:
625: /**
626: * Detach (or clear) the given field index.
627: */
628: private void detachField(PersistenceCapable from, int i,
629: boolean fg) {
630: // tell the state manager to provide the fields from the source to
631: // this field manager, which will then replace the field with a
632: // detached version
633: if (fg)
634: sm.provideField(from, this , i);
635: else if (!_copy) {
636: // if not copying and field should not be detached, clear it
637: clear();
638: sm.replaceField(_to, this , i);
639: }
640: }
641:
642: public void storeBooleanField(int field, boolean curVal) {
643: super .storeBooleanField(field, curVal);
644: sm.replaceField(_to, this , field);
645: }
646:
647: public void storeByteField(int field, byte curVal) {
648: super .storeByteField(field, curVal);
649: sm.replaceField(_to, this , field);
650: }
651:
652: public void storeCharField(int field, char curVal) {
653: super .storeCharField(field, curVal);
654: sm.replaceField(_to, this , field);
655: }
656:
657: public void storeDoubleField(int field, double curVal) {
658: super .storeDoubleField(field, curVal);
659: sm.replaceField(_to, this , field);
660: }
661:
662: public void storeFloatField(int field, float curVal) {
663: super .storeFloatField(field, curVal);
664: sm.replaceField(_to, this , field);
665: }
666:
667: public void storeIntField(int field, int curVal) {
668: super .storeIntField(field, curVal);
669: sm.replaceField(_to, this , field);
670: }
671:
672: public void storeLongField(int field, long curVal) {
673: super .storeLongField(field, curVal);
674: sm.replaceField(_to, this , field);
675: }
676:
677: public void storeShortField(int field, short curVal) {
678: super .storeShortField(field, curVal);
679: sm.replaceField(_to, this , field);
680: }
681:
682: public void storeStringField(int field, String curVal) {
683: super .storeStringField(field, curVal);
684: sm.replaceField(_to, this , field);
685: }
686:
687: public void storeObjectField(int field, Object curVal) {
688: super .storeObjectField(field, detachField(curVal, field));
689: sm.replaceField(_to, this , field);
690: }
691:
692: /**
693: * Set the owner of the field's proxy to the detached state manager.
694: */
695: private Object reproxy(Object obj, int field) {
696: if (obj != null && _detSM != null && obj instanceof Proxy)
697: ((Proxy) obj).setOwner(_detSM, field);
698: return obj;
699: }
700:
701: /**
702: * Detach the given value if needed.
703: */
704: private Object detachField(Object curVal, int field) {
705: if (curVal == null)
706: return null;
707:
708: FieldMetaData fmd = sm.getMetaData().getField(field);
709: Object newVal = null;
710: switch (fmd.getDeclaredTypeCode()) {
711: case JavaTypes.ARRAY:
712: if (_copy)
713: newVal = _proxy.copyArray(curVal);
714: else
715: newVal = curVal;
716: detachArray(newVal, fmd);
717: return newVal;
718: case JavaTypes.COLLECTION:
719: if (_copy) {
720: if (_detSM != null) {
721: newVal = _proxy
722: .newCollectionProxy(
723: fmd.getProxyType(),
724: fmd.getElement()
725: .getDeclaredType(),
726: fmd.getInitializer() instanceof Comparator ? (Comparator) fmd
727: .getInitializer()
728: : null);
729: ((Collection) newVal)
730: .addAll((Collection) curVal);
731: } else
732: newVal = _proxy
733: .copyCollection((Collection) curVal);
734: } else
735: newVal = curVal;
736: detachCollection((Collection) newVal,
737: (Collection) curVal, fmd);
738: return reproxy(newVal, field);
739: case JavaTypes.MAP:
740: if (_copy) {
741: if (_detSM != null) {
742: newVal = _proxy
743: .newMapProxy(
744: fmd.getProxyType(),
745: fmd.getKey().getDeclaredType(),
746: fmd.getElement()
747: .getDeclaredType(),
748: fmd.getInitializer() instanceof Comparator ? (Comparator) fmd
749: .getInitializer()
750: : null);
751: ((Map) newVal).putAll((Map) curVal);
752: } else
753: newVal = _proxy.copyMap((Map) curVal);
754: } else
755: newVal = curVal;
756: detachMap((Map) newVal, (Map) curVal, fmd);
757: return reproxy(newVal, field);
758: case JavaTypes.CALENDAR:
759: newVal = (_copy) ? _proxy
760: .copyCalendar((Calendar) curVal) : curVal;
761: return reproxy(newVal, field);
762: case JavaTypes.DATE:
763: newVal = (_copy) ? _proxy.copyDate((Date) curVal)
764: : curVal;
765: return reproxy(newVal, field);
766: case JavaTypes.OBJECT:
767: if (_copy)
768: newVal = _proxy.copyCustom(curVal);
769: return reproxy((newVal == null) ? curVal : newVal,
770: field);
771: case JavaTypes.PC:
772: case JavaTypes.PC_UNTYPED:
773: return detachInternal(curVal);
774: default:
775: return curVal;
776: }
777: }
778:
779: /**
780: * Make sure all the values in the given array are detached.
781: */
782: private void detachArray(Object array, FieldMetaData fmd) {
783: if (!fmd.getElement().isDeclaredTypePC())
784: return;
785:
786: int len = Array.getLength(array);
787: for (int i = 0; i < len; i++)
788: Array
789: .set(array, i, detachInternal(Array.get(array,
790: i)));
791: }
792:
793: /**
794: * Make sure all the values in the given collection are detached.
795: */
796: private void detachCollection(Collection coll, Collection orig,
797: FieldMetaData fmd) {
798: // coll can be null if not copyable
799: if (_copy && coll == null)
800: throw new UserException(_loc.get("not-copyable", fmd));
801: if (!fmd.getElement().isDeclaredTypePC())
802: return;
803:
804: // unfortunately we have to clear the original and re-add to copy
805: if (_copy)
806: coll.clear();
807: Object detached;
808: for (Iterator itr = orig.iterator(); itr.hasNext();) {
809: detached = detachInternal(itr.next());
810: if (_copy)
811: coll.add(detached);
812: }
813: }
814:
815: /**
816: * Make sure all the values in the given map are detached.
817: */
818: private void detachMap(Map map, Map orig, FieldMetaData fmd) {
819: // map can be null if not copyable
820: if (_copy && map == null)
821: throw new UserException(_loc.get("not-copyable", fmd));
822: boolean keyPC = fmd.getKey().isDeclaredTypePC();
823: boolean valPC = fmd.getElement().isDeclaredTypePC();
824: if (!keyPC && !valPC)
825: return;
826:
827: // if we have to copy keys, just clear and re-add; otherwise
828: // we can use the entry set to reset the values only
829: Map.Entry entry;
830: if (!_copy || keyPC) {
831: if (_copy)
832: map.clear();
833: Object key, val;
834: for (Iterator itr = orig.entrySet().iterator(); itr
835: .hasNext();) {
836: entry = (Map.Entry) itr.next();
837: key = entry.getKey();
838: if (keyPC)
839: key = detachInternal(key);
840: val = entry.getValue();
841: if (valPC)
842: val = detachInternal(val);
843: if (_copy)
844: map.put(key, val);
845: }
846: } else {
847: for (Iterator itr = map.entrySet().iterator(); itr
848: .hasNext();) {
849: entry = (Map.Entry) itr.next();
850: entry.setValue(detachInternal(entry.getValue()));
851: }
852: }
853: }
854: }
855: }
|