/**
   * Notifies this instance that there was a change in the value of a property of an
   * <tt>Endpoint</tt> participating in this multipoint conference.
   *
   * @param endpoint the <tt>Endpoint</tt> which is the source of the event/notification and is
   *     participating in this multipoint conference
   * @param ev a <tt>PropertyChangeEvent</tt> which specifies the source of the event/notification,
   *     the name of the property and the old and new values of that property
   */
  private void endpointPropertyChange(Endpoint endpoint, PropertyChangeEvent ev) {
    String propertyName = ev.getPropertyName();
    boolean maybeRemoveEndpoint;

    if (Endpoint.SCTP_CONNECTION_PROPERTY_NAME.equals(propertyName)) {
      // The SctpConnection of/associated with an Endpoint has changed. We
      // may want to fire initial events over that SctpConnection (as soon
      // as it is ready).
      SctpConnection oldValue = (SctpConnection) ev.getOldValue();
      SctpConnection newValue = (SctpConnection) ev.getNewValue();

      endpointSctpConnectionChanged(endpoint, oldValue, newValue);

      // The SctpConnection may have expired.
      maybeRemoveEndpoint = (newValue == null);
    } else if (Endpoint.CHANNELS_PROPERTY_NAME.equals(propertyName)) {
      // An RtpChannel may have expired.
      maybeRemoveEndpoint = true;
    } else {
      maybeRemoveEndpoint = false;
    }
    if (maybeRemoveEndpoint) {
      // It looks like there is a chance that the Endpoint may have
      // expired. Endpoints are held by this Conference via WeakReferences
      // but WeakReferences are unpredictable. We have functionality
      // though which could benefit from discovering that an Endpoint has
      // expired as quickly as possible (e.g. ConferenceSpeechActivity).
      // Consequently, try to expedite the removal of expired Endpoints.
      if (endpoint.getSctpConnection() == null && endpoint.getChannelCount(null) == 0) {
        removeEndpoint(endpoint);
      }
    }
  }
  /**
   * Notifies this instance that there was a change in the value of a property of {@link
   * #speechActivity}.
   *
   * @param ev a <tt>PropertyChangeEvent</tt> which specifies the source of the event/notification,
   *     the name of the property and the old and new values of that property
   */
  private void speechActivityPropertyChange(PropertyChangeEvent ev) {
    String propertyName = ev.getPropertyName();

    if (ConferenceSpeechActivity.DOMINANT_ENDPOINT_PROPERTY_NAME.equals(propertyName)) {
      // The dominant speaker in this Conference has changed. We will
      // likely want to notify the Endpoints participating in this
      // Conference.
      dominantSpeakerChanged();
    } else if (ConferenceSpeechActivity.ENDPOINTS_PROPERTY_NAME.equals(propertyName)) {
      speechActivityEndpointsChanged();
    }
  }
  /**
   * Notifies this instance that there was a change in the value of a property of an object in which
   * this instance is interested.
   *
   * @param ev a <tt>PropertyChangeEvent</tt> which specifies the object of interest, the name of
   *     the property and the old and new values of that property
   */
  @Override
  public void propertyChange(PropertyChangeEvent ev) {
    Object source = ev.getSource();

    if (isExpired()) {
      // An expired Conference is to be treated like a null Conference
      // i.e. it does not handle any PropertyChangeEvents. If possible,
      // make sure that no further PropertyChangeEvents will be delivered
      // to this Conference.
      if (source instanceof PropertyChangeNotifier) {
        ((PropertyChangeNotifier) source).removePropertyChangeListener(propertyChangeListener);
      }
    } else if (source == speechActivity) {
      speechActivityPropertyChange(ev);
    } else if (source instanceof Endpoint) {
      // We care about PropertyChangeEvents from Endpoint but only if the
      // Endpoint in question is still participating in this Conference.
      Endpoint endpoint = getEndpoint(((Endpoint) source).getID());

      if (endpoint != null) endpointPropertyChange(endpoint, ev);
    }
  }