Package com.googlecode.openbeans.beancontext

Source Code of com.googlecode.openbeans.beancontext.BeanContextSupport

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package com.googlecode.openbeans.beancontext;

import java.awt.Component;
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;
import java.util.Locale;

import org.apache.harmony.beans.internal.nls.Messages;

import com.googlecode.openbeans.Beans;
import com.googlecode.openbeans.PropertyChangeEvent;
import com.googlecode.openbeans.PropertyChangeListener;
import com.googlecode.openbeans.PropertyVetoException;
import com.googlecode.openbeans.VetoableChangeListener;
import com.googlecode.openbeans.Visibility;

/**
* This support class implements <code>BeanContext</code> interface. This class can be used directly, or be a super class of your class, or be a delegate of
* your implementation that needs to support <code>BeanContext</code> interface.
*
*/
@SuppressWarnings("rawtypes")
public class BeanContextSupport extends BeanContextChildSupport implements BeanContext, PropertyChangeListener, VetoableChangeListener, Serializable
{

  /**
   * Every child of context is companied with a <code>BCSChild</code> instance. It can hold implementation specific information about each child.
   * <p>
   * This class holds references of the child and its peer if there is one.
   * </p>
   *
   */
  protected class BCSChild implements Serializable
  {

    private static final long  serialVersionUID  = -5815286101609939109L;

    Object            child;

    Object            proxyPeer;

    BCSChild(Object child, Object proxyPeer)
    {
      this.child = child;
      this.proxyPeer = proxyPeer;
    }
  }

  /**
   * This implementation wraps an iterator and override <code>remove()</code> with a noop method.
   *
   */
  protected static final class BCSIterator implements Iterator
  {

    private Iterator  backIter;

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

    public boolean hasNext()
    {
      return backIter.hasNext();
    }

    public Object next()
    {
      return backIter.next();
    }

    public void remove()
    {
      // no-op
    }
  }

  private static final long          serialVersionUID  = -4879613978649577204L// J2SE 1.4.2

  /**
   * A list of registered membership listeners. All access to this object should be synchronized on itself.
   */
  transient protected ArrayList        bcmListeners;

  /**
   * A map of children - key is child instance, value is <code>BCSChild</code> instance. All access to this object should be synchronized on itself.
   */
  transient protected HashMap          children;

  transient private boolean          serializing;

  transient private boolean          inNeedsGui;

  transient private PropertyChangeListener  nonSerPCL;

  private int                  serializable;

  /**
   * The locale of this context.
   */
  protected Locale              locale;

  /**
   * A flag indicating whether this context is allowed to use GUI.
   */
  protected boolean              okToUseGui;

  /**
   * A flag indicating whether this context is in design mode.
   */
  protected boolean              designTime;

  /**
   * Constructs a standload <code>BeanContextSupport</code>.
   */
  public BeanContextSupport()
  {
    this(null, Locale.getDefault(), false, true);
  }

  /**
   * Constructs a <code>BeanContextSupport</code> which is a delegate of the given peer.
   *
   * @param peer
   *            the peer of this context
   */
  public BeanContextSupport(BeanContext peer)
  {
    this(peer, Locale.getDefault(), false, true);
  }

  /**
   * Constructs a <code>BeanContextSupport</code> which is a delegate of the given peer.
   *
   * @param peer
   *            the peer of this context
   * @param locale
   *            the locale of this context
   */
  public BeanContextSupport(BeanContext peer, Locale locale)
  {
    this(peer, locale, false, true);
  }

  /**
   * Constructs a <code>BeanContextSupport</code> which is a delegate of the given peer.
   *
   * @param peer
   *            the peer of this context
   * @param locale
   *            the locale of this context
   * @param designTime
   *            whether in design mode or not
   */
  public BeanContextSupport(BeanContext peer, Locale locale, boolean designTime)
  {
    this(peer, locale, designTime, true);
  }

  /**
   * Constructs a <code>BeanContextSupport</code> which is a delegate of the given peer.
   *
   * @param peer
   *            the peer of this context
   * @param locale
   *            the locale of this context
   * @param designTime
   *            whether in design mode or not
   * @param okToUseGui
   *            whether GUI is usable or not
   */
  public BeanContextSupport(BeanContext peer, Locale locale, boolean designTime, boolean okToUseGui)
  {
    super(peer);
    if (locale == null)
    {
      locale = Locale.getDefault();
    }
    this.locale = locale;
    this.designTime = designTime;
    this.okToUseGui = okToUseGui;

    initialize();
  }

  /**
   * Add a child to this context.
   * <p>
   * If the child already exists in this context, simply returns false. Otherwise, it is validated by calling <code>validatePendingAdd()</code>. If the add is
   * valid, the child and its proxy (if the child implements <code>BeanContextProxy</code>) is then added, and <code>setBeanContext()</code> is called on it
   * (if the child implements <code>BeanContextChild</code> or it has a proxy). Last, the <code>childJustAddedHook()</code> is called and all registered
   * <code>BeanContextMembershipListener</code>s are notified.
   * </p>
   *
   * @param child
   *            the child to add
   * @return true if the child is added to this context; otherwise false
   * @throws IllegalStateException
   *             if the child is not valid to add
   * @see java.util.Collection#add(java.lang.Object)
   */
  @SuppressWarnings("unchecked")
  public boolean add(Object child)
  {
    if (child == null)
    {
      throw new IllegalArgumentException(Messages.getString("beans.67"));
    }

    BeanContextChild proxy = null;

    synchronized (globalHierarchyLock)
    {
      // check existence
      if (contains(child))
      {
        return false;
      }

      // check serializing state
      if (serializing)
      {
        throw new IllegalStateException(Messages.getString("beans.68"));
      }

      // validate
      boolean valid = validatePendingAdd(child);
      if (!valid)
      {
        throw new IllegalStateException(Messages.getString("beans.69"));
      }

      // find the proxy, if there's one
      if (child instanceof BeanContextProxy)
      {
        proxy = ((BeanContextProxy) child).getBeanContextProxy();
        if (proxy == null)
        {
          throw new NullPointerException(Messages.getString("beans.6A"));
        }
      }
      BeanContextChild beanContextChild = getChildBeanContextChild(child);

      // add to children
      BCSChild childBCSC = null, proxyBCSC = null;
      synchronized (children)
      {
        childBCSC = createBCSChild(child, proxy);
        children.put(child, childBCSC);
        if (proxy != null)
        {
          proxyBCSC = createBCSChild(proxy, child);
          children.put(proxy, proxyBCSC);
        }
      }

      // set child's beanContext property
      if (beanContextChild != null)
      {
        try
        {
          beanContextChild.setBeanContext(getBeanContextPeer());
        }
        catch (PropertyVetoException e)
        {
          synchronized (children)
          {
            children.remove(child);
            if (proxy != null)
            {
              children.remove(proxy);
            }
          }
          throw new IllegalStateException(Messages.getString("beans.6B"));
        }
        // ensure no duplicate listener
        beanContextChild.removePropertyChangeListener("beanContext", nonSerPCL);
        // listen to child's beanContext change
        beanContextChild.addPropertyChangeListener("beanContext", nonSerPCL);
      }

      // trigger hook
      synchronized (child)
      {
        addSerializable(childBCSC);
        childJustAddedHook(child, childBCSC);
      }
      if (proxy != null)
      {
        synchronized (proxy)
        {
          addSerializable(proxyBCSC);
          childJustAddedHook(proxy, proxyBCSC);
        }
      }
    }

    // notify listeners
    fireChildrenAdded(new BeanContextMembershipEvent(getBeanContextPeer(), proxy == null ? new Object[] { child } : new Object[] { child, proxy }));
    return true;
  }

  /**
   * This method is unsupported, throws <code>UnsupportedOperationException</code>.
   *
   * @see java.util.Collection#addAll(java.util.Collection)
   */
  public boolean addAll(Collection collection)
  {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * com.googlecode.openbeans.beancontext.BeanContext#addBeanContextMembershipListener(com.googlecode.openbeans.beancontext.BeanContextMembershipListener)
   */
  @SuppressWarnings("unchecked")
  public void addBeanContextMembershipListener(BeanContextMembershipListener listener)
  {
    if (listener == null)
    {
      throw new NullPointerException();
    }
    synchronized (bcmListeners)
    {
      if (!bcmListeners.contains(listener))
      {
        bcmListeners.add(listener);
      }
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.Visibility#avoidingGui()
   */
  public boolean avoidingGui()
  {
    // Avoiding GUI means that
    // GUI is needed but not allowed to use at this time
    return (needsGui() && !this.okToUseGui);
  }

  /**
   * Returns an iterator of all <code>BCSChild</code> instances, with <code>remove()</code> disabled.
   *
   * @return an iterator of all <code>BCSChild</code> instances
   */
  protected Iterator bcsChildren()
  {
    synchronized (children)
    {
      return new BCSIterator(children.values().iterator());
    }
  }

  /**
   * This method is called by <code>readObject()</code> after <code>defaultReadObject()</code> and before deserializing any children or listeners. Subclass
   * can insert its specific deserialization behavior by overrideing this method.
   * <p>
   * The default implementation does nothing.
   * </p>
   *
   * @param ois
   *            the object input stream
   * @throws IOException
   * @throws ClassNotFoundException
   */
  protected void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException
  {
    // to be overridden
  }

  /**
   * This method is called by <code>writeObject()</code> after <code>defaultWriteObject()</code> and before serializing any children or listeners. Subclass
   * can insert its specific serialization behavior by overrideing this method.
   * <p>
   * The default implementation does nothing.
   * </p>
   *
   * @param oos
   *            the object output stream
   * @throws IOException
   */
  protected void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException
  {
    // to be overridden
  }

  /**
   * This method is called during deserialization everytime a child is read.
   * <p>
   * The default implementation does nothing.
   * </p>
   *
   * @param child
   *            the child just deserialized
   * @param bcsChild
   *            the <code>BCSChild</code> just deserialized
   */
  protected void childDeserializedHook(Object child, BCSChild bcsChild)
  {
    // to be overridden
  }

  /**
   * This method is called everytime a child is added to this context. This method is called with child synchronized.
   * <p>
   * The default implementation does nothing.
   * </p>
   *
   * @param child
   *            the child just added
   * @param bcsChild
   *            the <code>BCSChild</code> just added
   */
  protected void childJustAddedHook(Object child, BCSChild bcsChild)
  {
    // to be overridden
  }

  /**
   * This method is called everytime a child is removed from this context. This method is called with child synchronized.
   * <p>
   * The default implementation does nothing.
   * </p>
   *
   * @param child
   *            the child just removed
   * @param bcsChild
   *            the <code>BCSChild</code> just removed
   */
  protected void childJustRemovedHook(Object child, BCSChild bcsChild)
  {
    // to be overridden
  }

  /**
   * Compares if two classes are equal or their class names are equal.
   *
   * @param clz1
   *            a class
   * @param clz2
   *            another class
   * @return true if two class objects are equal or their class names are equal.
   */
  protected static final boolean classEquals(Class clz1, Class clz2)
  {
    if (clz1 == null || clz2 == null)
    {
      throw new NullPointerException();
    }
    return clz1 == clz2 || clz1.getName().equals(clz2.getName());
  }

  /**
   * This method is unsupported, throws <code>UnsupportedOperationException</code>.
   *
   * @see java.util.Collection#clear()
   */
  public void clear()
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Returns true if the given object is a child of this context.
   *
   * @param child
   *            the object to test
   * @return true if the given object is a child of this context
   * @see java.util.Collection#contains(java.lang.Object)
   */
  public boolean contains(Object child)
  {
    synchronized (children)
    {
      return children.containsKey(child);
    }
  }

  /**
   * Returns true if given objects are children of this context.
   *
   * @param collection
   *            a collection of objects
   * @return true if given objects are children of this context
   * @see java.util.Collection#containsAll(java.util.Collection)
   */
  @SuppressWarnings("unchecked")
  public boolean containsAll(Collection collection)
  {
    synchronized (children)
    {
      return children.keySet().containsAll(collection);
    }
  }

  /**
   * Returns true if the given object is a child of this context.
   *
   * @param child
   *            the object to test
   * @return true if the given object is a child of this context
   */
  public boolean containsKey(Object child)
  {
    synchronized (children)
    {
      return children.containsKey(child);
    }
  }

  /**
   * Returns an array containing all children of this context.
   *
   * @return an array containing all children of this context
   */
  protected final Object[] copyChildren()
  {
    synchronized (children)
    {
      return children.keySet().toArray();
    }
  }

  /**
   * Creates a <code>BCSChild</code> object to company the given child.
   *
   * @param child
   *            the child
   * @param proxyPeer
   *            the proxy peer of the child if there is one
   * @return a <code>BCSChild</code> object to company the given child
   */
  protected BCSChild createBCSChild(Object child, Object proxyPeer)
  {
    return new BCSChild(child, proxyPeer);
  }

  /**
   * Deserialize a collection.
   * <p>
   * First read a <code>int</code> indicating of number of rest objects, then read the objects one by one.
   * </p>
   *
   * @param ois
   *            the stream where the collection is read from
   * @param collection
   *            the collection to hold read objects
   * @throws IOException
   *             if I/O exception occurs
   * @throws ClassNotFoundException
   *             if class of any read object is not found
   */
  @SuppressWarnings("unchecked")
  protected final void deserialize(ObjectInputStream ois, Collection collection) throws IOException, ClassNotFoundException
  {
    int size = ois.readInt();
    for (int i = 0; i < size; i++)
    {
      collection.add(ois.readObject());
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.Visibility#dontUseGui()
   */
  public void dontUseGui()
  {
    okToUseGui = false;
  }

  /**
   * Notifies registered <code>BeanContextMembershipListener</code>s that a new child has been added.
   *
   * @param event
   *            the <code>BeanContextMembershipEvent</code>
   */
  protected final void fireChildrenAdded(BeanContextMembershipEvent event)
  {
    Object listeners[];
    synchronized (bcmListeners)
    {
      listeners = bcmListeners.toArray();
    }
    for (int i = 0; i < listeners.length; i++)
    {
      BeanContextMembershipListener l = (BeanContextMembershipListener) listeners[i];
      l.childrenAdded(event);
    }
  }

  /**
   * Notifies registered <code>BeanContextMembershipListener</code>s that a child has been removed.
   *
   * @param event
   *            the <code>BeanContextMembershipEvent</code>
   */
  protected final void fireChildrenRemoved(BeanContextMembershipEvent event)
  {
    Object listeners[];
    synchronized (bcmListeners)
    {
      listeners = bcmListeners.toArray();
    }
    for (int i = 0; i < listeners.length; i++)
    {
      BeanContextMembershipListener l = (BeanContextMembershipListener) listeners[i];
      l.childrenRemoved(event);
    }
  }

  /**
   * Returns the peer of this context casted as <code>BeanContext</code>.
   *
   * @return the peer of this context casted as <code>BeanContext</code>
   */
  public BeanContext getBeanContextPeer()
  {
    return (BeanContext) beanContextChildPeer;
  }

  /**
   * Returns the <code>BeanContextChild</code> related with the given child.
   * <p>
   * If the child implements <code>BeanContextChild</code>, it is returned. If the child implements <code>BeanContextProxy</code>, the proxy is returned.
   * Otherwise, null is returned.
   * </p>
   *
   * @param child
   *            a child
   * @return the <code>BeanContextChild</code> related with the given child
   * @throws IllegalStateException
   *             if the child implements both <code>BeanContextChild</code> and <code>BeanContextProxy</code>
   */
  protected static final BeanContextChild getChildBeanContextChild(Object child)
  {
    if (child instanceof BeanContextChild)
    {
      if (child instanceof BeanContextProxy)
      {
        throw new IllegalArgumentException(Messages.getString("beans.6C"));
      }
      return (BeanContextChild) child;
    }
    if (child instanceof BeanContextProxy)
    {
      if (child instanceof BeanContextChild)
      {
        throw new IllegalArgumentException(Messages.getString("beans.6C"));
      }
      return ((BeanContextProxy) child).getBeanContextProxy();
    }
    return null;
  }

  /**
   * Returns the given child casted to <code>BeanContextMembershipListener</code>, or null if it does not implements the interface.
   *
   * @param child
   *            a child
   * @return the given child casted to <code>BeanContextMembershipListener</code>, or null if it does not implements the interface
   */
  protected static final BeanContextMembershipListener getChildBeanContextMembershipListener(Object child)
  {
    if (child instanceof BeanContextMembershipListener)
    {
      return (BeanContextMembershipListener) child;
    }
    else
    {
      return null;
    }
  }

  /**
   * Returns the given child casted to <code>PropertyChangeListener</code>, or null if it does not implements the interface.
   *
   * @param child
   *            a child
   * @return the given child casted to <code>PropertyChangeListener</code>, or null if it does not implements the interface
   */
  protected static final PropertyChangeListener getChildPropertyChangeListener(Object child)
  {
    if (child instanceof PropertyChangeListener)
    {
      return (PropertyChangeListener) child;
    }
    else
    {
      return null;
    }
  }

  /**
   * Returns the given child casted to <code>Serializable</code>, or null if it does not implements the interface.
   *
   * @param child
   *            a child
   * @return the given child casted to <code>Serializable</code>, or null if it does not implements the interface
   */
  protected static final Serializable getChildSerializable(Object child)
  {
    if (child instanceof Serializable)
    {
      return (Serializable) child;
    }
    else
    {
      return null;
    }
  }

  /**
   * Returns the given child casted to <code>VetoableChangeListener</code>, or null if it does not implements the interface.
   *
   * @param child
   *            a child
   * @return the given child casted to <code>VetoableChangeListener</code>, or null if it does not implements the interface
   */
  protected static final VetoableChangeListener getChildVetoableChangeListener(Object child)
  {
    if (child instanceof VetoableChangeListener)
    {
      return (VetoableChangeListener) child;
    }
    else
    {
      return null;
    }
  }

  /**
   * Returns the given child casted to <code>Visibility</code>, or null if it does not implements the interface.
   *
   * @param child
   *            a child
   * @return the given child casted to <code>Visibility</code>, or null if it does not implements the interface
   */
  protected static final Visibility getChildVisibility(Object child)
  {
    if (child instanceof Visibility)
    {
      return (Visibility) child;
    }
    else
    {
      return null;
    }
  }

  /**
   * Returns the locale of this context.
   *
   * @return the locale of this context
   */
  public Locale getLocale()
  {
    return locale;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.beancontext.BeanContext#getResource(java.lang.String, com.googlecode.openbeans.beancontext.BeanContextChild)
   */
  public URL getResource(String resourceName, BeanContextChild child)
  {
    if (resourceName == null || child == null)
    {
      throw new NullPointerException();
    }
    if (!contains(child))
    {
      throw new IllegalArgumentException(Messages.getString("beans.6D"));
    }

    return ClassLoader.getSystemResource(resourceName);
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.beancontext.BeanContext#getResourceAsStream(java.lang.String, com.googlecode.openbeans.beancontext.BeanContextChild)
   */
  public InputStream getResourceAsStream(String resourceName, BeanContextChild child) throws IllegalArgumentException
  {
    if (resourceName == null || child == null)
    {
      throw new NullPointerException();
    }
    if (!contains(child))
    {
      throw new IllegalArgumentException(Messages.getString("beans.6D"));
    }

    return ClassLoader.getSystemResourceAsStream(resourceName);
  }

  /**
   * Initializes all transient fields of this instance, called by constructors and <code>readObject()</code>.
   */
  protected void initialize()
  {
    // init transient fields
    bcmListeners = new ArrayList<BeanContextMembershipListener>();
    children = new HashMap();
    serializing = false;
    inNeedsGui = false;
    nonSerPCL = new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent event)
      {
        BeanContextSupport.this.propertyChange(event);
      }
    };
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.beancontext.BeanContext#instantiateChild(java.lang.String)
   */
  public Object instantiateChild(String beanName) throws IOException, ClassNotFoundException
  {
    return Beans.instantiate(getClass().getClassLoader(), beanName, getBeanContextPeer());
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.DesignMode#isDesignTime()
   */
  public boolean isDesignTime()
  {
    return designTime;
  }

  /*
   * (non-Javadoc)
   *
   * @see java.util.Collection#isEmpty()
   */
  public boolean isEmpty()
  {
    synchronized (children)
    {
      return children.isEmpty();
    }
  }

  /**
   * Returns true if this context is currently being serialized (by another thread).
   *
   * @return true if this context is currently being serialized (by another thread)
   */
  public boolean isSerializing()
  {
    return serializing;
  }

  /**
   * Returns an iterator of children of this context, with <code>remove()</code> disabled.
   *
   * @see java.util.Collection#iterator()
   */
  public Iterator iterator()
  {
    synchronized (children)
    {
      return new BCSIterator(children.keySet().iterator());
    }
  }

  /**
   * Returns true if this context or its children needs GUI to work properly.
   * <p>
   * The implementation checks the peer and all the children that implement <code>Visibility</code> to see if any of their <code>needsGui()</code> returns
   * true, and if any of the children extends <code>java.awt.Component</code>.
   * </p>
   *
   * @see com.googlecode.openbeans.Visibility#needsGui()
   */
  public boolean needsGui()
  {
    if (inNeedsGui)
    {
      return false;
    }
    inNeedsGui = true;

    try
    {
      if (getBeanContextPeer() != this)
      {
        if (getBeanContextPeer().needsGui())
        {
          return true;
        }
      }
      Object childs[] = copyChildren();
      for (int i = 0; i < childs.length; i++)
      {
        if (childs[i] instanceof Component)
        {
          return true;
        }
        Visibility v = getChildVisibility(childs[i]);
        if (v != null && v.needsGui())
        {
          return true;
        }
      }
      return false;
    }
    finally
    {
      inNeedsGui = false;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.Visibility#okToUseGui()
   */
  public void okToUseGui()
  {
    okToUseGui = true;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.PropertyChangeListener#propertyChange(com.googlecode.openbeans.PropertyChangeEvent)
   */
  public void propertyChange(PropertyChangeEvent event)
  {
    if (contains(event.getSource()) && "beanContext".equals(event.getPropertyName()) && event.getOldValue() == getBeanContextPeer())
    {
      remove(event.getSource(), false);
    }
  }

  /**
   * Deserializes children from the given object input stream.
   * <p>
   * The implementation reads pairs of child object and <code>BCSChild</code> object according to <code>serializable</code> property. For each pair, it is
   * added to the <code>children</code> map and the <code>childDeserializedHook()</code> is called. If the child implements <code>BeanContextChild</code>, its
   * <code>setBeanContext()</code> is also called.
   * </p>
   * <p>
   * This method is called by <code>readObject()</code> if the context works standalone. Or if this support object is a delegate of another
   * <code>BeanContext</code> implementation, then this method should be called by the peer. Doing this means that derialization can proceed without any
   * circular dependency problems.
   *
   * @param ois
   *            the object input stream
   * @throws IOException
   *             if I/O exception occurs
   * @throws ClassNotFoundException
   *             if class of read object is not found
   */
  @SuppressWarnings("unchecked")
  public final void readChildren(ObjectInputStream ois) throws IOException, ClassNotFoundException
  {
    synchronized (children)
    {
      for (int i = 0; i < serializable; i++)
      {
        Object child = ois.readObject();
        BCSChild childBCSC = (BCSChild) ois.readObject();
        children.put(child, childBCSC);

        childDeserializedHook(child, childBCSC);

        // set child's beanContext property
        BeanContextChild beanContextChild = getChildBeanContextChild(child);
        if (beanContextChild != null)
        {
          try
          {
            beanContextChild.setBeanContext(getBeanContextPeer());
          }
          catch (PropertyVetoException e)
          {
            throw new IOException(Messages.getString("beans.6B"));
          }
          // ensure no duplicate listener
          beanContextChild.removePropertyChangeListener("beanContext", nonSerPCL);
          // listen to child's beanContext change
          beanContextChild.addPropertyChangeListener("beanContext", nonSerPCL);
        }
      }
    }
  }

  /**
   * Removes the given child from this context.
   * <p>
   * Delegates to <code>remove(child, true)</code>.
   * </p>
   *
   * @param child
   *            a child of this context
   * @return true if the child is removed; or false if it is not a child of this context
   * @throws IllegalArgumentException
   *             if the child is null
   * @throws IllegalStateException
   *             if the child is not valid to remove
   * @see java.util.Collection#remove(java.lang.Object)
   */
  public boolean remove(Object child)
  {
    return remove(child, true);
  }

  /**
   * Removes the given child from this context.
   * <p>
   * If the given child is not a child of this context, simply returns false. Otherwise, <code>validatePendingRemove()</code> is called. If the removal is
   * valid, the child's <code>beanContext</code> property is updated (if required) and the child and its proxy peer (if there is one) is removed. Last,
   * <code>childJustRemovedHook()</code> is called and listeners are notified.
   * </p>
   *
   * @param child
   *            a child of this context
   * @param setChildBC
   *            whether to call <code>setBeanContext()</code> on the child or not
   * @return true if the child is removed; or false if it is not a child of this context
   * @throws IllegalArgumentException
   *             if the child is null
   * @throws IllegalStateException
   *             if the child is not valid to remove
   */
  protected boolean remove(Object child, boolean setChildBC)
  {
    if (child == null)
    {
      throw new IllegalArgumentException(Messages.getString("beans.67"));
    }

    Object peer = null;

    synchronized (globalHierarchyLock)
    {
      // check existence
      if (!contains(child))
      {
        return false;
      }

      // check serializing state
      if (serializing)
      {
        throw new IllegalStateException(Messages.getString("beans.68"));
      }

      // validate
      boolean valid = validatePendingRemove(child);
      if (!valid)
      {
        throw new IllegalStateException(Messages.getString("beans.6E"));
      }

      // set child's beanContext property
      BeanContextChild beanContextChild = getChildBeanContextChild(child);
      if (beanContextChild != null && setChildBC)
      {
        // remove listener, first
        beanContextChild.removePropertyChangeListener("beanContext", nonSerPCL);
        try
        {
          beanContextChild.setBeanContext(null);
        }
        catch (PropertyVetoException e)
        {
          // rollback the listener change
          beanContextChild.addPropertyChangeListener("beanContext", nonSerPCL);
          throw new IllegalStateException(Messages.getString("beans.6B"));
        }
      }

      // remove from children
      BCSChild childBCSC = null, peerBCSC = null;
      synchronized (children)
      {
        childBCSC = (BCSChild) children.remove(child);
        peer = childBCSC.proxyPeer;
        if (peer != null)
        {
          peerBCSC = (BCSChild) children.remove(peer);
        }
      }

      // trigger hook
      synchronized (child)
      {
        removeSerializable(childBCSC);
        childJustRemovedHook(child, childBCSC);
      }
      if (peer != null)
      {
        synchronized (peer)
        {
          removeSerializable(peerBCSC);
          childJustRemovedHook(peer, peerBCSC);
        }
      }
    }

    // notify listeners
    fireChildrenRemoved(new BeanContextMembershipEvent(getBeanContextPeer(), peer == null ? new Object[] { child } : new Object[] { child, peer }));
    return true;
  }

  /**
   * This method is unsupported, throws <code>UnsupportedOperationException</code>.
   *
   * @see java.util.Collection#removeAll(java.util.Collection)
   */
  public boolean removeAll(Collection collection)
  {
    throw new UnsupportedOperationException();
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * com.googlecode.openbeans.beancontext.BeanContext#removeBeanContextMembershipListener(com.googlecode.openbeans.beancontext.BeanContextMembershipListener)
   */
  public void removeBeanContextMembershipListener(BeanContextMembershipListener listener)
  {
    if (listener == null)
    {
      throw new NullPointerException();
    }
    synchronized (bcmListeners)
    {
      bcmListeners.remove(listener);
    }
  }

  /**
   * This method is unsupported, throws <code>UnsupportedOperationException</code>.
   *
   * @see java.util.Collection#retainAll(java.util.Collection)
   */
  public boolean retainAll(Collection collection)
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Serializes the given collection.
   * <p>
   * First writes a <code>int</code> indicating the number of all serializable elements (implements <code>Serializable</code>, then objects are writtern one
   * by one.
   * </p>
   *
   * @param oos
   *            the stream where the collection is writtern to
   * @param collection
   *            the collection to serialize
   * @throws IOException
   *             if I/O exception occurs
   */
  protected final void serialize(ObjectOutputStream oos, Collection collection) throws IOException
  {
    Object array[] = collection.toArray();
    int serCount = 0;
    for (int i = 0; i < array.length; i++)
    {
      if (array[i] instanceof Serializable)
      {
        serCount++;
      }
    }

    oos.writeInt(serCount);
    for (int i = 0; i < array.length; i++)
    {
      if (array[i] instanceof Serializable)
      {
        oos.writeObject(array[i]);
      }
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.DesignMode#setDesignTime(boolean)
   */
  public void setDesignTime(boolean designTime)
  {
    this.designTime = designTime;
  }

  /**
   * Sets the locale of this context. <code>VetoableChangeListener</code>s and <code>PropertyChangeListener</code>s are notified.
   *
   * @param newLocale
   *            the new locale to set
   * @throws PropertyVetoException
   *             if any <code>VetoableChangeListener</code> vetos this change
   */
  public void setLocale(Locale newLocale) throws PropertyVetoException
  {
    if (newLocale == null || newLocale == locale)
    {
      return; // ignore null locale
    }

    PropertyChangeEvent event = new PropertyChangeEvent(beanContextChildPeer, "locale", locale, newLocale);

    // apply change
    Locale oldLocale = locale;
    locale = newLocale;

    try
    {
      // notify vetoable listeners
      vcSupport.fireVetoableChange(event);
    }
    catch (PropertyVetoException e)
    {
      // rollback change
      locale = oldLocale;
      throw e;
    }
    // Notify BeanContext about this change
    this.pcSupport.firePropertyChange(event);
  }

  /**
   * Returns the number children of this context.
   *
   * @return the number children of this context
   * @see java.util.Collection#size()
   */
  public int size()
  {
    synchronized (children)
    {
      return children.size();
    }
  }

  /**
   * Returns an array of children of this context.
   *
   * @return an array of children of this context
   * @see java.util.Collection#toArray()
   */
  public Object[] toArray()
  {
    synchronized (children)
    {
      return children.keySet().toArray();
    }
  }

  /**
   * Returns an array of children of this context.
   *
   * @return an array of children of this context
   * @see java.util.Collection#toArray(java.lang.Object[])
   */
  @SuppressWarnings("unchecked")
  public Object[] toArray(Object[] array)
  {
    synchronized (children)
    {
      return children.keySet().toArray(array);
    }
  }

  /**
   * Validates the pending add of child.
   * <p>
   * Default implementation always returns true.
   * </p>
   *
   * @param child
   *            the child to be added
   * @return true if it is valid to add the child
   */
  protected boolean validatePendingAdd(Object child)
  {
    // to be overridden
    return true;
  }

  /**
   * Validates the pending removal of child.
   * <p>
   * Default implementation always returns true.
   * </p>
   *
   * @param child
   *            the child to be removed
   * @return true if it is valid to remove the child
   */
  protected boolean validatePendingRemove(Object child)
  {
    // to be overridden
    return true;
  }

  /*
   * (non-Javadoc)
   *
   * @see com.googlecode.openbeans.VetoableChangeListener#vetoableChange(com.googlecode.openbeans.PropertyChangeEvent)
   */
  public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException
  {
    if (pce == null)
    {
      throw new NullPointerException(Messages.getString("beans.1C")); //$NON-NLS-1$
    }
  }

  /**
   * Serializes children to the given object input stream.
   * <p>
   * The implementation iterates through all children and writes out pairs of child object and <code>BCSChild</code> object if the child is serializable
   * (implements <code>Serialization</code>.
   * </p>
   * <p>
   * This method is called by <code>writeObject()</code> if the context works standalone. Or if this support object is a delegate of another
   * <code>BeanContext</code> implementation, then this method should be called by the peer to avoid the 'chicken and egg' problem during deserialization.
   * </p>
   *
   * @param oos
   *            the stream to write
   * @throws IOException
   *             if I/O exception occurs
   */
  public final void writeChildren(ObjectOutputStream oos) throws IOException
  {
    boolean origSer = serializing;
    serializing = true;

    try
    {
      int count = 0;
      synchronized (children)
      {
        for (Iterator iter = children.values().iterator(); iter.hasNext();)
        {
          BCSChild bcsc = (BCSChild) iter.next();
          if (bcsc.child instanceof Serializable && (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable))
          {
            oos.writeObject(bcsc.child);
            oos.writeObject(bcsc);
            count++;
          }
        }
      }

      // what if count not equals to serializable?
      if (count != serializable)
      {
        throw new IOException(Messages.getString("beans.6F"));
      }
    }
    finally
    {
      serializing = origSer;
    }
  }

  /**
   * The implementation goes through following steps:
   * <p>
   * <ol>
   * <li>Writes out non-transient properties by calling <code>defaultWriteObject()</code>, especially the <code>serializable</code> indicating the number of
   * serializable children.</li>
   * <li>Calls <code>bcsPreSerializationHook()</code>.</li>
   * <li>Writes out children by calling <code>writeChildren()</code> if this context works standalone. Otherwise it is the peer's responsibility to call
   * <code>writeChildren()</code> after this object is serialized.</li>
   * <li>Writes out serializable membership listeners.</li>
   * </ol>
   * </p>
   *
   * @param oos
   *            the object output stream
   * @throws IOException
   *             if I/O exception occurs
   */
  private void writeObject(ObjectOutputStream oos) throws IOException
  {
    boolean origSer = serializing;
    serializing = true;

    try
    {
      oos.defaultWriteObject();

      bcsPreSerializationHook(oos);

      if (this == getBeanContextPeer())
      {
        writeChildren(oos);
      }

      synchronized (bcmListeners)
      {
        serialize(oos, bcmListeners);
      }
    }
    finally
    {
      serializing = origSer;
    }
  }

  /**
   * The implementation goes through following steps:
   * <p>
   * <ol>
   * <li>Reads non-transient properties by calling <code>defaultReadObject()</code>.</li>
   * <li>Calls <code>bcsPreDeserializationHook()</code>.</li>
   * <li>Reads children by calling <code>readChildren()</code> if this context works standalone. Otherwise it is the peer's responsibility to call
   * <code>readChildren()</code> after this object is deserialized.</li>
   * <li>Reads serializable membership listeners.</li>
   * </ol>
   * </p>
   *
   * @param ois
   *            the object input stream
   * @throws IOException
   *             if I/O error occurs
   * @throws ClassNotFoundException
   *             if class of read object is not found
   */
  private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
  {

    ois.defaultReadObject();

    initialize(); // init transient fields

    bcsPreDeserializationHook(ois);

    if (this == getBeanContextPeer())
    {
      readChildren(ois);
    }

    deserialize(ois, bcmListeners);
  }

  /*
   * Increase variable serializable if child and proxyPeer fields of the given BCSChild object are serializable
   */
  private void addSerializable(BCSChild bcsc)
  {
    if (bcsc.child instanceof Serializable && (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable))
    {
      serializable++;
    }
  }

  /*
   * Decrease variable serializable if child and proxyPeer fields of the given BCSChild object are serializable
   */
  private void removeSerializable(BCSChild bcsc)
  {
    if (serializable > 0 && bcsc.child instanceof Serializable && (bcsc.proxyPeer == null || bcsc.proxyPeer instanceof Serializable))
    {
      serializable--;
    }
  }

}
TOP

Related Classes of com.googlecode.openbeans.beancontext.BeanContextSupport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.