/**
  * Check if there are any listeners for a specific property, including those registered on all
  * properties. If <code>propertyName</code> is null, only check for listeners registered on all
  * properties.
  *
  * @param propertyName the property name.
  * @return true if there are one or more listeners for the given property
  */
 public synchronized boolean hasListeners(String propertyName) {
   if (listeners != null && !listeners.isEmpty()) {
     // there is a generic listener
     return true;
   }
   if (children != null && propertyName != null) {
     PropertyChangeSupport child = (PropertyChangeSupport) children.get(propertyName);
     if (child != null && child.listeners != null) {
       return !child.listeners.isEmpty();
     }
   }
   return false;
 }
  /**
   * @serialData Null terminated list of <code>PropertyChangeListeners</code>.
   *     <p>At serialization time we skip non-serializable listeners and only serialize the
   *     serializable listeners.
   */
  private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();

    if (listeners != null) {
      Object[] list = listeners.getListenersCopy();

      for (int i = 0; i < list.length; i++) {
        PropertyChangeListener l = (PropertyChangeListener) list[i];
        if (l instanceof Serializable) {
          s.writeObject(l);
        }
      }
    }
    s.writeObject(null);
  }
  /**
   * Remove a PropertyChangeListener from the listener list. This removes a PropertyChangeListener
   * that was registered for all properties. If <code>listener</code> was added more than once to
   * the same event source, it will be notified one less time after being removed. If <code>listener
   * </code> is null, or was never added, no exception is thrown and no action is taken.
   *
   * @param listener The PropertyChangeListener to be removed
   */
  public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
    if (listener == null) {
      return;
    }

    if (listener instanceof PropertyChangeListenerProxy) {
      PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener;
      // Call two argument remove method.
      removePropertyChangeListener(
          proxy.getPropertyName(), (PropertyChangeListener) proxy.getListener());
    } else {
      if (listeners == null) {
        return;
      }
      listeners.remove(listener);
    }
  }
  /**
   * Add a PropertyChangeListener to the listener list. The listener is registered for all
   * properties. The same listener object may be added more than once, and will be called as many
   * times as it is added. If <code>listener</code> is null, no exception is thrown and no action is
   * taken.
   *
   * @param listener The PropertyChangeListener to be added
   */
  public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
    if (listener == null) {
      return;
    }

    if (listener instanceof PropertyChangeListenerProxy) {
      PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener;
      // Call two argument add method.
      addPropertyChangeListener(
          proxy.getPropertyName(), (PropertyChangeListener) proxy.getListener());
    } else {
      if (listeners == null) {
        listeners = new EventListenerAggregate(PropertyChangeListener.class);
      }
      listeners.add(listener);
    }
  }
  /**
   * Returns an array of all the listeners that were added to the PropertyChangeSupport object with
   * addPropertyChangeListener().
   *
   * <p>If some listeners have been added with a named property, then the returned array will be a
   * mixture of PropertyChangeListeners and <code>PropertyChangeListenerProxy</code>s. If the
   * calling method is interested in distinguishing the listeners then it must test each element to
   * see if it's a <code>PropertyChangeListenerProxy</code>, perform the cast, and examine the
   * parameter.
   *
   * <pre>
   * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
   * for (int i = 0; i < listeners.length; i++) {
   *  if (listeners[i] instanceof PropertyChangeListenerProxy) {
   *     PropertyChangeListenerProxy proxy =
   *                    (PropertyChangeListenerProxy)listeners[i];
   *     if (proxy.getPropertyName().equals("foo")) {
   *       // proxy is a PropertyChangeListener which was associated
   *       // with the property named "foo"
   *     }
   *   }
   * }
   * </pre>
   *
   * @see PropertyChangeListenerProxy
   * @return all of the <code>PropertyChangeListeners</code> added or an empty array if no listeners
   *     have been added
   * @since 1.4
   */
  public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
    List returnList = new ArrayList();

    // Add all the PropertyChangeListeners
    if (listeners != null) {
      returnList.addAll(Arrays.asList(listeners.getListenersInternal()));
    }

    // Add all the PropertyChangeListenerProxys
    if (children != null) {
      Iterator iterator = children.keySet().iterator();
      while (iterator.hasNext()) {
        String key = (String) iterator.next();
        PropertyChangeSupport child = (PropertyChangeSupport) children.get(key);
        PropertyChangeListener[] childListeners = child.getPropertyChangeListeners();
        for (int index = childListeners.length - 1; index >= 0; index--) {
          returnList.add(new PropertyChangeListenerProxy(key, childListeners[index]));
        }
      }
    }
    return (PropertyChangeListener[]) (returnList.toArray(new PropertyChangeListener[0]));
  }
  /**
   * Fire an existing PropertyChangeEvent to any registered listeners. No event is fired if the
   * given event's old and new values are equal and non-null.
   *
   * @param evt The PropertyChangeEvent object.
   */
  public void firePropertyChange(PropertyChangeEvent evt) {
    Object oldValue = evt.getOldValue();
    Object newValue = evt.getNewValue();
    String propertyName = evt.getPropertyName();
    if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
      return;
    }

    if (listeners != null) {
      Object[] list = listeners.getListenersInternal();
      for (int i = 0; i < list.length; i++) {
        PropertyChangeListener target = (PropertyChangeListener) list[i];
        target.propertyChange(evt);
      }
    }

    if (children != null && propertyName != null) {
      PropertyChangeSupport child = null;
      child = (PropertyChangeSupport) children.get(propertyName);
      if (child != null) {
        child.firePropertyChange(evt);
      }
    }
  }