BeanContextSupport.java 32 KB
Newer Older
Tom Tromey committed
1
/* BeanContextSupport.java --
2
   Copyright (C) 2003, 2005, 2006  Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.beans.beancontext;

41
import java.beans.Beans;
42
import java.beans.DesignMode;
Tom Tromey committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.Visibility;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
58
import java.util.List;
Tom Tromey committed
59 60 61
import java.util.Locale;

/**
62 63 64 65
 * This is a helper class for implementing a bean context.  It is
 * intended to be used either by subclassing or by calling methods
 * of this implementation from another.
 *
Tom Tromey committed
66
 * @author Michael Koch
67
 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
Tom Tromey committed
68 69 70 71 72 73 74
 * @since 1.2
 */
public class BeanContextSupport extends BeanContextChildSupport
  implements BeanContext, Serializable, PropertyChangeListener,
  VetoableChangeListener
{
  private static final long serialVersionUID = -4879613978649577204L;
75

76 77 78 79 80 81 82 83 84 85 86 87 88
  /**
   * Deserializes a stored bean context.  Hook methods are provided to allow
   * subclasses to perform their own deserialization after the default
   * deserialization but prior to the deserialization of the children.  Note that
   * {@link #readChildren(ObjectInputStream)} is only called if there
   * is no distinct peer.  If there is, the peer is expected to call
   * the method instead.
   *
   * @param s the stream to deserialize.
   * @throws ClassNotFoundException if the class of an object being deserialized
   *                                could not be found.
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
89
  private void readObject (ObjectInputStream s)
90
    throws ClassNotFoundException, IOException
Tom Tromey committed
91
  {
92 93 94 95 96
    s.defaultReadObject();
    bcsPreDeserializationHook(s);
    BeanContext peer = getBeanContextPeer();
    if (peer == null || peer == this)
      readChildren(s);
Tom Tromey committed
97 98
  }

99 100 101 102 103 104 105 106 107 108 109 110 111
  /**
   * Serializes a bean context.  Hook methods are provided to allow
   * subclasses to perform their own serialization after the default
   * serialization but prior to serialization of the children.  Note that
   * {@link #writeChildren(ObjectOutputStream)} is only called if there
   * is no distinct peer.  If there is, the peer is expected to call
   * the method instead.
   *
   * @param s the stream to serialize.
   * @throws ClassNotFoundException if the class of an object being deserialized
   *                                could not be found.
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
112
  private void writeObject (ObjectOutputStream s)
113
    throws ClassNotFoundException, IOException
Tom Tromey committed
114
  {
115 116 117 118 119 120 121
    serializing = true;
    s.defaultWriteObject();
    bcsPreSerializationHook(s);
    BeanContext peer = getBeanContextPeer();
    if (peer == null || peer == this)
      writeChildren(s);
    serializing = false;
Tom Tromey committed
122 123 124 125
  }

  protected class BCSChild implements Serializable
  {
126
    private static final long serialVersionUID = -5815286101609939109L;
127 128 129 130 131 132 133 134 135

    private Object targetChild;
    private Object peer;

    BCSChild(Object targetChild, Object peer)
    {
      this.targetChild = targetChild;
      this.peer = peer;
    }
136 137 138 139 140 141

    private Object getTargetChild()
    {
      return targetChild;
    }

Tom Tromey committed
142 143 144 145
  }

  protected static final class BCSIterator implements Iterator
  {
146 147 148 149 150 151 152
    private Iterator child;

    BCSIterator(Iterator child)
    {
      this.child = child;
    }

Tom Tromey committed
153 154
    public boolean hasNext ()
    {
155
      return child.hasNext();
Tom Tromey committed
156 157 158 159
    }

    public Object next ()
    {
160
      return child.next();
Tom Tromey committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    }

    public void remove ()
    {
      // This must be a noop remove operation.
    }
  }

  protected transient ArrayList bcmListeners;

  protected transient HashMap children;

  protected transient boolean designTime;

  protected transient Locale locale;

  protected transient boolean okToUseGui;

179 180
  private transient boolean serializing;

Tom Tromey committed
181 182 183 184 185
  /**
   * Construct a BeanContextSupport instance.
   */
  public BeanContextSupport ()
  {
186
    this (null, null, false, true);
Tom Tromey committed
187 188 189 190
  }

  /**
   * Construct a BeanContextSupport instance.
191
   *
192
   * @param peer  the bean context peer (<code>null</code> permitted).
Tom Tromey committed
193
   */
194
  public BeanContextSupport(BeanContext peer)
Tom Tromey committed
195
  {
196
    this (peer, null, false, true);
Tom Tromey committed
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).
Tom Tromey committed
205
   */
206
  public BeanContextSupport (BeanContext peer, Locale locale)
Tom Tromey committed
207
  {
208
    this (peer, locale, false, true);
Tom Tromey committed
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 217 218
   *     the default locale).
   * @param dtime  a flag indicating whether or not the bean context is in
   *     design time mode.
Tom Tromey committed
219
   */
220
  public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime)
Tom Tromey committed
221
  {
222
    this (peer, locale, dtime, true);
Tom Tromey committed
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 231 232 233
   *     the default locale).
   * @param dtime  a flag indicating whether or not the bean context is in
   *     design time mode.
   * @param visible  initial value of the <code>okToUseGui</code> flag.
Tom Tromey committed
234
   */
235
  public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime,
Tom Tromey committed
236 237
                             boolean visible)
  {
238 239
    super(peer);

240
    this.locale = locale == null ? Locale.getDefault() : locale;
Tom Tromey committed
241 242 243 244 245 246
    designTime = dtime;
    okToUseGui = visible;

    initialize ();
  }

247 248 249 250
  /**
   * <p>
   * Add a child to the bean context.  A child can be a simple
   * <code>Object</code>, a <code>BeanContextChild</code>
251
   * or another <code>BeanContext</code>.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
   * </p>
   * <p>
   * The children of a <code>BeanContext</code> form a set.  As
   * a result, this method returns <code>false</code> if the given
   * object is already a child of this context.
   * </p>
   * <p>
   * If the child is a <code>BeanContextChild</code>, or a proxy
   * for such a child, the <code>setBeanContext()</code> method
   * is invoked on the child.  If this operation is vetoed by the
   * child, via throwing a <code>PropertyVetoException</code>,
   * then the current completion state of the <code>add()</code>
   * operation is rolled back and a <code>IllegalStateException</code>
   * is thrown.  If the <code>BeanContextChild</code> is successfully
   * added, then the context registers with its
   * <code>PropertyChangeListener</code> and
   * <code>VetoableChangeListener</code> for "beanContext" events.
   * </p>
   * <p>
   * If the child implements <code>java.beans.Visibility</code>,
   * then its ability to use a GUI is set based on that of
   * this context.
   * </p>
275
   * <p>
276 277 278 279 280 281 282 283 284 285 286 287 288 289
   * A <code>BeanContextMembershipEvent</code> is fired when the
   * child is successfully added to the bean context.
   * </p>
   * <p>
   * This method is synchronized over the global hierarchy lock.
   * </p>
   *
   * @param targetChild the child to add.
   * @return false if the child has already been added.
   * @throws IllegalArgumentException if the child is null.
   * @throws IllegalStateException if the child vetos the setting
   *                               of its context.
   */
  public boolean add(Object targetChild)
Tom Tromey committed
290
  {
291
    synchronized (globalHierarchyLock)
292
      {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        if (targetChild == null)
          throw new IllegalArgumentException();

        BCSChild child;
        synchronized (children)
          {
            if (children.containsKey(targetChild)
                || ! validatePendingAdd(targetChild))
              return false;
            child = createBCSChild(targetChild, beanContextChildPeer);
            children.put(targetChild, child);
          }
        synchronized (targetChild)
          {
            BeanContextChild bcChild = null;
            if (targetChild instanceof BeanContextChild)
              bcChild = (BeanContextChild) targetChild;
            if (targetChild instanceof BeanContextProxy)
              bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy();
            if (bcChild != null)
              try
                {
                  bcChild.setBeanContext(this);
                  bcChild.addVetoableChangeListener("beanContext", this);
                  bcChild.addPropertyChangeListener("beanContext", this);
                }
              catch (PropertyVetoException e)
                {
                  synchronized (children)
                    {
                      children.remove(targetChild);
                    }
                  throw new IllegalStateException("The child refused to " +
                                                  "associate itself with " +
                                                  "this context.", e);
                }
            if (targetChild instanceof Visibility)
              {
                Visibility visibleChild = (Visibility) targetChild;
                if (okToUseGui)
                  visibleChild.okToUseGui();
                else
                  visibleChild.dontUseGui();
              }
            childJustAddedHook(targetChild, child);
          }
        fireChildrenAdded(new BeanContextMembershipEvent(this,
                                                         new Object[]{ targetChild }));
        return true;
342
      }
Tom Tromey committed
343 344 345 346
  }

  public boolean addAll (Collection c)
  {
347
    // Intentionally throws an exception.
Tom Tromey committed
348 349 350 351 352 353
    throw new UnsupportedOperationException();
  }

  public void addBeanContextMembershipListener
    (BeanContextMembershipListener listener)
  {
354 355 356 357 358
    synchronized (bcmListeners)
      {
        if (! bcmListeners.contains(listener))
          bcmListeners.add(listener);
      }
Tom Tromey committed
359 360
  }

361 362 363 364 365 366 367 368 369
  /**
   * Returns true if this bean needs a GUI
   * but is being prevented from using one.
   *
   * @return true if <code>needsGui()</code>
   *              is true but the bean has been
   *              told not to use it.
   */
  public boolean avoidingGui()
Tom Tromey committed
370
  {
371
    return needsGui() && (!okToUseGui);
Tom Tromey committed
372 373 374 375
  }

  protected Iterator bcsChildren ()
  {
376 377 378 379
    synchronized (children)
      {
        return new BCSIterator(children.values().iterator());
      }
Tom Tromey committed
380 381
  }

382 383 384 385 386 387 388 389 390 391 392 393
  /**
   * Subclasses may use this method to perform their own deserialization
   * after the default deserialization process has taken place, but
   * prior to the deserialization of the children.  It should not
   * be used to replace the implementation of <code>readObject</code>
   * in the subclass.
   *
   * @param ois the input stream.
   * @throws ClassNotFoundException if the class of an object being deserialized
   *                                could not be found.
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
394
  protected void bcsPreDeserializationHook (ObjectInputStream ois)
395
    throws ClassNotFoundException, IOException
Tom Tromey committed
396
  {
397
    /* Purposefully left empty */
Tom Tromey committed
398 399
  }

400 401 402 403 404 405 406 407 408 409
  /**
   * Subclasses may use this method to perform their own serialization
   * after the default serialization process has taken place, but
   * prior to the serialization of the children.  It should not
   * be used to replace the implementation of <code>writeObject</code>
   * in the subclass.
   *
   * @param oos the output stream.
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
410
  protected void bcsPreSerializationHook (ObjectOutputStream oos)
411
    throws IOException
Tom Tromey committed
412
  {
413
    /* Purposefully left empty */
Tom Tromey committed
414 415
  }

416 417
  /**
   * Called when a child is deserialized.
418
   *
419 420 421
   * @param child the deserialized child.
   * @param bcsc the deserialized context wrapper for the child.
   */
Tom Tromey committed
422 423
  protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc)
  {
424
    // Do nothing in the base class.
Tom Tromey committed
425 426 427 428
  }

  protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc)
  {
429
    // Do nothing in the base class.
Tom Tromey committed
430 431 432 433
  }

  protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc)
  {
434
    // Do nothing in the base class.
Tom Tromey committed
435 436 437 438
  }

  protected static final boolean classEquals (Class first, Class second)
  {
439 440
    // Lame function!
    return (first == second || first.getName().equals(second.getName()));
Tom Tromey committed
441 442 443 444
  }

  public void clear ()
  {
445 446
    // This is the right thing to do.
    // The JDK docs are really bad here.
Tom Tromey committed
447 448 449 450 451
    throw new UnsupportedOperationException();
  }

  public boolean contains (Object o)
  {
452 453 454 455
    synchronized (children)
      {
        return children.containsKey(o);
      }
Tom Tromey committed
456 457 458 459
  }

  public boolean containsAll (Collection c)
  {
460 461 462 463 464 465 466 467
    synchronized (children)
      {
        Iterator it = c.iterator();
        while (it.hasNext())
          if (! children.containsKey(it.next()))
            return false;
      }
    return true;
Tom Tromey committed
468 469 470 471
  }

  public boolean containsKey (Object o)
  {
472 473 474 475
    synchronized (children)
      {
        return children.containsKey(o);
      }
Tom Tromey committed
476 477 478 479
  }

  protected final Object[] copyChildren ()
  {
480 481 482 483
    synchronized (children)
      {
        return children.keySet().toArray();
      }
Tom Tromey committed
484 485 486 487
  }

  protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer)
  {
488
    return new BCSChild(targetChild, peer);
Tom Tromey committed
489 490
  }

491
  /**
492
   * Deserializes objects (written by {@link #serialize(ObjectOutputStream,
493
   * Collection)}) and adds them to the specified collection.
494
   *
495 496 497
   * @param ois  the input stream (<code>null</code> not permitted).
   * @param coll  the collection to add the objects to (<code>null</code> not
   *     permitted).
498
   *
499 500
   * @throws ClassNotFoundException
   * @throws IOException
501
   *
502 503
   * @see #serialize(ObjectOutputStream, Collection)
   */
Tom Tromey committed
504
  protected final void deserialize (ObjectInputStream ois, Collection coll)
505
    throws ClassNotFoundException, IOException
Tom Tromey committed
506
  {
507 508 509
    int itemCount = ois.readInt();
    for (int i = 0; i < itemCount; i++)
      coll.add(ois.readObject());
Tom Tromey committed
510 511
  }

512 513 514 515 516
  /**
   * Informs this bean that is should not make
   * use of the GUI.
   */
  public void dontUseGui()
Tom Tromey committed
517
  {
518
    okToUseGui = false;
Tom Tromey committed
519 520 521 522
  }

  protected final void fireChildrenAdded (BeanContextMembershipEvent bcme)
  {
523 524 525 526 527 528 529 530 531 532
    synchronized (bcmListeners)
      {
        Iterator it = bcmListeners.iterator();
        while (it.hasNext())
          {
            BeanContextMembershipListener l
              = (BeanContextMembershipListener) it.next();
            l.childrenAdded(bcme);
          }
      }
Tom Tromey committed
533 534 535 536
  }

  protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme)
  {
537 538 539 540 541 542 543 544 545 546
    synchronized (bcmListeners)
      {
        Iterator it = bcmListeners.iterator();
        while (it.hasNext())
          {
            BeanContextMembershipListener l
            = (BeanContextMembershipListener) it.next();
            l.childrenRemoved(bcme);
          }
      }
Tom Tromey committed
547 548
  }

549 550
  /**
   * Returns the bean context peer.
551
   *
552
   * @return The bean context peer.
553
   *
554 555 556
   * @see BeanContextChildSupport#beanContextChildPeer
   */
  public BeanContext getBeanContextPeer()
Tom Tromey committed
557
  {
558
    return (BeanContext) beanContextChildPeer;
Tom Tromey committed
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 569 570 571
   * @throws IllegalArgumentException if <code>child</code> implements both
   *     the {@link BeanContextChild} and {@link BeanContextProxy} interfaces.
   */
  protected static final BeanContextChild getChildBeanContextChild(Object child)
Tom Tromey committed
572
  {
573 574 575
    if (child == null)
      return null;
    if (child instanceof BeanContextChild && child instanceof BeanContextProxy)
576
      throw new IllegalArgumentException("Child cannot implement "
577 578 579 580 581 582
          + "BeanContextChild and BeanContextProxy simultaneously.");
    if (child instanceof BeanContextChild)
      return (BeanContextChild) child;
    if (child instanceof BeanContextProxy)
      return ((BeanContextProxy) child).getBeanContextProxy();
    return null;
Tom Tromey committed
583 584
  }

585
  /**
586 587
   * Returns <code>child</code> as an instance of
   * {@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 593
   * @return The child cast to {@link BeanContextMembershipListener}.
   */
594
  protected static final BeanContextMembershipListener
595
      getChildBeanContextMembershipListener(Object child)
Tom Tromey committed
596
  {
597
    if (child instanceof BeanContextMembershipListener)
598
      return (BeanContextMembershipListener) child;
599
    else
600
      return null;
Tom Tromey committed
601 602
  }

603
  /**
604
   * Returns <code>child</code> as an instance of
605 606
   * {@link PropertyChangeListener}, or <code>null</code> if <code>child</code>
   * does not implement that interface.
607
   *
608
   * @param child  the child (<code>null</code> permitted).
609
   *
610 611 612 613
   * @return The child cast to {@link PropertyChangeListener}.
   */
  protected static final PropertyChangeListener getChildPropertyChangeListener(
      Object child)
Tom Tromey committed
614
  {
615
    if (child instanceof PropertyChangeListener)
616
      return (PropertyChangeListener) child;
617
    else
618
      return null;
Tom Tromey committed
619 620
  }

621
  /**
622 623
   * Returns <code>child</code> as an instance of {@link Serializable}, or
   * <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 629 630
   * @return The child cast to {@link Serializable}.
   */
  protected static final Serializable getChildSerializable(Object child)
Tom Tromey committed
631
  {
632
    if (child instanceof Serializable)
633
      return (Serializable) child;
634
    else
635
      return null;
Tom Tromey committed
636 637
  }

638
  /**
639
   * Returns <code>child</code> as an instance of
640 641
   * {@link VetoableChangeListener}, or <code>null</code> if <code>child</code>
   * does not implement that interface.
642
   *
643
   * @param child  the child (<code>null</code> permitted).
644
   *
645 646 647 648
   * @return The child cast to {@link VetoableChangeListener}.
   */
  protected static final VetoableChangeListener getChildVetoableChangeListener(
      Object child)
Tom Tromey committed
649
  {
650
    if (child instanceof VetoableChangeListener)
651
      return (VetoableChangeListener) child;
652
    else
653
      return null;
Tom Tromey committed
654 655
  }

656 657 658
  /**
   * Returns <code>child</code> as an instance of {@link Visibility}, or
   * <code>null</code> if <code>child</code> does not implement that interface.
659
   *
660
   * @param child  the child (<code>null</code> permitted).
661
   *
662 663 664
   * @return The child cast to {@link Visibility}.
   */
  protected static final Visibility getChildVisibility(Object child)
Tom Tromey committed
665
  {
666
    if (child instanceof Visibility)
667
      return (Visibility) child;
668
    else
669
      return null;
Tom Tromey committed
670 671 672 673 674 675 676 677 678
  }

  public Locale getLocale ()
  {
    return locale;
  }

  public URL getResource (String name, BeanContextChild bcc)
  {
679 680 681 682 683
    if (! contains(bcc))
      throw new IllegalArgumentException("argument not a child");
    ClassLoader loader = bcc.getClass().getClassLoader();
    return (loader == null ? ClassLoader.getSystemResource(name)
            : loader.getResource(name));
Tom Tromey committed
684 685 686 687
  }

  public InputStream getResourceAsStream (String name, BeanContextChild bcc)
  {
688 689 690 691 692
    if (! contains(bcc))
      throw new IllegalArgumentException("argument not a child");
    ClassLoader loader = bcc.getClass().getClassLoader();
    return (loader == null ? ClassLoader.getSystemResourceAsStream(name)
            : loader.getResourceAsStream(name));
Tom Tromey committed
693 694 695 696 697 698 699 700
  }

  protected void initialize ()
  {
    bcmListeners = new ArrayList();
    children = new HashMap();
  }

701 702 703 704 705 706 707 708 709 710
  /**
   * This is a convenience method for instantiating a bean inside this
   * context.  It delegates to the appropriate method in
   * <code>java.beans.Beans</code> using the context's classloader.
   *
   * @param beanName the name of the class of bean to instantiate.
   * @throws IOException if an I/O error occurs in loading the class.
   * @throws ClassNotFoundException if the class, <code>beanName</code>,
   *                                can not be found.
   */
Tom Tromey committed
711
  public Object instantiateChild (String beanName)
712
    throws IOException, ClassNotFoundException
Tom Tromey committed
713
  {
714
    return Beans.instantiate(getClass().getClassLoader(), beanName, this);
Tom Tromey committed
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 724 725
   * @see #setDesignTime(boolean)
   */
  public boolean isDesignTime()
Tom Tromey committed
726
  {
727
    return designTime;
Tom Tromey committed
728 729
  }

730 731 732 733 734
  /**
   * Returns true if this bean context has no children.
   *
   * @return true if there are no children.
   */
Tom Tromey committed
735 736
  public boolean isEmpty ()
  {
737 738 739 740
    synchronized (children)
      {
        return children.isEmpty();
      }
Tom Tromey committed
741 742
  }

743 744 745 746 747 748 749
  /**
   * Returns true if the bean context is in the process
   * of being serialized.
   *
   * @return true if the context is being serialized.
   */
  public boolean isSerializing()
Tom Tromey committed
750
  {
751
    return serializing;
Tom Tromey committed
752 753 754 755
  }

  public Iterator iterator ()
  {
756 757 758 759
    synchronized (children)
      {
        return children.keySet().iterator();
      }
Tom Tromey committed
760 761
  }

762 763 764 765 766 767 768
  /**
   * Returns false as this bean does not a
   * GUI for its operation.
   *
   * @return false
   */
  public boolean needsGui()
Tom Tromey committed
769
  {
770
    return false;
Tom Tromey committed
771 772
  }

773 774 775 776
  /**
   * Informs this bean that it is okay to make use of
   * the GUI.
   */
Tom Tromey committed
777 778
  public void okToUseGui ()
  {
779
    okToUseGui = true;
Tom Tromey committed
780 781
  }

782 783 784 785 786 787 788 789
  /**
   * Subclasses may use this method to catch property changes
   * arising from the children of this context.  At present,
   * we just listen for the beans being assigned to a different
   * context and remove them from here if such an event occurs.
   *
   * @param pce the property change event.
   */
Tom Tromey committed
790 791
  public void propertyChange (PropertyChangeEvent pce)
  {
792 793
    if (pce.getNewValue() != this)
      remove(pce.getSource(), false);
Tom Tromey committed
794 795
  }

796
  /**
797
   * Deserializes the children using the
798 799 800 801
   * {@link #deserialize(ObjectInputStream, Collection} method
   * and then calls {@link childDeserializedHook(Object, BCSChild)}
   * for each child deserialized.
   *
802
   * @param ois the input stream.
803 804
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
805
  public final void readChildren (ObjectInputStream ois)
806
    throws IOException, ClassNotFoundException
Tom Tromey committed
807
  {
808 809 810 811 812
    List temp = new ArrayList();
    deserialize(ois, temp);
    Iterator i = temp.iterator();
    synchronized (globalHierarchyLock)
      {
813 814 815 816 817 818 819 820 821
        synchronized (children)
          {
            while (i.hasNext())
              {
                BCSChild bcs = (BCSChild) i.next();
                childDeserializedHook(bcs.getTargetChild(), bcs);
                children.put(bcs.getTargetChild(), bcs);
              }
          }
822
      }
Tom Tromey committed
823 824
  }

825 826 827 828 829 830 831 832
  /**
   * Remove the specified child from the context.  This is
   * the same as calling <code>remove(Object,boolean)</code>
   * with a request for the <code>setBeanContext()</code> method
   * of the child to be called (i.e. the second argument is true).
   *
   * @param targetChild the child to remove.
   */
Tom Tromey committed
833 834 835 836 837
  public boolean remove (Object targetChild)
  {
    return remove(targetChild, true);
  }

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
  /**
   * <p>
   * Removes a child from the bean context.  A child can be a simple
   * <code>Object</code>, a <code>BeanContextChild</code>
   * or another <code>BeanContext</code>.  If the given child is not
   * a child of this context, this method returns <code>false</code>.
   * </p>
   * <p>
   * If the child is a <code>BeanContextChild</code>, or a proxy
   * for such a child, the <code>setBeanContext()</code> method
   * is invoked on the child (if specified).  If this operation is vetoed
   * by the child, via throwing a <code>PropertyVetoException</code>,
   * then the current completion state of the <code>remove()</code>
   * operation is rolled back and a <code>IllegalStateException</code>
   * is thrown.  If the <code>BeanContextChild</code> is successfully
   * removed, then the context deregisters with its
   * <code>PropertyChangeListener</code> and
   * <code>VetoableChangeListener</code> for "beanContext" events.
   * </p>
857
   * <p>
858 859 860 861 862 863 864
   * A <code>BeanContextMembershipEvent</code> is fired when the
   * child is successfully removed from the bean context.
   * </p>
   * <p>
   * This method is synchronized over the global hierarchy lock.
   * </p>
   *
865
   * @param targetChild the child to remove.
866 867 868 869 870 871 872
   * @param callChildSetBC true if the <code>setBeanContext()</code>
   *                       method of the child should be called.
   * @return false if the child doesn't exist.
   * @throws IllegalArgumentException if the child is null.
   * @throws IllegalStateException if the child vetos the setting
   *                               of its context.
   */
Tom Tromey committed
873 874
  protected boolean remove (Object targetChild, boolean callChildSetBC)
  {
875 876
    synchronized (globalHierarchyLock)
      {
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
        if (targetChild == null)
          throw new IllegalArgumentException();

        BCSChild child;
        synchronized (children)
          {
            if (!children.containsKey(targetChild)
                || !validatePendingRemove(targetChild))
              return false;
            child = (BCSChild) children.remove(targetChild);
          }
        synchronized (targetChild)
          {
            BeanContextChild bcChild = null;
            if (targetChild instanceof BeanContextChild)
              bcChild = (BeanContextChild) targetChild;
            if (targetChild instanceof BeanContextProxy)
              bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy();
            if (bcChild != null)
              try
                {
                  if (callChildSetBC)
                    bcChild.setBeanContext(null);
                  bcChild.removeVetoableChangeListener("beanContext", this);
                  bcChild.removePropertyChangeListener("beanContext", this);
                }
              catch (PropertyVetoException e)
                {
                  synchronized (children)
                    {
                      children.put(targetChild, child);
                    }
                  throw new IllegalStateException("The child refused to " +
                                                  "disassociate itself with " +
                                                  "this context.", e);
                }
            childJustRemovedHook(targetChild, child);
          }
        fireChildrenRemoved(new BeanContextMembershipEvent(this,
                                                         new Object[]{ targetChild }));
        return true;
918
      }
Tom Tromey committed
919 920 921 922
  }

  public boolean removeAll (Collection c)
  {
923
    // Intentionally throws an exception.
Tom Tromey committed
924 925 926 927 928
    throw new UnsupportedOperationException();
  }

  public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml)
  {
929 930 931 932
    synchronized (bcmListeners)
      {
        bcmListeners.remove(bcml);
      }
Tom Tromey committed
933 934 935 936
  }

  public boolean retainAll (Collection c)
  {
937
    // Intentionally throws an exception.
Tom Tromey committed
938 939 940
    throw new UnsupportedOperationException();
  }

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 947
   * @param oos  the output stream (<code>null</code> not permitted).
   * @param coll  the collection (<code>null</code> not permitted).
948
   *
949
   * @throws IOException
950
   *
951 952 953 954
   * @see #deserialize(ObjectInputStream, Collection)
   */
  protected final void serialize(ObjectOutputStream oos, Collection coll)
    throws IOException
Tom Tromey committed
955
  {
956 957 958 959 960 961 962 963 964 965 966 967 968
    Object[] items = coll.toArray();
    int itemCount = 0;
    for (int i = 0; i < items.length; i++)
      {
        if (items[i] instanceof Serializable)
          itemCount++;
      }
    oos.writeInt(itemCount);
    for (int i = 0; i < items.length; i++)
      {
        if (items[i] instanceof Serializable)
          oos.writeObject(items[i]);
      }
Tom Tromey committed
969 970
  }

971
  /**
972
   * Sets the flag that indicates whether or not the
973 974 975 976 977
   * <code>BeanContext</code> is in design mode.  If the flag changes
   * value, a {@link PropertyChangeEvent} (with the property name 'designMode')
   * is sent to registered listeners.  Note that the property name used here
   * does NOT match the specification in the {@link DesignMode} interface, we
   * match the reference implementation instead - see bug parade entry 4295174.
978
   *
979
   * @param dtime  the new value for the flag.
980
   *
981 982 983
   * @see #isDesignTime()
   */
  public void setDesignTime(boolean dtime)
Tom Tromey committed
984
  {
985 986
    boolean save = designTime;
    designTime = dtime;
987 988 989
    // note that we use the same property name as Sun's implementation,
    // even though this is a known bug: see bug parade entry 4295174
    firePropertyChange("designMode", Boolean.valueOf(save),
990
                       Boolean.valueOf(dtime));
Tom Tromey committed
991 992 993 994 995
  }

  public void setLocale (Locale newLocale)
    throws PropertyVetoException
  {
996 997 998 999 1000 1001
    if (newLocale == null || locale == newLocale)
      return;
    fireVetoableChange("locale", locale, newLocale);
    Locale oldLocale = locale;
    locale = newLocale;
    firePropertyChange("locale", oldLocale, newLocale);
Tom Tromey committed
1002 1003 1004 1005
  }

  public int size ()
  {
1006 1007 1008 1009
    synchronized (children)
      {
        return children.size();
      }
Tom Tromey committed
1010 1011
  }

1012 1013
  /**
   * Returns an array containing the children of this <code>BeanContext</code>.
1014
   *
1015 1016 1017
   * @return An array containing the children.
   */
  public Object[] toArray()
Tom Tromey committed
1018
  {
1019 1020 1021 1022
    synchronized (children)
      {
        return children.keySet().toArray();
      }
Tom Tromey committed
1023 1024
  }

1025
  /**
1026 1027 1028
   * Populates, then returns, the supplied array with the children of this
   * <code>BeanContext</code>.  If the array is too short to hold the
   * 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 1032
   * @param array  an array to populate (<code>null</code> not permitted).
   */
Tom Tromey committed
1033 1034
  public Object[] toArray(Object[] array)
  {
1035 1036 1037 1038
    synchronized (children)
      {
        return children.keySet().toArray(array);
      }
Tom Tromey committed
1039 1040 1041 1042
  }

  protected boolean validatePendingAdd (Object targetChild)
  {
1043
    return true;
Tom Tromey committed
1044 1045 1046 1047
  }

  protected boolean validatePendingRemove (Object targetChild)
  {
1048
    return true;
Tom Tromey committed
1049 1050
  }

1051 1052 1053 1054 1055 1056
  /**
   * Subclasses may use this method to veto changes arising
   * from the children of this context.
   *
   * @param pce the vetoable property change event fired.
   */
Tom Tromey committed
1057
  public void vetoableChange (PropertyChangeEvent pce)
1058
    throws PropertyVetoException
Tom Tromey committed
1059
  {
1060
    /* Purposefully left empty */
Tom Tromey committed
1061 1062
  }

1063 1064 1065 1066 1067 1068 1069
  /**
   * Serializes the children using the
   * {@link #serialize(ObjectOutputStream, Collection} method.
   *
   * @param oos the output stream.
   * @throws IOException if an I/O error occurs.
   */
Tom Tromey committed
1070
  public final void writeChildren (ObjectOutputStream oos)
1071
    throws IOException
Tom Tromey committed
1072
  {
1073 1074
    synchronized (children)
      {
1075
        serialize(oos, children.values());
1076
      }
Tom Tromey committed
1077
  }
1078

Tom Tromey committed
1079
}