/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed 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 net.java.sip.communicator.service.protocol;
import java.beans.*;
import java.util.*;
import net.java.sip.communicator.service.protocol.event.*;
/**
* Provides implementations for some of the methods in the <tt>Call</tt>
* abstract class to facilitate implementations.
*
* @param <T> the peer extension class like for example <tt>CallPeerSipImpl</tt>
* or <tt>CallPeerJabberImpl</tt>
* @param <U> the provider extension class like for example
* <tt>ProtocolProviderServiceSipImpl</tt> or
* <tt>ProtocolProviderServiceJabberImpl</tt>
*
* @author Emil Ivov
* @author Lyubomir Marinov
*/
public abstract class AbstractCall<T extends CallPeer,
U extends ProtocolProviderService>
extends Call
{
/**
* The list of <tt>CallPeer</tt>s of this <tt>Call</tt>. It is implemented
* as a copy-on-write storage in order to optimize the implementation of
* {@link Call#getCallPeers()}. It represents private state which is to not
* be exposed to outsiders. An unmodifiable view which may safely be exposed
* to outsiders without the danger of
* <tt>ConcurrentModificationException</tt> is
* {@link #unmodifiableCallPeers}.
*/
private List<T> callPeers;
/**
* The <tt>Object</tt> which is used to synchronize the access to
* {@link #callPeers} and {@link #unmodifiableCallPeers}.
*/
private final Object callPeersSyncRoot = new Object();
/**
* The <tt>PropertyChangeSupport</tt> which helps this instance with
* <tt>PropertyChangeListener</tt>s.
*/
private final PropertyChangeSupport propertyChangeSupport
= new PropertyChangeSupport(this);
/**
* An unmodifiable view of {@link #callPeers}. It may safely be exposed to
* outsiders without the danger of <tt>ConcurrentModificationException</tt>
* and thus optimizes the implementation of {@link Call#getCallPeers()}.
*/
private List<T> unmodifiableCallPeers;
/**
* Creates a new Call instance.
*
* @param sourceProvider the proto provider that created us.
*/
protected AbstractCall(U sourceProvider)
{
super(sourceProvider);
callPeers = Collections.emptyList();
unmodifiableCallPeers = Collections.unmodifiableList(callPeers);
}
/**
* {@inheritDoc}
*
* Delegates to {@link #propertyChangeSupport}.
*/
@Override
public void addPropertyChangeListener(PropertyChangeListener listener)
{
propertyChangeSupport.addPropertyChangeListener(listener);
}
/**
* Adds a specific <tt>CallPeer</tt> to the list of <tt>CallPeer</tt>s of
* this <tt>Call</tt> if the list does not contain it; otherwise, does
* nothing. Does not fire {@link CallPeerEvent#CALL_PEER_ADDED}.
* <p>
* The method is named <tt>doAddCallPeer</tt> and not <tt>addCallPeer</tt>
* because, at the time of its introduction, multiple extenders have already
* defined an <tt>addCallPeer</tt> method with the same argument but with no
* return value.
* </p>
*
* @param callPeer the <tt>CallPeer</tt> to be added to the list of
* <tt>CallPeer</tt>s of this <tt>Call</tt>
* @return <tt>true</tt> if the list of <tt>CallPeer</tt>s of this
* <tt>Call</tt> was modified as a result of the execution of the method;
* otherwise, <tt>false</tt>
* @throws NullPointerException if <tt>callPeer</tt> is <tt>null</tt>
*/
protected boolean doAddCallPeer(T callPeer)
{
if (callPeer == null)
throw new NullPointerException("callPeer");
synchronized (callPeersSyncRoot)
{
if (callPeers.contains(callPeer))
return false;
else
{
/*
* The List of CallPeers of this Call is implemented as a
* copy-on-write storage in order to optimize the implementation
* of the Call.getCallPeers() method.
*/
List<T> newCallPeers = new ArrayList<T>(callPeers);
if (newCallPeers.add(callPeer))
{
callPeers = newCallPeers;
unmodifiableCallPeers
= Collections.unmodifiableList(callPeers);
return true;
}
else
return false;
}
}
}
/**
* Removes a specific <tt>CallPeer</tt> from the list of <tt>CallPeer</tt>s
* of this <tt>Call</tt> if the list does contain it; otherwise, does
* nothing. Does not fire {@link CallPeerEvent#CALL_PEER_REMOVED}.
* <p>
* The method is named <tt>doRemoveCallPeer</tt> and not
* <tt>removeCallPeer</tt> because, at the time of its introduction,
* multiple extenders have already defined a <tt>removeCallPeer</tt> method
* with the same argument but with no return value.
* </p>
*
* @param callPeer the <tt>CallPeer</tt> to be removed from the list of
* <tt>CallPeer</tt>s of this <tt>Call</tt>
* @return <tt>true</tt> if the list of <tt>CallPeer</tt>s of this
* <tt>Call</tt> was modified as a result of the execution of the method;
* otherwise, <tt>false</tt>
*/
protected boolean doRemoveCallPeer(T callPeer)
{
synchronized (callPeersSyncRoot)
{
/*
* The List of CallPeers of this Call is implemented as a
* copy-on-write storage in order to optimize the implementation of
* the Call.getCallPeers() method.
*/
List<T> newCallPeers = new ArrayList<T>(callPeers);
if (newCallPeers.remove(callPeer))
{
callPeers = newCallPeers;
unmodifiableCallPeers
= Collections.unmodifiableList(callPeers);
return true;
}
else
return false;
}
}
/**
* {@inheritDoc}
*
* Delegates to {@link #propertyChangeSupport}.
*/
@Override
protected void firePropertyChange(
String property,
Object oldValue, Object newValue)
{
propertyChangeSupport.firePropertyChange(property, oldValue, newValue);
}
/**
* Returns the number of peers currently associated with this call.
*
* @return an <tt>int</tt> indicating the number of peers currently
* associated with this call.
*/
@Override
public int getCallPeerCount()
{
return getCallPeerList().size();
}
/**
* Gets an unmodifiable <tt>List</tt> of the <tt>CallPeer</tt>s of this
* <tt>Call</tt>. The implementation of {@link Call#getCallPeers()} returns
* an <tt>Iterator</tt> over the same <tt>List</tt>.
*
* @return an unmodifiable <tt>List</tt> of the <tt>CallPeer</tt>s of this
* <tt>Call</tt>
*/
public List<T> getCallPeerList()
{
synchronized (callPeersSyncRoot)
{
return unmodifiableCallPeers;
}
}
/**
* Returns an <tt>Iterator</tt> over the (list of) <tt>CallPeer</tt>s of
* this <tt>Call</tt>. The returned <tt>Iterator</tt> operates over the
* <tt>List</tt> returned by {@link #getCallPeerList()}.
*
* @return an <tt>Iterator</tt> over the (list of) <tt>CallPeer</tt>s of
* this <tt>Call</tt>
*/
@Override
public Iterator<T> getCallPeers()
{
return getCallPeerList().iterator();
}
/**
* Returns a reference to the <tt>ProtocolProviderService</tt> instance
* that created this call.
*
* @return the <tt>ProtocolProviderService</tt> that created this call.
*/
@Override
@SuppressWarnings("unchecked")
public U getProtocolProvider()
{
return (U) super.getProtocolProvider();
}
/**
* {@inheritDoc}
*
* Delegates to {@link #propertyChangeSupport}.
*/
@Override
public void removePropertyChangeListener(PropertyChangeListener listener)
{
propertyChangeSupport.removePropertyChangeListener(listener);
}
}