001 /* BeanContextSupport.java --
002 Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.beans.beancontext;
040
041 import java.beans.Beans;
042 import java.beans.DesignMode;
043 import java.beans.PropertyChangeEvent;
044 import java.beans.PropertyChangeListener;
045 import java.beans.PropertyVetoException;
046 import java.beans.VetoableChangeListener;
047 import java.beans.Visibility;
048 import java.io.IOException;
049 import java.io.InputStream;
050 import java.io.ObjectInputStream;
051 import java.io.ObjectOutputStream;
052 import java.io.Serializable;
053 import java.net.URL;
054 import java.util.ArrayList;
055 import java.util.Collection;
056 import java.util.HashMap;
057 import java.util.Iterator;
058 import java.util.List;
059 import java.util.Locale;
060
061 /**
062 * This is a helper class for implementing a bean context. It is
063 * intended to be used either by subclassing or by calling methods
064 * of this implementation from another.
065 *
066 * @author Michael Koch
067 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
068 * @since 1.2
069 */
070 public class BeanContextSupport extends BeanContextChildSupport
071 implements BeanContext, Serializable, PropertyChangeListener,
072 VetoableChangeListener
073 {
074 private static final long serialVersionUID = -4879613978649577204L;
075
076 /**
077 * Deserializes a stored bean context. Hook methods are provided to allow
078 * subclasses to perform their own deserialization after the default
079 * deserialization but prior to the deserialization of the children. Note that
080 * {@link #readChildren(ObjectInputStream)} is only called if there
081 * is no distinct peer. If there is, the peer is expected to call
082 * the method instead.
083 *
084 * @param s the stream to deserialize.
085 * @throws ClassNotFoundException if the class of an object being deserialized
086 * could not be found.
087 * @throws IOException if an I/O error occurs.
088 */
089 private void readObject (ObjectInputStream s)
090 throws ClassNotFoundException, IOException
091 {
092 s.defaultReadObject();
093 bcsPreDeserializationHook(s);
094 BeanContext peer = getBeanContextPeer();
095 if (peer == null || peer == this)
096 readChildren(s);
097 }
098
099 /**
100 * Serializes a bean context. Hook methods are provided to allow
101 * subclasses to perform their own serialization after the default
102 * serialization but prior to serialization of the children. Note that
103 * {@link #writeChildren(ObjectOutputStream)} is only called if there
104 * is no distinct peer. If there is, the peer is expected to call
105 * the method instead.
106 *
107 * @param s the stream to serialize.
108 * @throws ClassNotFoundException if the class of an object being deserialized
109 * could not be found.
110 * @throws IOException if an I/O error occurs.
111 */
112 private void writeObject (ObjectOutputStream s)
113 throws ClassNotFoundException, IOException
114 {
115 serializing = true;
116 s.defaultWriteObject();
117 bcsPreSerializationHook(s);
118 BeanContext peer = getBeanContextPeer();
119 if (peer == null || peer == this)
120 writeChildren(s);
121 serializing = false;
122 }
123
124 protected class BCSChild implements Serializable
125 {
126 private static final long serialVersionUID = -5815286101609939109L;
127
128 private Object targetChild;
129 private Object peer;
130
131 BCSChild(Object targetChild, Object peer)
132 {
133 this.targetChild = targetChild;
134 this.peer = peer;
135 }
136
137 private Object getTargetChild()
138 {
139 return targetChild;
140 }
141
142 }
143
144 protected static final class BCSIterator implements Iterator
145 {
146 private Iterator child;
147
148 BCSIterator(Iterator child)
149 {
150 this.child = child;
151 }
152
153 public boolean hasNext ()
154 {
155 return child.hasNext();
156 }
157
158 public Object next ()
159 {
160 return child.next();
161 }
162
163 public void remove ()
164 {
165 // This must be a noop remove operation.
166 }
167 }
168
169 protected transient ArrayList bcmListeners;
170
171 protected transient HashMap children;
172
173 protected transient boolean designTime;
174
175 protected transient Locale locale;
176
177 protected transient boolean okToUseGui;
178
179 private transient boolean serializing;
180
181 /**
182 * Construct a BeanContextSupport instance.
183 */
184 public BeanContextSupport ()
185 {
186 this (null, null, false, true);
187 }
188
189 /**
190 * Construct a BeanContextSupport instance.
191 *
192 * @param peer the bean context peer (<code>null</code> permitted).
193 */
194 public BeanContextSupport(BeanContext peer)
195 {
196 this (peer, null, false, true);
197 }
198
199 /**
200 * Construct a BeanContextSupport instance.
201 *
202 * @param peer the bean context peer (<code>null</code> permitted).
203 * @param locale the locale (<code>null</code> permitted, equivalent to
204 * the default locale).
205 */
206 public BeanContextSupport (BeanContext peer, Locale locale)
207 {
208 this (peer, locale, false, true);
209 }
210
211 /**
212 * Construct a BeanContextSupport instance.
213 *
214 * @param peer the bean context peer (<code>null</code> permitted).
215 * @param locale the locale (<code>null</code> permitted, equivalent to
216 * the default locale).
217 * @param dtime a flag indicating whether or not the bean context is in
218 * design time mode.
219 */
220 public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime)
221 {
222 this (peer, locale, dtime, true);
223 }
224
225 /**
226 * Construct a BeanContextSupport instance.
227 *
228 * @param peer the bean context peer (<code>null</code> permitted).
229 * @param locale the locale (<code>null</code> permitted, equivalent to
230 * the default locale).
231 * @param dtime a flag indicating whether or not the bean context is in
232 * design time mode.
233 * @param visible initial value of the <code>okToUseGui</code> flag.
234 */
235 public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime,
236 boolean visible)
237 {
238 super(peer);
239
240 this.locale = locale == null ? Locale.getDefault() : locale;
241 designTime = dtime;
242 okToUseGui = visible;
243
244 initialize ();
245 }
246
247 /**
248 * <p>
249 * Add a child to the bean context. A child can be a simple
250 * <code>Object</code>, a <code>BeanContextChild</code>
251 * or another <code>BeanContext</code>.
252 * </p>
253 * <p>
254 * The children of a <code>BeanContext</code> form a set. As
255 * a result, this method returns <code>false</code> if the given
256 * object is already a child of this context.
257 * </p>
258 * <p>
259 * If the child is a <code>BeanContextChild</code>, or a proxy
260 * for such a child, the <code>setBeanContext()</code> method
261 * is invoked on the child. If this operation is vetoed by the
262 * child, via throwing a <code>PropertyVetoException</code>,
263 * then the current completion state of the <code>add()</code>
264 * operation is rolled back and a <code>IllegalStateException</code>
265 * is thrown. If the <code>BeanContextChild</code> is successfully
266 * added, then the context registers with its
267 * <code>PropertyChangeListener</code> and
268 * <code>VetoableChangeListener</code> for "beanContext" events.
269 * </p>
270 * <p>
271 * If the child implements <code>java.beans.Visibility</code>,
272 * then its ability to use a GUI is set based on that of
273 * this context.
274 * </p>
275 * <p>
276 * A <code>BeanContextMembershipEvent</code> is fired when the
277 * child is successfully added to the bean context.
278 * </p>
279 * <p>
280 * This method is synchronized over the global hierarchy lock.
281 * </p>
282 *
283 * @param targetChild the child to add.
284 * @return false if the child has already been added.
285 * @throws IllegalArgumentException if the child is null.
286 * @throws IllegalStateException if the child vetos the setting
287 * of its context.
288 */
289 public boolean add(Object targetChild)
290 {
291 synchronized (globalHierarchyLock)
292 {
293 if (targetChild == null)
294 throw new IllegalArgumentException();
295
296 BCSChild child;
297 synchronized (children)
298 {
299 if (children.containsKey(targetChild)
300 || ! validatePendingAdd(targetChild))
301 return false;
302 child = createBCSChild(targetChild, beanContextChildPeer);
303 children.put(targetChild, child);
304 }
305 synchronized (targetChild)
306 {
307 BeanContextChild bcChild = null;
308 if (targetChild instanceof BeanContextChild)
309 bcChild = (BeanContextChild) targetChild;
310 if (targetChild instanceof BeanContextProxy)
311 bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy();
312 if (bcChild != null)
313 try
314 {
315 bcChild.setBeanContext(this);
316 bcChild.addVetoableChangeListener("beanContext", this);
317 bcChild.addPropertyChangeListener("beanContext", this);
318 }
319 catch (PropertyVetoException e)
320 {
321 synchronized (children)
322 {
323 children.remove(targetChild);
324 }
325 throw new IllegalStateException("The child refused to " +
326 "associate itself with " +
327 "this context.", e);
328 }
329 if (targetChild instanceof Visibility)
330 {
331 Visibility visibleChild = (Visibility) targetChild;
332 if (okToUseGui)
333 visibleChild.okToUseGui();
334 else
335 visibleChild.dontUseGui();
336 }
337 childJustAddedHook(targetChild, child);
338 }
339 fireChildrenAdded(new BeanContextMembershipEvent(this,
340 new Object[]{ targetChild }));
341 return true;
342 }
343 }
344
345 public boolean addAll (Collection c)
346 {
347 // Intentionally throws an exception.
348 throw new UnsupportedOperationException();
349 }
350
351 public void addBeanContextMembershipListener
352 (BeanContextMembershipListener listener)
353 {
354 synchronized (bcmListeners)
355 {
356 if (! bcmListeners.contains(listener))
357 bcmListeners.add(listener);
358 }
359 }
360
361 /**
362 * Returns true if this bean needs a GUI
363 * but is being prevented from using one.
364 *
365 * @return true if <code>needsGui()</code>
366 * is true but the bean has been
367 * told not to use it.
368 */
369 public boolean avoidingGui()
370 {
371 return needsGui() && (!okToUseGui);
372 }
373
374 protected Iterator bcsChildren ()
375 {
376 synchronized (children)
377 {
378 return new BCSIterator(children.values().iterator());
379 }
380 }
381
382 /**
383 * Subclasses may use this method to perform their own deserialization
384 * after the default deserialization process has taken place, but
385 * prior to the deserialization of the children. It should not
386 * be used to replace the implementation of <code>readObject</code>
387 * in the subclass.
388 *
389 * @param ois the input stream.
390 * @throws ClassNotFoundException if the class of an object being deserialized
391 * could not be found.
392 * @throws IOException if an I/O error occurs.
393 */
394 protected void bcsPreDeserializationHook (ObjectInputStream ois)
395 throws ClassNotFoundException, IOException
396 {
397 /* Purposefully left empty */
398 }
399
400 /**
401 * Subclasses may use this method to perform their own serialization
402 * after the default serialization process has taken place, but
403 * prior to the serialization of the children. It should not
404 * be used to replace the implementation of <code>writeObject</code>
405 * in the subclass.
406 *
407 * @param oos the output stream.
408 * @throws IOException if an I/O error occurs.
409 */
410 protected void bcsPreSerializationHook (ObjectOutputStream oos)
411 throws IOException
412 {
413 /* Purposefully left empty */
414 }
415
416 /**
417 * Called when a child is deserialized.
418 *
419 * @param child the deserialized child.
420 * @param bcsc the deserialized context wrapper for the child.
421 */
422 protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc)
423 {
424 // Do nothing in the base class.
425 }
426
427 protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc)
428 {
429 // Do nothing in the base class.
430 }
431
432 protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc)
433 {
434 // Do nothing in the base class.
435 }
436
437 protected static final boolean classEquals (Class first, Class second)
438 {
439 // Lame function!
440 return (first == second || first.getName().equals(second.getName()));
441 }
442
443 public void clear ()
444 {
445 // This is the right thing to do.
446 // The JDK docs are really bad here.
447 throw new UnsupportedOperationException();
448 }
449
450 public boolean contains (Object o)
451 {
452 synchronized (children)
453 {
454 return children.containsKey(o);
455 }
456 }
457
458 public boolean containsAll (Collection c)
459 {
460 synchronized (children)
461 {
462 Iterator it = c.iterator();
463 while (it.hasNext())
464 if (! children.containsKey(it.next()))
465 return false;
466 }
467 return true;
468 }
469
470 public boolean containsKey (Object o)
471 {
472 synchronized (children)
473 {
474 return children.containsKey(o);
475 }
476 }
477
478 protected final Object[] copyChildren ()
479 {
480 synchronized (children)
481 {
482 return children.keySet().toArray();
483 }
484 }
485
486 protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer)
487 {
488 return new BCSChild(targetChild, peer);
489 }
490
491 /**
492 * Deserializes objects (written by {@link #serialize(ObjectOutputStream,
493 * Collection)}) and adds them to the specified collection.
494 *
495 * @param ois the input stream (<code>null</code> not permitted).
496 * @param coll the collection to add the objects to (<code>null</code> not
497 * permitted).
498 *
499 * @throws ClassNotFoundException
500 * @throws IOException
501 *
502 * @see #serialize(ObjectOutputStream, Collection)
503 */
504 protected final void deserialize (ObjectInputStream ois, Collection coll)
505 throws ClassNotFoundException, IOException
506 {
507 int itemCount = ois.readInt();
508 for (int i = 0; i < itemCount; i++)
509 coll.add(ois.readObject());
510 }
511
512 /**
513 * Informs this bean that is should not make
514 * use of the GUI.
515 */
516 public void dontUseGui()
517 {
518 okToUseGui = false;
519 }
520
521 protected final void fireChildrenAdded (BeanContextMembershipEvent bcme)
522 {
523 synchronized (bcmListeners)
524 {
525 Iterator it = bcmListeners.iterator();
526 while (it.hasNext())
527 {
528 BeanContextMembershipListener l
529 = (BeanContextMembershipListener) it.next();
530 l.childrenAdded(bcme);
531 }
532 }
533 }
534
535 protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme)
536 {
537 synchronized (bcmListeners)
538 {
539 Iterator it = bcmListeners.iterator();
540 while (it.hasNext())
541 {
542 BeanContextMembershipListener l
543 = (BeanContextMembershipListener) it.next();
544 l.childrenRemoved(bcme);
545 }
546 }
547 }
548
549 /**
550 * Returns the bean context peer.
551 *
552 * @return The bean context peer.
553 *
554 * @see BeanContextChildSupport#beanContextChildPeer
555 */
556 public BeanContext getBeanContextPeer()
557 {
558 return (BeanContext) beanContextChildPeer;
559 }
560
561 /**
562 * Returns the {@link BeanContextChild} implementation for the given child.
563 *
564 * @param child the child (<code>null</code> permitted).
565 *
566 * @return The bean context child.
567 *
568 * @throws IllegalArgumentException if <code>child</code> implements both
569 * the {@link BeanContextChild} and {@link BeanContextProxy} interfaces.
570 */
571 protected static final BeanContextChild getChildBeanContextChild(Object child)
572 {
573 if (child == null)
574 return null;
575 if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
576 throw new IllegalArgumentException("Child cannot implement "
577 + "BeanContextChild and BeanContextProxy simultaneously.");
578 if (child instanceof BeanContextChild)
579 return (BeanContextChild) child;
580 if (child instanceof BeanContextProxy)
581 return ((BeanContextProxy) child).getBeanContextProxy();
582 return null;
583 }
584
585 /**
586 * Returns <code>child</code> as an instance of
587 * {@link BeanContextMembershipListener}, or <code>null</code> if
588 * <code>child</code> does not implement that interface.
589 *
590 * @param child the child (<code>null</code> permitted).
591 *
592 * @return The child cast to {@link BeanContextMembershipListener}.
593 */
594 protected static final BeanContextMembershipListener
595 getChildBeanContextMembershipListener(Object child)
596 {
597 if (child instanceof BeanContextMembershipListener)
598 return (BeanContextMembershipListener) child;
599 else
600 return null;
601 }
602
603 /**
604 * Returns <code>child</code> as an instance of
605 * {@link PropertyChangeListener}, or <code>null</code> if <code>child</code>
606 * does not implement that interface.
607 *
608 * @param child the child (<code>null</code> permitted).
609 *
610 * @return The child cast to {@link PropertyChangeListener}.
611 */
612 protected static final PropertyChangeListener getChildPropertyChangeListener(
613 Object child)
614 {
615 if (child instanceof PropertyChangeListener)
616 return (PropertyChangeListener) child;
617 else
618 return null;
619 }
620
621 /**
622 * Returns <code>child</code> as an instance of {@link Serializable}, or
623 * <code>null</code> if <code>child</code> does not implement that
624 * interface.
625 *
626 * @param child the child (<code>null</code> permitted).
627 *
628 * @return The child cast to {@link Serializable}.
629 */
630 protected static final Serializable getChildSerializable(Object child)
631 {
632 if (child instanceof Serializable)
633 return (Serializable) child;
634 else
635 return null;
636 }
637
638 /**
639 * Returns <code>child</code> as an instance of
640 * {@link VetoableChangeListener}, or <code>null</code> if <code>child</code>
641 * does not implement that interface.
642 *
643 * @param child the child (<code>null</code> permitted).
644 *
645 * @return The child cast to {@link VetoableChangeListener}.
646 */
647 protected static final VetoableChangeListener getChildVetoableChangeListener(
648 Object child)
649 {
650 if (child instanceof VetoableChangeListener)
651 return (VetoableChangeListener) child;
652 else
653 return null;
654 }
655
656 /**
657 * Returns <code>child</code> as an instance of {@link Visibility}, or
658 * <code>null</code> if <code>child</code> does not implement that interface.
659 *
660 * @param child the child (<code>null</code> permitted).
661 *
662 * @return The child cast to {@link Visibility}.
663 */
664 protected static final Visibility getChildVisibility(Object child)
665 {
666 if (child instanceof Visibility)
667 return (Visibility) child;
668 else
669 return null;
670 }
671
672 public Locale getLocale ()
673 {
674 return locale;
675 }
676
677 public URL getResource (String name, BeanContextChild bcc)
678 {
679 if (! contains(bcc))
680 throw new IllegalArgumentException("argument not a child");
681 ClassLoader loader = bcc.getClass().getClassLoader();
682 return (loader == null ? ClassLoader.getSystemResource(name)
683 : loader.getResource(name));
684 }
685
686 public InputStream getResourceAsStream (String name, BeanContextChild bcc)
687 {
688 if (! contains(bcc))
689 throw new IllegalArgumentException("argument not a child");
690 ClassLoader loader = bcc.getClass().getClassLoader();
691 return (loader == null ? ClassLoader.getSystemResourceAsStream(name)
692 : loader.getResourceAsStream(name));
693 }
694
695 protected void initialize ()
696 {
697 bcmListeners = new ArrayList();
698 children = new HashMap();
699 }
700
701 /**
702 * This is a convenience method for instantiating a bean inside this
703 * context. It delegates to the appropriate method in
704 * <code>java.beans.Beans</code> using the context's classloader.
705 *
706 * @param beanName the name of the class of bean to instantiate.
707 * @throws IOException if an I/O error occurs in loading the class.
708 * @throws ClassNotFoundException if the class, <code>beanName</code>,
709 * can not be found.
710 */
711 public Object instantiateChild (String beanName)
712 throws IOException, ClassNotFoundException
713 {
714 return Beans.instantiate(getClass().getClassLoader(), beanName, this);
715 }
716
717 /**
718 * Returns <code>true</code> if the <code>BeanContext</code> is in
719 * design time mode, and <code>false</code> if it is in runtime mode.
720 *
721 * @return A boolean.
722 *
723 * @see #setDesignTime(boolean)
724 */
725 public boolean isDesignTime()
726 {
727 return designTime;
728 }
729
730 /**
731 * Returns true if this bean context has no children.
732 *
733 * @return true if there are no children.
734 */
735 public boolean isEmpty ()
736 {
737 synchronized (children)
738 {
739 return children.isEmpty();
740 }
741 }
742
743 /**
744 * Returns true if the bean context is in the process
745 * of being serialized.
746 *
747 * @return true if the context is being serialized.
748 */
749 public boolean isSerializing()
750 {
751 return serializing;
752 }
753
754 public Iterator iterator ()
755 {
756 synchronized (children)
757 {
758 return children.keySet().iterator();
759 }
760 }
761
762 /**
763 * Returns false as this bean does not a
764 * GUI for its operation.
765 *
766 * @return false
767 */
768 public boolean needsGui()
769 {
770 return false;
771 }
772
773 /**
774 * Informs this bean that it is okay to make use of
775 * the GUI.
776 */
777 public void okToUseGui ()
778 {
779 okToUseGui = true;
780 }
781
782 /**
783 * Subclasses may use this method to catch property changes
784 * arising from the children of this context. At present,
785 * we just listen for the beans being assigned to a different
786 * context and remove them from here if such an event occurs.
787 *
788 * @param pce the property change event.
789 */
790 public void propertyChange (PropertyChangeEvent pce)
791 {
792 if (pce.getNewValue() != this)
793 remove(pce.getSource(), false);
794 }
795
796 /**
797 * Deserializes the children using the
798 * {@link #deserialize(ObjectInputStream, Collection} method
799 * and then calls {@link childDeserializedHook(Object, BCSChild)}
800 * for each child deserialized.
801 *
802 * @param ois the input stream.
803 * @throws IOException if an I/O error occurs.
804 */
805 public final void readChildren (ObjectInputStream ois)
806 throws IOException, ClassNotFoundException
807 {
808 List temp = new ArrayList();
809 deserialize(ois, temp);
810 Iterator i = temp.iterator();
811 synchronized (globalHierarchyLock)
812 {
813 synchronized (children)
814 {
815 while (i.hasNext())
816 {
817 BCSChild bcs = (BCSChild) i.next();
818 childDeserializedHook(bcs.getTargetChild(), bcs);
819 children.put(bcs.getTargetChild(), bcs);
820 }
821 }
822 }
823 }
824
825 /**
826 * Remove the specified child from the context. This is
827 * the same as calling <code>remove(Object,boolean)</code>
828 * with a request for the <code>setBeanContext()</code> method
829 * of the child to be called (i.e. the second argument is true).
830 *
831 * @param targetChild the child to remove.
832 */
833 public boolean remove (Object targetChild)
834 {
835 return remove(targetChild, true);
836 }
837
838 /**
839 * <p>
840 * Removes a child from the bean context. A child can be a simple
841 * <code>Object</code>, a <code>BeanContextChild</code>
842 * or another <code>BeanContext</code>. If the given child is not
843 * a child of this context, this method returns <code>false</code>.
844 * </p>
845 * <p>
846 * If the child is a <code>BeanContextChild</code>, or a proxy
847 * for such a child, the <code>setBeanContext()</code> method
848 * is invoked on the child (if specified). If this operation is vetoed
849 * by the child, via throwing a <code>PropertyVetoException</code>,
850 * then the current completion state of the <code>remove()</code>
851 * operation is rolled back and a <code>IllegalStateException</code>
852 * is thrown. If the <code>BeanContextChild</code> is successfully
853 * removed, then the context deregisters with its
854 * <code>PropertyChangeListener</code> and
855 * <code>VetoableChangeListener</code> for "beanContext" events.
856 * </p>
857 * <p>
858 * A <code>BeanContextMembershipEvent</code> is fired when the
859 * child is successfully removed from the bean context.
860 * </p>
861 * <p>
862 * This method is synchronized over the global hierarchy lock.
863 * </p>
864 *
865 * @param targetChild the child to remove.
866 * @param callChildSetBC true if the <code>setBeanContext()</code>
867 * method of the child should be called.
868 * @return false if the child doesn't exist.
869 * @throws IllegalArgumentException if the child is null.
870 * @throws IllegalStateException if the child vetos the setting
871 * of its context.
872 */
873 protected boolean remove (Object targetChild, boolean callChildSetBC)
874 {
875 synchronized (globalHierarchyLock)
876 {
877 if (targetChild == null)
878 throw new IllegalArgumentException();
879
880 BCSChild child;
881 synchronized (children)
882 {
883 if (!children.containsKey(targetChild)
884 || !validatePendingRemove(targetChild))
885 return false;
886 child = (BCSChild) children.remove(targetChild);
887 }
888 synchronized (targetChild)
889 {
890 BeanContextChild bcChild = null;
891 if (targetChild instanceof BeanContextChild)
892 bcChild = (BeanContextChild) targetChild;
893 if (targetChild instanceof BeanContextProxy)
894 bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy();
895 if (bcChild != null)
896 try
897 {
898 if (callChildSetBC)
899 bcChild.setBeanContext(null);
900 bcChild.removeVetoableChangeListener("beanContext", this);
901 bcChild.removePropertyChangeListener("beanContext", this);
902 }
903 catch (PropertyVetoException e)
904 {
905 synchronized (children)
906 {
907 children.put(targetChild, child);
908 }
909 throw new IllegalStateException("The child refused to " +
910 "disassociate itself with " +
911 "this context.", e);
912 }
913 childJustRemovedHook(targetChild, child);
914 }
915 fireChildrenRemoved(new BeanContextMembershipEvent(this,
916 new Object[]{ targetChild }));
917 return true;
918 }
919 }
920
921 public boolean removeAll (Collection c)
922 {
923 // Intentionally throws an exception.
924 throw new UnsupportedOperationException();
925 }
926
927 public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml)
928 {
929 synchronized (bcmListeners)
930 {
931 bcmListeners.remove(bcml);
932 }
933 }
934
935 public boolean retainAll (Collection c)
936 {
937 // Intentionally throws an exception.
938 throw new UnsupportedOperationException();
939 }
940
941 /**
942 * Writes the items in the collection to the specified output stream. Items
943 * in the collection that are not instances of {@link Serializable}
944 * (this includes <code>null</code>) are simply ignored.
945 *
946 * @param oos the output stream (<code>null</code> not permitted).
947 * @param coll the collection (<code>null</code> not permitted).
948 *
949 * @throws IOException
950 *
951 * @see #deserialize(ObjectInputStream, Collection)
952 */
953 protected final void serialize(ObjectOutputStream oos, Collection coll)
954 throws IOException
955 {
956 Object[] items = coll.toArray();
957 int itemCount = 0;
958 for (int i = 0; i < items.length; i++)
959 {
960 if (items[i] instanceof Serializable)
961 itemCount++;
962 }
963 oos.writeInt(itemCount);
964 for (int i = 0; i < items.length; i++)
965 {
966 if (items[i] instanceof Serializable)
967 oos.writeObject(items[i]);
968 }
969 }
970
971 /**
972 * Sets the flag that indicates whether or not the
973 * <code>BeanContext</code> is in design mode. If the flag changes
974 * value, a {@link PropertyChangeEvent} (with the property name 'designMode')
975 * is sent to registered listeners. Note that the property name used here
976 * does NOT match the specification in the {@link DesignMode} interface, we
977 * match the reference implementation instead - see bug parade entry 4295174.
978 *
979 * @param dtime the new value for the flag.
980 *
981 * @see #isDesignTime()
982 */
983 public void setDesignTime(boolean dtime)
984 {
985 boolean save = designTime;
986 designTime = dtime;
987 // note that we use the same property name as Sun's implementation,
988 // even though this is a known bug: see bug parade entry 4295174
989 firePropertyChange("designMode", Boolean.valueOf(save),
990 Boolean.valueOf(dtime));
991 }
992
993 public void setLocale (Locale newLocale)
994 throws PropertyVetoException
995 {
996 if (newLocale == null || locale == newLocale)
997 return;
998 fireVetoableChange("locale", locale, newLocale);
999 Locale oldLocale = locale;
1000 locale = newLocale;
1001 firePropertyChange("locale", oldLocale, newLocale);
1002 }
1003
1004 public int size ()
1005 {
1006 synchronized (children)
1007 {
1008 return children.size();
1009 }
1010 }
1011
1012 /**
1013 * Returns an array containing the children of this <code>BeanContext</code>.
1014 *
1015 * @return An array containing the children.
1016 */
1017 public Object[] toArray()
1018 {
1019 synchronized (children)
1020 {
1021 return children.keySet().toArray();
1022 }
1023 }
1024
1025 /**
1026 * Populates, then returns, the supplied array with the children of this
1027 * <code>BeanContext</code>. If the array is too short to hold the
1028 * children, a new array is allocated and returned. If the array is too
1029 * long, it is padded with <code>null</code> items at the end.
1030 *
1031 * @param array an array to populate (<code>null</code> not permitted).
1032 */
1033 public Object[] toArray(Object[] array)
1034 {
1035 synchronized (children)
1036 {
1037 return children.keySet().toArray(array);
1038 }
1039 }
1040
1041 protected boolean validatePendingAdd (Object targetChild)
1042 {
1043 return true;
1044 }
1045
1046 protected boolean validatePendingRemove (Object targetChild)
1047 {
1048 return true;
1049 }
1050
1051 /**
1052 * Subclasses may use this method to veto changes arising
1053 * from the children of this context.
1054 *
1055 * @param pce the vetoable property change event fired.
1056 */
1057 public void vetoableChange (PropertyChangeEvent pce)
1058 throws PropertyVetoException
1059 {
1060 /* Purposefully left empty */
1061 }
1062
1063 /**
1064 * Serializes the children using the
1065 * {@link #serialize(ObjectOutputStream, Collection} method.
1066 *
1067 * @param oos the output stream.
1068 * @throws IOException if an I/O error occurs.
1069 */
1070 public final void writeChildren (ObjectOutputStream oos)
1071 throws IOException
1072 {
1073 synchronized (children)
1074 {
1075 serialize(oos, children.values());
1076 }
1077 }
1078
1079 }