blob: d236d3255eb97fe5a1a77c66259ca9cf56ab4dce [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.beans.beancontext;
27
28import java.awt.Component;
29import java.awt.Container;
30
31import java.beans.Beans;
32import java.beans.AppletInitializer;
33
34import java.beans.DesignMode;
35
36import java.beans.PropertyChangeEvent;
37import java.beans.PropertyChangeListener;
38import java.beans.PropertyChangeSupport;
39
40import java.beans.VetoableChangeListener;
41import java.beans.VetoableChangeSupport;
42import java.beans.PropertyVetoException;
43
44import java.beans.Visibility;
45
46import java.io.IOException;
47import java.io.InputStream;
48import java.io.ObjectInputStream;
49import java.io.ObjectOutputStream;
50import java.io.Serializable;
51
52import java.net.URL;
53
54import java.util.ArrayList;
55import java.util.Collection;
56import java.util.HashMap;
57import java.util.Iterator;
58import java.util.Locale;
59import java.util.Map;
60
61
62/**
63 * This helper class provides a utility implementation of the
64 * java.beans.beancontext.BeanContext interface.
65 * </p>
66 * <p>
67 * Since this class directly implements the BeanContext interface, the class
68 * can, and is intended to be used either by subclassing this implementation,
69 * or via ad-hoc delegation of an instance of this class from another.
70 * </p>
71 *
72 * @author Laurence P. G. Cable
73 * @since 1.2
74 */
75public class BeanContextSupport extends BeanContextChildSupport
76 implements BeanContext,
77 Serializable,
78 PropertyChangeListener,
79 VetoableChangeListener {
80
81 // Fix for bug 4282900 to pass JCK regression test
82 static final long serialVersionUID = -4879613978649577204L;
83
84 /**
85 *
86 * Construct a BeanContextSupport instance
87 *
88 *
89 * @param peer The peer <tt>BeanContext</tt> we are
90 * supplying an implementation for,
91 * or <tt>null</tt>
92 * if this object is its own peer
93 * @param lcle The current Locale for this BeanContext. If
94 * <tt>lcle</tt> is <tt>null</tt>, the default locale
95 * is assigned to the <tt>BeanContext</tt> instance.
96 * @param dTime The initial state,
97 * <tt>true</tt> if in design mode,
98 * <tt>false</tt> if runtime.
99 * @param visible The initial visibility.
100 * @see java.util.Locale#getDefault()
101 * @see java.util.Locale#setDefault(java.util.Locale)
102 */
103 public BeanContextSupport(BeanContext peer, Locale lcle, boolean dTime, boolean visible) {
104 super(peer);
105
106 locale = lcle != null ? lcle : Locale.getDefault();
107 designTime = dTime;
108 okToUseGui = visible;
109
110 initialize();
111 }
112
113 /**
114 * Create an instance using the specified Locale and design mode.
115 *
116 * @param peer The peer <tt>BeanContext</tt> we
117 * are supplying an implementation for,
118 * or <tt>null</tt> if this object is its own peer
119 * @param lcle The current Locale for this <tt>BeanContext</tt>. If
120 * <tt>lcle</tt> is <tt>null</tt>, the default locale
121 * is assigned to the <tt>BeanContext</tt> instance.
122 * @param dtime The initial state, <tt>true</tt>
123 * if in design mode,
124 * <tt>false</tt> if runtime.
125 * @see java.util.Locale#getDefault()
126 * @see java.util.Locale#setDefault(java.util.Locale)
127 */
128 public BeanContextSupport(BeanContext peer, Locale lcle, boolean dtime) {
129 this (peer, lcle, dtime, true);
130 }
131
132 /**
133 * Create an instance using the specified locale
134 *
135 * @param peer The peer BeanContext we are
136 * supplying an implementation for,
137 * or <tt>null</tt> if this object
138 * is its own peer
139 * @param lcle The current Locale for this
140 * <tt>BeanContext</tt>. If
141 * <tt>lcle</tt> is <tt>null</tt>,
142 * the default locale
143 * is assigned to the <tt>BeanContext</tt>
144 * instance.
145 * @see java.util.Locale#getDefault()
146 * @see java.util.Locale#setDefault(java.util.Locale)
147 */
148 public BeanContextSupport(BeanContext peer, Locale lcle) {
149 this (peer, lcle, false, true);
150 }
151
152 /**
153 * Create an instance using with a default locale
154 *
155 * @param peer The peer <tt>BeanContext</tt> we are
156 * supplying an implementation for,
157 * or <tt>null</tt> if this object
158 * is its own peer
159 */
160 public BeanContextSupport(BeanContext peer) {
161 this (peer, null, false, true);
162 }
163
164 /**
165 * Create an instance that is not a delegate of another object
166 */
167
168 public BeanContextSupport() {
169 this (null, null, false, true);
170 }
171
172 /**
173 * Gets the instance of <tt>BeanContext</tt> that
174 * this object is providing the implementation for.
175 * @return the BeanContext instance
176 */
177 public BeanContext getBeanContextPeer() { return (BeanContext)getBeanContextChildPeer(); }
178
179 /**
180 * <p>
181 * The instantiateChild method is a convenience hook
182 * in BeanContext to simplify
183 * the task of instantiating a Bean, nested,
184 * into a <tt>BeanContext</tt>.
185 * </p>
186 * <p>
187 * The semantics of the beanName parameter are defined by java.beans.Beans.instantate.
188 * </p>
189 *
190 * @param beanName the name of the Bean to instantiate within this BeanContext
191 * @throws IOException if there is an I/O error when the bean is being deserialized
192 * @throws ClassNotFoundException if the class
193 * identified by the beanName parameter is not found
194 * @return the new object
195 */
196 public Object instantiateChild(String beanName)
197 throws IOException, ClassNotFoundException {
198 BeanContext bc = getBeanContextPeer();
199
200 return Beans.instantiate(bc.getClass().getClassLoader(), beanName, bc);
201 }
202
203 /**
204 * Gets the number of children currently nested in
205 * this BeanContext.
206 *
207 * @return number of children
208 */
209 public int size() {
210 synchronized(children) {
211 return children.size();
212 }
213 }
214
215 /**
216 * Reports whether or not this
217 * <tt>BeanContext</tt> is empty.
218 * A <tt>BeanContext</tt> is considered
219 * empty when it contains zero
220 * nested children.
221 * @return if there are not children
222 */
223 public boolean isEmpty() {
224 synchronized(children) {
225 return children.isEmpty();
226 }
227 }
228
229 /**
230 * Determines whether or not the specified object
231 * is currently a child of this <tt>BeanContext</tt>.
232 * @param o the Object in question
233 * @return if this object is a child
234 */
235 public boolean contains(Object o) {
236 synchronized(children) {
237 return children.containsKey(o);
238 }
239 }
240
241 /**
242 * Determines whether or not the specified object
243 * is currently a child of this <tt>BeanContext</tt>.
244 * @param o the Object in question
245 * @return if this object is a child
246 */
247 public boolean containsKey(Object o) {
248 synchronized(children) {
249 return children.containsKey(o);
250 }
251 }
252
253 /**
254 * Gets all JavaBean or <tt>BeanContext</tt> instances
255 * currently nested in this <tt>BeanContext</tt>.
256 * @return an <tt>Iterator</tt> of the nested children
257 */
258 public Iterator iterator() {
259 synchronized(children) {
260 return new BCSIterator(children.keySet().iterator());
261 }
262 }
263
264 /**
265 * Gets all JavaBean or <tt>BeanContext</tt>
266 * instances currently nested in this BeanContext.
267 */
268 public Object[] toArray() {
269 synchronized(children) {
270 return children.keySet().toArray();
271 }
272 }
273
274 /**
275 * Gets an array containing all children of
276 * this <tt>BeanContext</tt> that match
277 * the types contained in arry.
278 * @param arry The array of object
279 * types that are of interest.
280 * @return an array of children
281 */
282 public Object[] toArray(Object[] arry) {
283 synchronized(children) {
284 return children.keySet().toArray(arry);
285 }
286 }
287
288
289 /************************************************************************/
290
291 /**
292 * protected final subclass that encapsulates an iterator but implements
293 * a noop remove() method.
294 */
295
296 protected static final class BCSIterator implements Iterator {
297 BCSIterator(Iterator i) { super(); src = i; }
298
299 public boolean hasNext() { return src.hasNext(); }
300 public Object next() { return src.next(); }
301 public void remove() { /* do nothing */ }
302
303 private Iterator src;
304 }
305
306 /************************************************************************/
307
308 /*
309 * protected nested class containing per child information, an instance
310 * of which is associated with each child in the "children" hashtable.
311 * subclasses can extend this class to include their own per-child state.
312 *
313 * Note that this 'value' is serialized with the corresponding child 'key'
314 * when the BeanContextSupport is serialized.
315 */
316
317 protected class BCSChild implements Serializable {
318
319 private static final long serialVersionUID = -5815286101609939109L;
320
321 BCSChild(Object bcc, Object peer) {
322 super();
323
324 child = bcc;
325 proxyPeer = peer;
326 }
327
328 Object getChild() { return child; }
329
330 void setRemovePending(boolean v) { removePending = v; }
331
332 boolean isRemovePending() { return removePending; }
333
334 boolean isProxyPeer() { return proxyPeer != null; }
335
336 Object getProxyPeer() { return proxyPeer; }
337 /*
338 * fields
339 */
340
341
342 private Object child;
343 private Object proxyPeer;
344
345 private transient boolean removePending;
346 }
347
348 /**
349 * <p>
350 * Subclasses can override this method to insert their own subclass
351 * of Child without having to override add() or the other Collection
352 * methods that add children to the set.
353 * </p>
354 *
355 * @param targetChild the child to create the Child on behalf of
356 * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
357 */
358
359 protected BCSChild createBCSChild(Object targetChild, Object peer) {
360 return new BCSChild(targetChild, peer);
361 }
362
363 /************************************************************************/
364
365 /**
366 * Adds/nests a child within this <tt>BeanContext</tt>.
367 * <p>
368 * Invoked as a side effect of java.beans.Beans.instantiate().
369 * If the child object is not valid for adding then this method
370 * throws an IllegalStateException.
371 * </p>
372 *
373 *
374 * @param targetChild The child objects to nest
375 * within this <tt>BeanContext</tt>
376 * @return true if the child was added successfully.
377 * @see #validatePendingAdd
378 */
379 public boolean add(Object targetChild) {
380
381 if (targetChild == null) throw new IllegalArgumentException();
382
383 // The specification requires that we do nothing if the child
384 // is already nested herein.
385
386 if (children.containsKey(targetChild)) return false; // test before locking
387
388 synchronized(BeanContext.globalHierarchyLock) {
389 if (children.containsKey(targetChild)) return false; // check again
390
391 if (!validatePendingAdd(targetChild)) {
392 throw new IllegalStateException();
393 }
394
395
396 // The specification requires that we invoke setBeanContext() on the
397 // newly added child if it implements the java.beans.beancontext.BeanContextChild interface
398
399 BeanContextChild cbcc = getChildBeanContextChild(targetChild);
400 BeanContextChild bccp = null;
401
402 synchronized(targetChild) {
403
404 if (targetChild instanceof BeanContextProxy) {
405 bccp = ((BeanContextProxy)targetChild).getBeanContextProxy();
406
407 if (bccp == null) throw new NullPointerException("BeanContextPeer.getBeanContextProxy()");
408 }
409
410 BCSChild bcsc = createBCSChild(targetChild, bccp);
411 BCSChild pbcsc = null;
412
413 synchronized (children) {
414 children.put(targetChild, bcsc);
415
416 if (bccp != null) children.put(bccp, pbcsc = createBCSChild(bccp, targetChild));
417 }
418
419 if (cbcc != null) synchronized(cbcc) {
420 try {
421 cbcc.setBeanContext(getBeanContextPeer());
422 } catch (PropertyVetoException pve) {
423
424 synchronized (children) {
425 children.remove(targetChild);
426
427 if (bccp != null) children.remove(bccp);
428 }
429
430 throw new IllegalStateException();
431 }
432
433 cbcc.addPropertyChangeListener("beanContext", childPCL);
434 cbcc.addVetoableChangeListener("beanContext", childVCL);
435 }
436
437 Visibility v = getChildVisibility(targetChild);
438
439 if (v != null) {
440 if (okToUseGui)
441 v.okToUseGui();
442 else
443 v.dontUseGui();
444 }
445
446 if (getChildSerializable(targetChild) != null) serializable++;
447
448 childJustAddedHook(targetChild, bcsc);
449
450 if (bccp != null) {
451 v = getChildVisibility(bccp);
452
453 if (v != null) {
454 if (okToUseGui)
455 v.okToUseGui();
456 else
457 v.dontUseGui();
458 }
459
460 if (getChildSerializable(bccp) != null) serializable++;
461
462 childJustAddedHook(bccp, pbcsc);
463 }
464
465
466 }
467
468 // The specification requires that we fire a notification of the change
469
470 fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), bccp == null ? new Object[] { targetChild } : new Object[] { targetChild, bccp } ));
471
472 }
473
474 return true;
475 }
476
477 /**
478 * Removes a child from this BeanContext. If the child object is not
479 * for adding then this method throws an IllegalStateException.
480 * @param targetChild The child objects to remove
481 * @see #validatePendingRemove
482 */
483 public boolean remove(Object targetChild) {
484 return remove(targetChild, true);
485 }
486
487 /**
488 * internal remove used when removal caused by
489 * unexpected <tt>setBeanContext</tt> or
490 * by <tt>remove()</tt> invocation.
491 * @param targetChild the JavaBean, BeanContext, or Object to be removed
492 * @param callChildSetBC used to indicate that
493 * the child should be notified that it is no
494 * longer nested in this <tt>BeanContext</tt>.
495 */
496 protected boolean remove(Object targetChild, boolean callChildSetBC) {
497
498 if (targetChild == null) throw new IllegalArgumentException();
499
500 synchronized(BeanContext.globalHierarchyLock) {
501 if (!containsKey(targetChild)) return false;
502
503 if (!validatePendingRemove(targetChild)) {
504 throw new IllegalStateException();
505 }
506
507 BCSChild bcsc = (BCSChild)children.get(targetChild);
508 BCSChild pbcsc = null;
509 Object peer = null;
510
511 // we are required to notify the child that it is no longer nested here if
512 // it implements java.beans.beancontext.BeanContextChild
513
514 synchronized(targetChild) {
515 if (callChildSetBC) {
516 BeanContextChild cbcc = getChildBeanContextChild(targetChild);
517 if (cbcc != null) synchronized(cbcc) {
518 cbcc.removePropertyChangeListener("beanContext", childPCL);
519 cbcc.removeVetoableChangeListener("beanContext", childVCL);
520
521 try {
522 cbcc.setBeanContext(null);
523 } catch (PropertyVetoException pve1) {
524 cbcc.addPropertyChangeListener("beanContext", childPCL);
525 cbcc.addVetoableChangeListener("beanContext", childVCL);
526 throw new IllegalStateException();
527 }
528
529 }
530 }
531
532 synchronized (children) {
533 children.remove(targetChild);
534
535 if (bcsc.isProxyPeer()) {
536 pbcsc = (BCSChild)children.get(peer = bcsc.getProxyPeer());
537 children.remove(peer);
538 }
539 }
540
541 if (getChildSerializable(targetChild) != null) serializable--;
542
543 childJustRemovedHook(targetChild, bcsc);
544
545 if (peer != null) {
546 if (getChildSerializable(peer) != null) serializable--;
547
548 childJustRemovedHook(peer, pbcsc);
549 }
550 }
551
552 fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { targetChild } : new Object[] { targetChild, peer } ));
553
554 }
555
556 return true;
557 }
558
559 /**
560 * Tests to see if all objects in the
561 * specified <tt>Collection</tt> are children of
562 * this <tt>BeanContext</tt>.
563 * @param c the specified <tt>Collection</tt>
564 *
565 * @return <tt>true</tt> if all objects
566 * in the collection are children of
567 * this <tt>BeanContext</tt>, false if not.
568 */
569 public boolean containsAll(Collection c) {
570 synchronized(children) {
571 Iterator i = c.iterator();
572 while (i.hasNext())
573 if(!contains(i.next()))
574 return false;
575
576 return true;
577 }
578 }
579
580 /**
581 * add Collection to set of Children (Unsupported)
582 * implementations must synchronized on the hierarchy lock and "children" protected field
583 * @throws UnsupportedOperationException
584 */
585 public boolean addAll(Collection c) {
586 throw new UnsupportedOperationException();
587 }
588
589 /**
590 * remove all specified children (Unsupported)
591 * implementations must synchronized on the hierarchy lock and "children" protected field
592 * @throws UnsupportedOperationException
593 */
594 public boolean removeAll(Collection c) {
595 throw new UnsupportedOperationException();
596 }
597
598
599 /**
600 * retain only specified children (Unsupported)
601 * implementations must synchronized on the hierarchy lock and "children" protected field
602 * @throws UnsupportedOperationException
603 */
604 public boolean retainAll(Collection c) {
605 throw new UnsupportedOperationException();
606 }
607
608 /**
609 * clear the children (Unsupported)
610 * implementations must synchronized on the hierarchy lock and "children" protected field
611 * @throws UnsupportedOperationException
612 */
613 public void clear() {
614 throw new UnsupportedOperationException();
615 }
616
617 /**
618 * Adds a BeanContextMembershipListener
619 *
620 * @param bcml the BeanContextMembershipListener to add
621 * @throws NullPointerException
622 */
623
624 public void addBeanContextMembershipListener(BeanContextMembershipListener bcml) {
625 if (bcml == null) throw new NullPointerException("listener");
626
627 synchronized(bcmListeners) {
628 if (bcmListeners.contains(bcml))
629 return;
630 else
631 bcmListeners.add(bcml);
632 }
633 }
634
635 /**
636 * Removes a BeanContextMembershipListener
637 *
638 * @param bcml the BeanContextMembershipListener to remove
639 * @throws NullPointerException
640 */
641
642 public void removeBeanContextMembershipListener(BeanContextMembershipListener bcml) {
643 if (bcml == null) throw new NullPointerException("listener");
644
645 synchronized(bcmListeners) {
646 if (!bcmListeners.contains(bcml))
647 return;
648 else
649 bcmListeners.remove(bcml);
650 }
651 }
652
653 /**
654 * @param name the name of the resource requested.
655 * @param bcc the child object making the request.
656 *
657 * @return the requested resource as an InputStream
658 * @throws NullPointerException
659 */
660
661 public InputStream getResourceAsStream(String name, BeanContextChild bcc) {
662 if (name == null) throw new NullPointerException("name");
663 if (bcc == null) throw new NullPointerException("bcc");
664
665 if (containsKey(bcc)) {
666 ClassLoader cl = bcc.getClass().getClassLoader();
667
668 return cl != null ? cl.getResourceAsStream(name)
669 : ClassLoader.getSystemResourceAsStream(name);
670 } else throw new IllegalArgumentException("Not a valid child");
671 }
672
673 /**
674 * @param name the name of the resource requested.
675 * @param bcc the child object making the request.
676 *
677 * @return the requested resource as an InputStream
678 */
679
680 public URL getResource(String name, BeanContextChild bcc) {
681 if (name == null) throw new NullPointerException("name");
682 if (bcc == null) throw new NullPointerException("bcc");
683
684 if (containsKey(bcc)) {
685 ClassLoader cl = bcc.getClass().getClassLoader();
686
687 return cl != null ? cl.getResource(name)
688 : ClassLoader.getSystemResource(name);
689 } else throw new IllegalArgumentException("Not a valid child");
690 }
691
692 /**
693 * Sets the new design time value for this <tt>BeanContext</tt>.
694 * @param dTime the new designTime value
695 */
696 public synchronized void setDesignTime(boolean dTime) {
697 if (designTime != dTime) {
698 designTime = dTime;
699
700 firePropertyChange("designMode", Boolean.valueOf(!dTime), Boolean.valueOf(dTime));
701 }
702 }
703
704
705 /**
706 * Reports whether or not this object is in
707 * currently in design time mode.
708 * @return <tt>true</tt> if in design time mode,
709 * <tt>false</tt> if not
710 */
711 public synchronized boolean isDesignTime() { return designTime; }
712
713 /**
714 * Sets the locale of this BeanContext.
715 * @param newLocale the new locale. This method call will have
716 * no effect if newLocale is <CODE>null</CODE>.
717 * @throws PropertyVetoException if the new value is rejected
718 */
719 public synchronized void setLocale(Locale newLocale) throws PropertyVetoException {
720
721 if ((locale != null && !locale.equals(newLocale)) && newLocale != null) {
722 Locale old = locale;
723
724 fireVetoableChange("locale", old, newLocale); // throws
725
726 locale = newLocale;
727
728 firePropertyChange("locale", old, newLocale);
729 }
730 }
731
732 /**
733 * Gets the locale for this <tt>BeanContext</tt>.
734 *
735 * @return the current Locale of the <tt>BeanContext</tt>
736 */
737 public synchronized Locale getLocale() { return locale; }
738
739 /**
740 * <p>
741 * This method is typically called from the environment in order to determine
742 * if the implementor "needs" a GUI.
743 * </p>
744 * <p>
745 * The algorithm used herein tests the BeanContextPeer, and its current children
746 * to determine if they are either Containers, Components, or if they implement
747 * Visibility and return needsGui() == true.
748 * </p>
749 * @return <tt>true</tt> if the implementor needs a GUI
750 */
751 public synchronized boolean needsGui() {
752 BeanContext bc = getBeanContextPeer();
753
754 if (bc != this) {
755 if (bc instanceof Visibility) return ((Visibility)bc).needsGui();
756
757 if (bc instanceof Container || bc instanceof Component)
758 return true;
759 }
760
761 synchronized(children) {
762 for (Iterator i = children.keySet().iterator(); i.hasNext();) {
763 Object c = i.next();
764
765 try {
766 return ((Visibility)c).needsGui();
767 } catch (ClassCastException cce) {
768 // do nothing ...
769 }
770
771 if (c instanceof Container || c instanceof Component)
772 return true;
773 }
774 }
775
776 return false;
777 }
778
779 /**
780 * notify this instance that it may no longer render a GUI.
781 */
782
783 public synchronized void dontUseGui() {
784 if (okToUseGui) {
785 okToUseGui = false;
786
787 // lets also tell the Children that can that they may not use their GUI's
788 synchronized(children) {
789 for (Iterator i = children.keySet().iterator(); i.hasNext();) {
790 Visibility v = getChildVisibility(i.next());
791
792 if (v != null) v.dontUseGui();
793 }
794 }
795 }
796 }
797
798 /**
799 * Notify this instance that it may now render a GUI
800 */
801
802 public synchronized void okToUseGui() {
803 if (!okToUseGui) {
804 okToUseGui = true;
805
806 // lets also tell the Children that can that they may use their GUI's
807 synchronized(children) {
808 for (Iterator i = children.keySet().iterator(); i.hasNext();) {
809 Visibility v = getChildVisibility(i.next());
810
811 if (v != null) v.okToUseGui();
812 }
813 }
814 }
815 }
816
817 /**
818 * Used to determine if the <tt>BeanContext</tt>
819 * child is avoiding using its GUI.
820 * @return is this instance avoiding using its GUI?
821 * @see Visibility
822 */
823 public boolean avoidingGui() {
824 return !okToUseGui && needsGui();
825 }
826
827 /**
828 * Is this <tt>BeanContext</tt> in the
829 * process of being serialized?
830 * @return if this <tt>BeanContext</tt> is
831 * currently being serialized
832 */
833 public boolean isSerializing() { return serializing; }
834
835 /**
836 * Returns an iterator of all children
837 * of this <tt>BeanContext</tt>.
838 * @return an iterator for all the current BCSChild values
839 */
840 protected Iterator bcsChildren() { synchronized(children) { return children.values().iterator(); } }
841
842 /**
843 * called by writeObject after defaultWriteObject() but prior to
844 * serialization of currently serializable children.
845 *
846 * This method may be overridden by subclasses to perform custom
847 * serialization of their state prior to this superclass serializing
848 * the children.
849 *
850 * This method should not however be used by subclasses to replace their
851 * own implementation (if any) of writeObject().
852 */
853
854 protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
855 }
856
857 /**
858 * called by readObject after defaultReadObject() but prior to
859 * deserialization of any children.
860 *
861 * This method may be overridden by subclasses to perform custom
862 * deserialization of their state prior to this superclass deserializing
863 * the children.
864 *
865 * This method should not however be used by subclasses to replace their
866 * own implementation (if any) of readObject().
867 */
868
869 protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
870 }
871
872 /**
873 * Called by readObject with the newly deserialized child and BCSChild.
874 * @param child the newly deserialized child
875 * @param bcsc the newly deserialized BCSChild
876 */
877 protected void childDeserializedHook(Object child, BCSChild bcsc) {
878 synchronized(children) {
879 children.put(child, bcsc);
880 }
881 }
882
883 /**
884 * Used by writeObject to serialize a Collection.
885 * @param oos the <tt>ObjectOutputStream</tt>
886 * to use during serialization
887 * @param coll the <tt>Collection</tt> to serialize
888 * @throws IOException if serialization failed
889 */
890 protected final void serialize(ObjectOutputStream oos, Collection coll) throws IOException {
891 int count = 0;
892 Object[] objects = coll.toArray();
893
894 for (int i = 0; i < objects.length; i++) {
895 if (objects[i] instanceof Serializable)
896 count++;
897 else
898 objects[i] = null;
899 }
900
901 oos.writeInt(count); // number of subsequent objects
902
903 for (int i = 0; count > 0; i++) {
904 Object o = objects[i];
905
906 if (o != null) {
907 oos.writeObject(o);
908 count--;
909 }
910 }
911 }
912
913 /**
914 * used by readObject to deserialize a collection.
915 * @param ois the ObjectInputStream to use
916 * @param coll the Collection
917 */
918 protected final void deserialize(ObjectInputStream ois, Collection coll) throws IOException, ClassNotFoundException {
919 int count = 0;
920
921 count = ois.readInt();
922
923 while (count-- > 0) {
924 coll.add(ois.readObject());
925 }
926 }
927
928 /**
929 * Used to serialize all children of
930 * this <tt>BeanContext</tt>.
931 * @param oos the <tt>ObjectOutputStream</tt>
932 * to use during serialization
933 * @throws IOException if serialization failed
934 */
935 public final void writeChildren(ObjectOutputStream oos) throws IOException {
936 if (serializable <= 0) return;
937
938 boolean prev = serializing;
939
940 serializing = true;
941
942 int count = 0;
943
944 synchronized(children) {
945 Iterator i = children.entrySet().iterator();
946
947 while (i.hasNext() && count < serializable) {
948 Map.Entry entry = (Map.Entry)i.next();
949
950 if (entry.getKey() instanceof Serializable) {
951 try {
952 oos.writeObject(entry.getKey()); // child
953 oos.writeObject(entry.getValue()); // BCSChild
954 } catch (IOException ioe) {
955 serializing = prev;
956 throw ioe;
957 }
958 count++;
959 }
960 }
961 }
962
963 serializing = prev;
964
965 if (count != serializable) {
966 throw new IOException("wrote different number of children than expected");
967 }
968
969 }
970
971 /**
972 * Serialize the BeanContextSupport, if this instance has a distinct
973 * peer (that is this object is acting as a delegate for another) then
974 * the children of this instance are not serialized here due to a
975 * 'chicken and egg' problem that occurs on deserialization of the
976 * children at the same time as this instance.
977 *
978 * Therefore in situations where there is a distinct peer to this instance
979 * it should always call writeObject() followed by writeChildren() and
980 * readObject() followed by readChildren().
981 *
982 * @param oos the ObjectOutputStream
983 */
984
985 private synchronized void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException {
986 serializing = true;
987
988 synchronized (BeanContext.globalHierarchyLock) {
989 try {
990 oos.defaultWriteObject(); // serialize the BeanContextSupport object
991
992 bcsPreSerializationHook(oos);
993
994 if (serializable > 0 && this.equals(getBeanContextPeer()))
995 writeChildren(oos);
996
997 serialize(oos, (Collection)bcmListeners);
998 } finally {
999 serializing = false;
1000 }
1001 }
1002 }
1003
1004 /**
1005 * When an instance of this class is used as a delegate for the
1006 * implementation of the BeanContext protocols (and its subprotocols)
1007 * there exists a 'chicken and egg' problem during deserialization
1008 */
1009
1010 public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1011 int count = serializable;
1012
1013 while (count-- > 0) {
1014 Object child = null;
1015 BeanContextSupport.BCSChild bscc = null;
1016
1017 try {
1018 child = ois.readObject();
1019 bscc = (BeanContextSupport.BCSChild)ois.readObject();
1020 } catch (IOException ioe) {
1021 continue;
1022 } catch (ClassNotFoundException cnfe) {
1023 continue;
1024 }
1025
1026
1027 synchronized(child) {
1028 BeanContextChild bcc = null;
1029
1030 try {
1031 bcc = (BeanContextChild)child;
1032 } catch (ClassCastException cce) {
1033 // do nothing;
1034 }
1035
1036 if (bcc != null) {
1037 try {
1038 bcc.setBeanContext(getBeanContextPeer());
1039
1040 bcc.addPropertyChangeListener("beanContext", childPCL);
1041 bcc.addVetoableChangeListener("beanContext", childVCL);
1042
1043 } catch (PropertyVetoException pve) {
1044 continue;
1045 }
1046 }
1047
1048 childDeserializedHook(child, bscc);
1049 }
1050 }
1051 }
1052
1053 /**
1054 * deserialize contents ... if this instance has a distinct peer the
1055 * children are *not* serialized here, the peer's readObject() must call
1056 * readChildren() after deserializing this instance.
1057 */
1058
1059 private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1060
1061 synchronized(BeanContext.globalHierarchyLock) {
1062 ois.defaultReadObject();
1063
1064 initialize();
1065
1066 bcsPreDeserializationHook(ois);
1067
1068 if (serializable > 0 && this.equals(getBeanContextPeer()))
1069 readChildren(ois);
1070
1071 deserialize(ois, bcmListeners = new ArrayList(1));
1072 }
1073 }
1074
1075 /**
1076 * subclasses may envelope to monitor veto child property changes.
1077 */
1078
1079 public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
1080 String propertyName = pce.getPropertyName();
1081 Object source = pce.getSource();
1082
1083 synchronized(children) {
1084 if ("beanContext".equals(propertyName) &&
1085 containsKey(source) &&
1086 !getBeanContextPeer().equals(pce.getNewValue())
1087 ) {
1088 if (!validatePendingRemove(source)) {
1089 throw new PropertyVetoException("current BeanContext vetoes setBeanContext()", pce);
1090 } else ((BCSChild)children.get(source)).setRemovePending(true);
1091 }
1092 }
1093 }
1094
1095 /**
1096 * subclasses may envelope to monitor child property changes.
1097 */
1098
1099 public void propertyChange(PropertyChangeEvent pce) {
1100 String propertyName = pce.getPropertyName();
1101 Object source = pce.getSource();
1102
1103 synchronized(children) {
1104 if ("beanContext".equals(propertyName) &&
1105 containsKey(source) &&
1106 ((BCSChild)children.get(source)).isRemovePending()) {
1107 BeanContext bc = getBeanContextPeer();
1108
1109 if (bc.equals(pce.getOldValue()) && !bc.equals(pce.getNewValue())) {
1110 remove(source, false);
1111 } else {
1112 ((BCSChild)children.get(source)).setRemovePending(false);
1113 }
1114 }
1115 }
1116 }
1117
1118 /**
1119 * <p>
1120 * Subclasses of this class may override, or envelope, this method to
1121 * add validation behavior for the BeanContext to examine child objects
1122 * immediately prior to their being added to the BeanContext.
1123 * </p>
1124 *
1125 * @return true iff the child may be added to this BeanContext, otherwise false.
1126 */
1127
1128 protected boolean validatePendingAdd(Object targetChild) {
1129 return true;
1130 }
1131
1132 /**
1133 * <p>
1134 * Subclasses of this class may override, or envelope, this method to
1135 * add validation behavior for the BeanContext to examine child objects
1136 * immediately prior to their being removed from the BeanContext.
1137 * </p>
1138 *
1139 * @return true iff the child may be removed from this BeanContext, otherwise false.
1140 */
1141
1142 protected boolean validatePendingRemove(Object targetChild) {
1143 return true;
1144 }
1145
1146 /**
1147 * subclasses may override this method to simply extend add() semantics
1148 * after the child has been added and before the event notification has
1149 * occurred. The method is called with the child synchronized.
1150 */
1151
1152 protected void childJustAddedHook(Object child, BCSChild bcsc) {
1153 }
1154
1155 /**
1156 * subclasses may override this method to simply extend remove() semantics
1157 * after the child has been removed and before the event notification has
1158 * occurred. The method is called with the child synchronized.
1159 */
1160
1161 protected void childJustRemovedHook(Object child, BCSChild bcsc) {
1162 }
1163
1164 /**
1165 * Gets the Component (if any) associated with the specified child.
1166 * @param child the specified child
1167 * @return the Component (if any) associated with the specified child.
1168 */
1169 protected static final Visibility getChildVisibility(Object child) {
1170 try {
1171 return (Visibility)child;
1172 } catch (ClassCastException cce) {
1173 return null;
1174 }
1175 }
1176
1177 /**
1178 * Gets the Serializable (if any) associated with the specified Child
1179 * @param child the specified child
1180 * @return the Serializable (if any) associated with the specified Child
1181 */
1182 protected static final Serializable getChildSerializable(Object child) {
1183 try {
1184 return (Serializable)child;
1185 } catch (ClassCastException cce) {
1186 return null;
1187 }
1188 }
1189
1190 /**
1191 * Gets the PropertyChangeListener
1192 * (if any) of the specified child
1193 * @param child the specified child
1194 * @return the PropertyChangeListener (if any) of the specified child
1195 */
1196 protected static final PropertyChangeListener getChildPropertyChangeListener(Object child) {
1197 try {
1198 return (PropertyChangeListener)child;
1199 } catch (ClassCastException cce) {
1200 return null;
1201 }
1202 }
1203
1204 /**
1205 * Gets the VetoableChangeListener
1206 * (if any) of the specified child
1207 * @param child the specified child
1208 * @return the VetoableChangeListener (if any) of the specified child
1209 */
1210 protected static final VetoableChangeListener getChildVetoableChangeListener(Object child) {
1211 try {
1212 return (VetoableChangeListener)child;
1213 } catch (ClassCastException cce) {
1214 return null;
1215 }
1216 }
1217
1218 /**
1219 * Gets the BeanContextMembershipListener
1220 * (if any) of the specified child
1221 * @param child the specified child
1222 * @return the BeanContextMembershipListener (if any) of the specified child
1223 */
1224 protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child) {
1225 try {
1226 return (BeanContextMembershipListener)child;
1227 } catch (ClassCastException cce) {
1228 return null;
1229 }
1230 }
1231
1232 /**
1233 * Gets the BeanContextChild (if any) of the specified child
1234 * @param child the specified child
1235 * @return the BeanContextChild (if any) of the specified child
1236 * @throws IllegalArgumentException if child implements both BeanContextChild and BeanContextProxy
1237 */
1238 protected static final BeanContextChild getChildBeanContextChild(Object child) {
1239 try {
1240 BeanContextChild bcc = (BeanContextChild)child;
1241
1242 if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
1243 throw new IllegalArgumentException("child cannot implement both BeanContextChild and BeanContextProxy");
1244 else
1245 return bcc;
1246 } catch (ClassCastException cce) {
1247 try {
1248 return ((BeanContextProxy)child).getBeanContextProxy();
1249 } catch (ClassCastException cce1) {
1250 return null;
1251 }
1252 }
1253 }
1254
1255 /**
1256 * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
1257 */
1258
1259 protected final void fireChildrenAdded(BeanContextMembershipEvent bcme) {
1260 Object[] copy;
1261
1262 synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
1263
1264 for (int i = 0; i < copy.length; i++)
1265 ((BeanContextMembershipListener)copy[i]).childrenAdded(bcme);
1266 }
1267
1268 /**
1269 * Fire a BeanContextshipEvent on the BeanContextMembershipListener interface
1270 */
1271
1272 protected final void fireChildrenRemoved(BeanContextMembershipEvent bcme) {
1273 Object[] copy;
1274
1275 synchronized(bcmListeners) { copy = bcmListeners.toArray(); }
1276
1277 for (int i = 0; i < copy.length; i++)
1278 ((BeanContextMembershipListener)copy[i]).childrenRemoved(bcme);
1279 }
1280
1281 /**
1282 * protected method called from constructor and readObject to initialize
1283 * transient state of BeanContextSupport instance.
1284 *
1285 * This class uses this method to instantiate inner class listeners used
1286 * to monitor PropertyChange and VetoableChange events on children.
1287 *
1288 * subclasses may envelope this method to add their own initialization
1289 * behavior
1290 */
1291
1292 protected synchronized void initialize() {
1293 children = new HashMap(serializable + 1);
1294 bcmListeners = new ArrayList(1);
1295
1296 childPCL = new PropertyChangeListener() {
1297
1298 /*
1299 * this adaptor is used by the BeanContextSupport class to forward
1300 * property changes from a child to the BeanContext, avoiding
1301 * accidential serialization of the BeanContext by a badly
1302 * behaved Serializable child.
1303 */
1304
1305 public void propertyChange(PropertyChangeEvent pce) {
1306 BeanContextSupport.this.propertyChange(pce);
1307 }
1308 };
1309
1310 childVCL = new VetoableChangeListener() {
1311
1312 /*
1313 * this adaptor is used by the BeanContextSupport class to forward
1314 * vetoable changes from a child to the BeanContext, avoiding
1315 * accidential serialization of the BeanContext by a badly
1316 * behaved Serializable child.
1317 */
1318
1319 public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
1320 BeanContextSupport.this.vetoableChange(pce);
1321 }
1322 };
1323 }
1324
1325 /**
1326 * Gets a copy of the this BeanContext's children.
1327 * @return a copy of the current nested children
1328 */
1329 protected final Object[] copyChildren() {
1330 synchronized(children) { return children.keySet().toArray(); }
1331 }
1332
1333 /**
1334 * Tests to see if two class objects,
1335 * or their names are equal.
1336 * @param first the first object
1337 * @param second the second object
1338 * @return true if equal, false if not
1339 */
1340 protected static final boolean classEquals(Class first, Class second) {
1341 return first.equals(second) || first.getName().equals(second.getName());
1342 }
1343
1344
1345 /*
1346 * fields
1347 */
1348
1349
1350 /**
1351 * all accesses to the <code> protected HashMap children </code> field
1352 * shall be synchronized on that object.
1353 */
1354 protected transient HashMap children;
1355
1356 private int serializable = 0; // children serializable
1357
1358 /**
1359 * all accesses to the <code> protected ArrayList bcmListeners </code> field
1360 * shall be synchronized on that object.
1361 */
1362 protected transient ArrayList bcmListeners;
1363
1364 //
1365
1366 /**
1367 * The current locale of this BeanContext.
1368 */
1369 protected Locale locale;
1370
1371 /**
1372 * A <tt>boolean</tt> indicating if this
1373 * instance may now render a GUI.
1374 */
1375 protected boolean okToUseGui;
1376
1377
1378 /**
1379 * A <tt>boolean</tt> indicating whether or not
1380 * this object is currently in design time mode.
1381 */
1382 protected boolean designTime;
1383
1384 /*
1385 * transient
1386 */
1387
1388 private transient PropertyChangeListener childPCL;
1389
1390 private transient VetoableChangeListener childVCL;
1391
1392 private transient boolean serializing;
1393}