1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.kernel;
20
21 import java.io.IOException;
22 import java.io.ObjectOutput;
23 import java.io.Serializable;
24 import java.sql.Timestamp;
25 import java.util.Arrays;
26 import java.util.Calendar;
27 import java.util.Collection;
28 import java.util.Date;
29 import java.util.Iterator;
30 import java.util.Map;
31
32 import org.apache.openjpa.lib.util.Localizer;
33 import org.apache.openjpa.meta.FieldMetaData;
34 import org.apache.openjpa.meta.JavaTypes;
35 import org.apache.openjpa.meta.ValueMetaData;
36 import org.apache.openjpa.util.ChangeTracker;
37 import org.apache.openjpa.util.Exceptions;
38 import org.apache.openjpa.util.InvalidStateException;
39 import org.apache.openjpa.util.MapChangeTracker;
40 import org.apache.openjpa.util.ObjectId;
41 import org.apache.openjpa.util.Proxies;
42 import org.apache.openjpa.util.Proxy;
43 import org.apache.openjpa.util.ProxyManager;
44 import org.apache.openjpa.util.UserException;
45
46 /**
47 * FieldManager type used to hold onto a single field value and then
48 * dispense it via the fetch methods. The manager can also perform actions
49 * on the held field.
50 *
51 * @author Abe White
52 */
53 class SingleFieldManager
54 extends TransferFieldManager
55 implements Serializable {
56
57 private static final Localizer _loc = Localizer.forPackage
58 (SingleFieldManager.class);
59
60 private final StateManagerImpl _sm;
61 private final BrokerImpl _broker;
62
63 public SingleFieldManager(StateManagerImpl sm, BrokerImpl broker) {
64 _sm = sm;
65 _broker = broker;
66 }
67
68 /**
69 * Proxy the held field if needed. Return true if the field needs to
70 * be replaced with the now-proxied instance.
71 */
72 public boolean proxy(boolean reset, boolean replaceNull) {
73 FieldMetaData fmd = _sm.getMetaData().getField(field);
74 Proxy proxy = null;
75 boolean ret = false;
76 switch (fmd.getDeclaredTypeCode()) {
77 case JavaTypes.DATE:
78 if (objval == null)
79 return false;
80 proxy = checkProxy();
81 if (proxy == null) {
82 proxy = (Proxy) _sm.newFieldProxy(field);
83 ((Date) proxy).setTime(((Date) objval).getTime());
84 if (proxy instanceof Timestamp
85 && objval instanceof Timestamp)
86 ((Timestamp) proxy).setNanos(((Timestamp) objval).
87 getNanos());
88 ret = true;
89 }
90 break;
91 case JavaTypes.CALENDAR:
92 if (objval == null)
93 return false;
94 proxy = checkProxy();
95 if (proxy == null) {
96 proxy = (Proxy) _sm.newFieldProxy(field);
97 ((Calendar) proxy).setTime(((Calendar) objval).getTime());
98 ret = true;
99 }
100 break;
101 case JavaTypes.COLLECTION:
102 if (objval == null && !replaceNull)
103 return false;
104 proxy = checkProxy();
105 if (proxy == null) {
106 proxy = (Proxy) _sm.newFieldProxy(field);
107 if (objval != null)
108 ((Collection) proxy).addAll((Collection) objval);
109 ret = true;
110 }
111 break;
112 case JavaTypes.MAP:
113 if (objval == null && !replaceNull)
114 return false;
115 proxy = checkProxy();
116 if (proxy == null) {
117 proxy = (Proxy) _sm.newFieldProxy(field);
118 if (objval != null)
119 ((Map) proxy).putAll((Map) objval);
120 ret = true;
121 }
122 break;
123 case JavaTypes.OBJECT:
124 if (objval == null)
125 return false;
126 proxy = checkProxy();
127 if (proxy == null) {
128 proxy = getProxyManager().newCustomProxy(objval);
129 ret = proxy != null;
130 }
131 break;
132 }
133
134 if (proxy != null) {
135 proxy.setOwner(_sm, field);
136 ChangeTracker tracker = proxy.getChangeTracker();
137 if (reset && tracker != null) {
138 if (fmd.getDeclaredTypeCode() == JavaTypes.MAP) {
139 // track values if key is derived from value, else keys
140 boolean keys = fmd.getKey().getValueMappedBy() == null;
141 ((MapChangeTracker) tracker).setTrackKeys(keys);
142 }
143 tracker.startTracking();
144 }
145 objval = proxy;
146 }
147 return ret;
148 }
149
150 /**
151 * If the current field is a usable proxy, return it; else return null.
152 */
153 private Proxy checkProxy() {
154 if (!(objval instanceof Proxy))
155 return null;
156
157 Proxy proxy = (Proxy) objval;
158 if (proxy.getOwner() == null || Proxies.isOwner(proxy, _sm, field))
159 return proxy;
160 return null;
161 }
162
163 /**
164 * Unproxies the current field if needed.
165 */
166 public void unproxy() {
167 if (objval == null)
168 return;
169
170 FieldMetaData fmd = _sm.getMetaData().getField(field);
171 switch (fmd.getDeclaredTypeCode()) {
172 case JavaTypes.COLLECTION:
173 case JavaTypes.MAP:
174 case JavaTypes.DATE:
175 case JavaTypes.OBJECT:
176 if (objval instanceof Proxy) {
177 Proxy proxy = (Proxy) objval;
178 proxy.setOwner(null, -1);
179 if (proxy.getChangeTracker() != null)
180 proxy.getChangeTracker().stopTracking();
181 }
182 }
183 }
184
185 /**
186 * Release the currently embedded field (make it transient).
187 */
188 public void releaseEmbedded() {
189 if (objval == null)
190 return;
191
192 FieldMetaData fmd = _sm.getMetaData().getField(field);
193 switch (fmd.getDeclaredTypeCode()) {
194 case JavaTypes.PC:
195 if (fmd.isEmbeddedPC())
196 releaseEmbedded(fmd, objval);
197 break;
198 case JavaTypes.ARRAY:
199 if (fmd.getElement().isEmbeddedPC())
200 releaseEmbedded(fmd.getElement(), (Object[]) objval);
201 break;
202 case JavaTypes.COLLECTION:
203 if (fmd.getElement().isEmbeddedPC())
204 releaseEmbedded(fmd.getElement(), (Collection) objval);
205 break;
206 case JavaTypes.MAP:
207 if (fmd.getKey().isEmbeddedPC())
208 releaseEmbedded(fmd.getKey(), ((Map) objval).keySet());
209 if (fmd.getElement().isEmbeddedPC())
210 releaseEmbedded(fmd.getElement(), ((Map) objval).values());
211 break;
212 }
213 }
214
215 /**
216 * Release the given embedded objects.
217 */
218 private void releaseEmbedded(ValueMetaData vmd, Object[] objs) {
219 for (int i = 0; i < objs.length; i++)
220 releaseEmbedded(vmd, objs[i]);
221 }
222
223 /**
224 * Release the given embedded objects.
225 */
226 private void releaseEmbedded(ValueMetaData vmd, Collection objs) {
227 for (Iterator itr = objs.iterator(); itr.hasNext();)
228 releaseEmbedded(vmd, itr.next());
229 }
230
231 /**
232 * Release the given embedd object.
233 */
234 private void releaseEmbedded(ValueMetaData vmd, Object obj) {
235 if (obj == null)
236 return;
237
238 StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
239 if (sm != null && sm.getOwner() == _sm
240 && sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())
241 sm.release(true);
242 }
243
244 /**
245 * Persist the stored field safely, preventing infinite recursion using
246 * the given set of already-persisted objects. This method is only called
247 * for fields that we know have cascade-immediate settings.
248 */
249 public void persist(OpCallbacks call) {
250 if (objval == null)
251 return;
252
253 FieldMetaData fmd = _sm.getMetaData().getField(field);
254 switch (fmd.getDeclaredTypeCode()) {
255 case JavaTypes.PC:
256 case JavaTypes.PC_UNTYPED:
257 if (!_broker.isDetachedNew() && _broker.isDetached(objval))
258 return; // allow but ignore
259 _broker.persist(objval, true, call);
260 break;
261 case JavaTypes.ARRAY:
262 _broker.persistAll(Arrays.asList((Object[]) objval), true,
263 call);
264 break;
265 case JavaTypes.COLLECTION:
266 _broker.persistAll((Collection) objval, true, call);
267 break;
268 case JavaTypes.MAP:
269 if (fmd.getKey().getCascadePersist()
270 == ValueMetaData.CASCADE_IMMEDIATE)
271 _broker.persistAll(((Map) objval).keySet(), true, call);
272 if (fmd.getElement().getCascadePersist()
273 == ValueMetaData.CASCADE_IMMEDIATE)
274 _broker.persistAll(((Map) objval).values(), true, call);
275 break;
276 }
277 }
278
279 /**
280 * Delete and/or dereference field values.
281 */
282 public void delete(OpCallbacks call) {
283 delete(true, call);
284 }
285
286 /**
287 * Dereference field values.
288 */
289 public void dereferenceDependent() {
290 delete(false, null);
291 }
292
293 /**
294 * Delete or dereference the stored field as necessary.
295 */
296 private void delete(boolean immediate, OpCallbacks call) {
297 if (objval == null)
298 return;
299
300 FieldMetaData fmd = _sm.getMetaData().getField(field);
301 if (fmd.getCascadeDelete() != ValueMetaData.CASCADE_NONE) {
302 // immediate cascade works on field value; dependent deref
303 // works on external value
304 if ((immediate || fmd.isEmbeddedPC())
305 && fmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE)
306 delete(fmd, objval, call);
307 else if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO)
308 dereferenceDependent(fmd.getExternalValue(objval, _broker));
309 return;
310 }
311
312 Object external = null;
313 ValueMetaData vmd = fmd.getKey();
314 if ((immediate || vmd.isEmbeddedPC())
315 && vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE)
316 delete(vmd, ((Map) objval).keySet(), call);
317 else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) {
318 external = fmd.getExternalValue(objval, _broker);
319 if (external == null)
320 return;
321 dereferenceDependent(((Map) external).keySet());
322 }
323
324 vmd = fmd.getElement();
325 if ((immediate || vmd.isEmbeddedPC())
326 && vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
327 switch (fmd.getDeclaredTypeCode()) {
328 case JavaTypes.COLLECTION:
329 delete(vmd, (Collection) objval, call);
330 break;
331 case JavaTypes.ARRAY:
332 delete(vmd, (Object[]) objval, call);
333 break;
334 case JavaTypes.MAP:
335 delete(vmd, ((Map) objval).values(), call);
336 break;
337 }
338 } else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) {
339 if (external == null) {
340 external = fmd.getExternalValue(objval, _broker);
341 if (external == null)
342 return;
343 }
344
345 switch (fmd.getTypeCode()) {
346 case JavaTypes.COLLECTION:
347 dereferenceDependent((Collection) external);
348 break;
349 case JavaTypes.ARRAY:
350 dereferenceDependent((Object[]) external);
351 break;
352 case JavaTypes.MAP:
353 dereferenceDependent(((Map) external).values());
354 break;
355 }
356 }
357 }
358
359 /**
360 * Delete the objects in the given value.
361 */
362 private void delete(ValueMetaData vmd, Object[] objs, OpCallbacks call) {
363 for (int i = 0; i < objs.length; i++)
364 delete(vmd, objs[i], call);
365 }
366
367 /**
368 * Delete the objects embedded in the given value.
369 */
370 private void delete(ValueMetaData vmd, Collection objs, OpCallbacks call) {
371 for (Iterator itr = objs.iterator(); itr.hasNext();)
372 delete(vmd, itr.next(), call);
373 }
374
375 /**
376 * Delete an object embedded in the given value.
377 */
378 void delete(ValueMetaData vmd, Object obj, OpCallbacks call) {
379 if (obj == null)
380 return;
381
382 // delete if unknowned or this isn't an embedded field or if owned by us
383 StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
384 if (sm != null && (sm.getOwner() == null || !vmd.isEmbeddedPC()
385 || (sm.getOwner() == _sm
386 && sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())))
387 _broker.delete(sm.getManagedInstance(), sm, call);
388 }
389
390 /**
391 * Dereference all valid persistent objects in the given collection.
392 */
393 private void dereferenceDependent(Object[] objs) {
394 for (int i = 0; i < objs.length; i++)
395 dereferenceDependent(objs[i]);
396 }
397
398 /**
399 * Dereference all valid persistent objects in the given collection.
400 */
401 private void dereferenceDependent(Collection objs) {
402 for (Iterator itr = objs.iterator(); itr.hasNext();)
403 dereferenceDependent(itr.next());
404 }
405
406 /**
407 * Dereference the given object.
408 */
409 void dereferenceDependent(Object obj) {
410 if (obj == null)
411 return;
412 StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
413 if (sm != null)
414 sm.setDereferencedDependent(true, true);
415 }
416
417 /**
418 * Recursively invoke the broker to gather cascade-refresh objects in
419 * the current field into the given set. This method is only called
420 * for fields that we know have cascade-refresh settings.
421 */
422 public void gatherCascadeRefresh(OpCallbacks call) {
423 if (objval == null)
424 return;
425
426 FieldMetaData fmd = _sm.getMetaData().getField(field);
427 switch (fmd.getDeclaredTypeCode()) {
428 case JavaTypes.PC:
429 case JavaTypes.PC_UNTYPED:
430 _broker.gatherCascadeRefresh(objval, call);
431 break;
432 case JavaTypes.ARRAY:
433 gatherCascadeRefresh((Object[]) objval, call);
434 break;
435 case JavaTypes.COLLECTION:
436 gatherCascadeRefresh((Collection) objval, call);
437 break;
438 case JavaTypes.MAP:
439 if (fmd.getKey().getCascadeRefresh()
440 == ValueMetaData.CASCADE_IMMEDIATE)
441 gatherCascadeRefresh(((Map) objval).keySet(), call);
442 if (fmd.getElement().getCascadeRefresh()
443 == ValueMetaData.CASCADE_IMMEDIATE)
444 gatherCascadeRefresh(((Map) objval).values(), call);
445 break;
446 }
447 }
448
449 /**
450 * Gather each element.
451 */
452 private void gatherCascadeRefresh(Object[] arr, OpCallbacks call) {
453 for (int i = 0; i < arr.length; i++)
454 _broker.gatherCascadeRefresh(arr[i], call);
455 }
456
457 /**
458 * Gather each element.
459 */
460 private void gatherCascadeRefresh(Collection coll, OpCallbacks call) {
461 for (Iterator itr = coll.iterator(); itr.hasNext();)
462 _broker.gatherCascadeRefresh(itr.next(), call);
463 }
464
465 /**
466 * Perform pre-flush tasks on the current field. This includes checking
467 * for nulls, persisting pcs, embedding embedded fields, and ref'ing
468 * pc fields. Return true if the field needs to be replaced with the
469 * new value.
470 */
471 public boolean preFlush(boolean logical, OpCallbacks call) {
472 // only care about object fields
473 FieldMetaData fmd = _sm.getMetaData().getField(field);
474 if (fmd.getDeclaredTypeCode() < JavaTypes.OBJECT)
475 return false;
476
477 // perform pers-by-reach and dependent refs
478 boolean ret = preFlush(fmd, logical, call);
479
480 // manage inverses
481 InverseManager manager = _broker.getInverseManager();
482 if (manager != null)
483 manager.correctRelations(_sm, fmd, objval);
484 return ret;
485 }
486
487 /**
488 * Return true if the last-provided field has a default value.
489 */
490 public boolean isDefaultValue() {
491 return dblval == 0 && longval == 0
492 && (objval == null || "".equals(objval));
493 }
494
495 /**
496 * Write the stored field or its default value to the given stream.
497 */
498 public void serialize(ObjectOutput out, boolean def)
499 throws IOException {
500 FieldMetaData fmd = _sm.getMetaData().getField(field);
501 switch (fmd.getDeclaredTypeCode()) {
502 case JavaTypes.BOOLEAN:
503 out.writeBoolean(!def && longval == 1);
504 break;
505 case JavaTypes.BYTE:
506 out.writeByte((def) ? (byte) 0 : (byte) longval);
507 break;
508 case JavaTypes.CHAR:
509 out.writeChar((def) ? (char) 0 : (char) longval);
510 break;
511 case JavaTypes.DOUBLE:
512 out.writeDouble((def) ? 0D : dblval);
513 break;
514 case JavaTypes.FLOAT:
515 out.writeFloat((def) ? 0F : (float) dblval);
516 break;
517 case JavaTypes.INT:
518 out.writeInt((def) ? 0 : (int) longval);
519 break;
520 case JavaTypes.LONG:
521 out.writeLong((def) ? 0L : longval);
522 break;
523 case JavaTypes.SHORT:
524 out.writeShort((def) ? (short) 0 : (short) longval);
525 break;
526 default:
527 out.writeObject((def) ? null : objval);
528 }
529 }
530
531 /**
532 * Helper method to perform pre flush actions on the current object.
533 */
534 private boolean preFlush(FieldMetaData fmd, boolean logical,
535 OpCallbacks call) {
536 // check for illegal nulls
537 if (objval == null) {
538 if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION
539 || fmd.getDeclaredTypeCode() == JavaTypes.OID)
540 throw new InvalidStateException(_loc.get("null-value",
541 fmd.getName(), _sm.getManagedInstance())).
542 setFatal(true);
543 return false;
544 }
545
546 // nothing else to do for non-persistent
547 if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
548 return false;
549
550 // don't allow managed objectid field value
551 if (fmd.getDeclaredTypeCode() == JavaTypes.OID) {
552 _sm.assertNotManagedObjectId(objval);
553 if (_sm.getObjectId() != null
554 && !objval.equals(((ObjectId) _sm.getObjectId()).getId()))
555 throw new InvalidStateException(_loc.get("changed-oid",
556 _sm.getObjectId(), objval,
557 Exceptions.toString(_sm.getManagedInstance()))).
558 setFatal(true);
559 }
560
561 // check for pcs in field value
562 if (preFlush(fmd, fmd.getDeclaredTypeCode(),
563 fmd.getKey().getDeclaredTypeCode(),
564 fmd.getElement().getDeclaredTypeCode(), false, logical, call))
565 return true;
566
567 // also check for pcs in externalized values
568 if (fmd.isExternalized())
569 preFlush(fmd, fmd.getTypeCode(), fmd.getKey().getTypeCode(),
570 fmd.getElement().getTypeCode(), true, logical, call);
571 return false;
572 }
573
574 /**
575 * Make new objects persistent and ref other objects so referenced
576 * dependent objects won't be deleted.
577 */
578 private boolean preFlush(FieldMetaData fmd, int type, int keyType,
579 int elemType, boolean external, boolean logical, OpCallbacks call) {
580 Object val = objval;
581 if (val == null)
582 return false;
583
584 boolean copy = false;
585 switch (type) {
586 case JavaTypes.PC:
587 if (fmd.isEmbeddedPC()) {
588 objval = embed(fmd, val);
589 copy = true;
590 } else {
591 if (external)
592 val = fmd.getExternalValue(val, _broker);
593 if (val != null)
594 preFlushPC(fmd, val, logical, call);
595 }
596 break;
597 case JavaTypes.PC_UNTYPED:
598 if (external)
599 val = fmd.getExternalValue(val, _broker);
600 if (val != null)
601 preFlushPC(fmd, val, logical, call);
602 break;
603 case JavaTypes.ARRAY:
604 if (fmd.getElement().isEmbeddedPC())
605 embed(fmd.getElement(), (Object[]) val);
606 else if (elemType == JavaTypes.PC
607 || elemType == JavaTypes.PC_UNTYPED) {
608 if (external)
609 val = fmd.getExternalValue(val, _broker);
610 if (val != null)
611 preFlushPCs(fmd.getElement(), (Object[]) val, logical,
612 call);
613 }
614 break;
615 case JavaTypes.COLLECTION:
616 if (fmd.getElement().isEmbeddedPC()) {
617 objval = embed(fmd.getElement(), (Collection) val);
618 copy = true;
619 } else if (elemType == JavaTypes.PC
620 || elemType == JavaTypes.PC_UNTYPED) {
621 boolean flushed = false;
622 if (external)
623 val = fmd.getExternalValue(val, _broker);
624 else if (val instanceof Proxy) {
625 // shortcut change trackers; also ensures we don't
626 // iterate lrs fields
627 ChangeTracker ct = ((Proxy) val).getChangeTracker();
628 if (ct != null && ct.isTracking()) {
629 preFlushPCs(fmd.getElement(), ct.getAdded(),
630 logical, call);
631 preFlushPCs(fmd.getElement(), ct.getChanged(),
632 logical, call);
633 flushed = true;
634 }
635 }
636 if (!flushed && val != null)
637 preFlushPCs(fmd.getElement(), (Collection) val, logical,
638 call);
639 }
640 break;
641 case JavaTypes.MAP:
642 boolean keyEmbed = fmd.getKey().isEmbeddedPC();
643 boolean valEmbed = fmd.getElement().isEmbeddedPC();
644 if (keyEmbed || valEmbed) {
645 objval = embed(fmd, (Map) val, keyEmbed, valEmbed);
646 copy = keyEmbed;
647 }
648
649 if (!keyEmbed && (keyType == JavaTypes.PC
650 || keyType == JavaTypes.PC_UNTYPED)) {
651 boolean flushed = false;
652 if (external) {
653 val = fmd.getExternalValue(val, _broker);
654 external = false;
655 } else if (val instanceof Proxy) {
656 // shortcut change trackers; also ensures we don't
657 // iterate lrs fields
658 MapChangeTracker ct = (MapChangeTracker) ((Proxy) val).
659 getChangeTracker();
660 if (ct != null && ct.isTracking() && ct.getTrackKeys())
661 {
662 preFlushPCs(fmd.getKey(), ct.getAdded(), logical,
663 call);
664 preFlushPCs(fmd.getKey(), ct.getChanged(), logical,
665 call);
666 flushed = true;
667 }
668 }
669 if (!flushed && val != null)
670 preFlushPCs(fmd.getKey(), ((Map) val).keySet(), logical,
671 call);
672 }
673
674 if (!valEmbed && (elemType == JavaTypes.PC
675 || elemType == JavaTypes.PC_UNTYPED)) {
676 boolean flushed = false;
677 if (external)
678 val = fmd.getExternalValue(val, _broker);
679 else if (val instanceof Proxy) {
680 // shortcut change trackers; also ensures we don't
681 // iterate lrs fields
682 MapChangeTracker ct = (MapChangeTracker) ((Proxy) val).
683 getChangeTracker();
684 if (ct != null && ct.isTracking()) {
685 if (ct.getTrackKeys()) {
686 preFlushPCs(fmd.getElement(), ct.getAdded(),
687 (Map) val, logical, call);
688 preFlushPCs(fmd.getElement(), ct.getChanged(),
689 (Map) val, logical, call);
690 } else {
691 preFlushPCs(fmd.getElement(), ct.getAdded(),
692 logical, call);
693 preFlushPCs(fmd.getElement(), ct.getChanged(),
694 logical, call);
695 }
696 flushed = true;
697 }
698 }
699 if (!flushed && val != null)
700 preFlushPCs(fmd.getElement(), ((Map) val).values(),
701 logical, call);
702 }
703 break;
704 }
705 return copy;
706 }
707
708 /**
709 * Make new objects persistent and ref all valid persistent objects for
710 * the given keys.
711 */
712 private void preFlushPCs(ValueMetaData vmd, Collection keys, Map map,
713 boolean logical, OpCallbacks call) {
714 for (Iterator itr = keys.iterator(); itr.hasNext();)
715 preFlushPC(vmd, map.get(itr.next()), logical, call);
716 }
717
718 /**
719 * Make new objects persistent and ref all valid persistent objects in
720 * the given array.
721 */
722 private void preFlushPCs(ValueMetaData vmd, Object[] objs,
723 boolean logical, OpCallbacks call) {
724 for (int i = 0; i < objs.length; i++)
725 preFlushPC(vmd, objs[i], logical, call);
726 }
727
728 /**
729 * Make new objects persistent and ref all valid persistent objects in
730 * the given collection.
731 */
732 private void preFlushPCs(ValueMetaData vmd, Collection objs,
733 boolean logical, OpCallbacks call) {
734 for (Iterator itr = objs.iterator(); itr.hasNext();)
735 preFlushPC(vmd, itr.next(), logical, call);
736 }
737
738 /**
739 * Perform pre flush operations on the given object.
740 */
741 private void preFlushPC(ValueMetaData vmd, Object obj, boolean logical,
742 OpCallbacks call) {
743 if (obj == null)
744 return;
745
746 OpenJPAStateManager sm;
747 if (vmd.getCascadePersist() == ValueMetaData.CASCADE_NONE) {
748 if (!_broker.isDetachedNew() && _broker.isDetached(obj))
749 return; // allow but ignore
750
751 sm = _broker.getStateManager(obj);
752 if (sm == null || !sm.isPersistent())
753 throw new InvalidStateException(
754 _loc.get("cant-cascade-persist", vmd))
755 .setFailedObject(obj);
756 } else {
757 sm = _broker.getStateManager(obj);
758 if (sm == null || !sm.isProvisional())
759 sm = _broker.persist(obj, null, true, call);
760 }
761
762 if (sm != null) {
763 // if deleted and not managed inverse, die
764 if (sm.isDeleted() && (_broker.getInverseManager() == null
765 || vmd.getFieldMetaData().getInverseMetaDatas().length == 0))
766 throw new UserException(_loc.get("ref-to-deleted",
767 Exceptions.toString(obj), vmd,
768 Exceptions.toString(_sm.getManagedInstance()))).
769 setFailedObject(obj);
770
771 StateManagerImpl smimpl = (StateManagerImpl) sm;
772 smimpl.nonprovisional(logical, call);
773 smimpl.setDereferencedDependent(false, true);
774 }
775 }
776
777 /**
778 * Make all elements of the given array embedded.
779 */
780 private void embed(ValueMetaData vmd, Object[] arr) {
781 for (int i = 0; i < arr.length; i++)
782 arr[i] = embed(vmd, arr[i]);
783 }
784
785 /**
786 * Create a copy of the given collection containing embedded elements.
787 */
788 private Collection embed(ValueMetaData vmd, Collection orig) {
789 // we have to copy to get a collection of the right type and size,
790 // though we immediately clear it
791 Collection coll = getProxyManager().copyCollection(orig);
792 if (coll == null)
793 throw new UserException(_loc.get("not-copyable",
794 vmd.getFieldMetaData()));
795
796 coll.clear();
797 for (Iterator itr = orig.iterator(); itr.hasNext();)
798 coll.add(embed(vmd, itr.next()));
799 return coll;
800 }
801
802 /**
803 * Embed the elements of the given map.
804 */
805 private Map embed(FieldMetaData fmd, Map orig, boolean keyEmbed,
806 boolean valEmbed) {
807 Map map;
808 Map.Entry entry;
809
810 // if we have to replace keys, we need to copy the map; otherwise
811 // we can mutate the values directly
812 if (keyEmbed) {
813 // we have to copy to get a collection of the right type and size,
814 // though we immediately clear it
815 map = getProxyManager().copyMap(orig);
816 if (map == null)
817 throw new UserException(_loc.get("not-copyable", fmd));
818
819 map.clear();
820 Object key, val;
821 for (Iterator itr = orig.entrySet().iterator(); itr.hasNext();) {
822 entry = (Map.Entry) itr.next();
823 key = embed(fmd.getKey(), entry.getKey());
824 val = entry.getValue();
825 if (valEmbed)
826 val = embed(fmd.getElement(), val);
827 map.put(key, val);
828 }
829 } else {
830 map = orig;
831 for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
832 entry = (Map.Entry) itr.next();
833 entry.setValue(embed(fmd.getElement(),
834 entry.getValue()));
835 }
836 }
837 return map;
838 }
839
840 /**
841 * Make the given object embedded.
842 */
843 private Object embed(ValueMetaData vmd, Object obj) {
844 if (obj == null)
845 return null;
846 return _broker.embed(obj, null, _sm, vmd).getManagedInstance();
847 }
848
849 /**
850 * Return the proxy manager.
851 */
852 private ProxyManager getProxyManager ()
853 {
854 return _broker.getConfiguration ().getProxyManagerInstance ();
855 }
856 }