/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.sip;
import java.util.*;
import javax.sip.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.media.*;
/**
* A SIP implementation of the Call abstract class encapsulating SIP dialogs.
*
* @author Emil Ivov
*/
public class CallSipImpl
extends Call
implements CallParticipantListener
{
private static final Logger logger = Logger.getLogger(CallSipImpl.class);
/**
* A list containing all <tt>CallParticipant</tt>s of this call.
*/
private Vector<CallParticipantSipImpl> callParticipants =
new Vector<CallParticipantSipImpl>();
/**
* The state that this call is currently in.
*/
private CallState callState = CallState.CALL_INITIALIZATION;
/**
* The <tt>CallSession</tt> that the media service has created for this
* call.
*/
private CallSession mediaCallSession = null;
/**
* Crates a CallSipImpl instance belonging to <tt>sourceProvider</tt> and
* initiated by <tt>CallCreator</tt>.
*
* @param sourceProvider the ProtocolProviderServiceSipImpl instance in the
* context of which this call has been created.
*/
protected CallSipImpl(ProtocolProviderServiceSipImpl sourceProvider)
{
super(sourceProvider);
}
/**
* Adds <tt>callParticipant</tt> to the list of participants in this call.
* If the call participant is already included in the call, the method has
* no effect.
*
* @param callParticipant the new <tt>CallParticipant</tt>
*/
public void addCallParticipant(CallParticipantSipImpl callParticipant)
{
if (callParticipants.contains(callParticipant))
return;
callParticipant.addCallParticipantListener(this);
this.callParticipants.add(callParticipant);
fireCallParticipantEvent(callParticipant,
CallParticipantEvent.CALL_PARTICIPANT_ADDED);
}
/**
* Removes <tt>callParticipant</tt> from the list of participants in this
* call. The method has no effect if there was no such participant in the
* call.
*
* @param callParticipant the <tt>CallParticipant</tt> leaving the call;
*/
public void removeCallParticipant(CallParticipantSipImpl callParticipant)
{
if (!callParticipants.contains(callParticipant))
return;
this.callParticipants.remove(callParticipant);
callParticipant.removeCallParticipantListener(this);
try
{
fireCallParticipantEvent(callParticipant,
CallParticipantEvent.CALL_PARTICIPANT_REMVOVED);
}
finally
{
/*
* The participant should loose its state once it has finished
* firing its events in order to allow the listeners to undo.
*/
callParticipant.setCall(null);
}
if (callParticipants.size() == 0)
setCallState(CallState.CALL_ENDED);
}
/**
* Sets the state of this call and fires a call change event notifying
* registered listeners for the change.
*
* @param newState a reference to the <tt>CallState</tt> instance that the
* call is to enter.
*/
public void setCallState(CallState newState)
{
CallState oldState = getCallState();
if (oldState == newState)
return;
this.callState = newState;
fireCallChangeEvent(CallChangeEvent.CALL_STATE_CHANGE, oldState,
newState);
}
/**
* Returns the state that this call is currently in.
*
* @return a reference to the <tt>CallState</tt> instance that the call is
* currently in.
*/
public CallState getCallState()
{
return callState;
}
/**
* Returns an iterator over all call participants.
*
* @return an Iterator over all participants currently involved in the call.
*/
public Iterator<CallParticipant> getCallParticipants()
{
return new LinkedList<CallParticipant>(callParticipants).iterator();
}
/**
* Returns the number of participants currently associated with this call.
*
* @return an <tt>int</tt> indicating the number of participants currently
* associated with this call.
*/
public int getCallParticipantsCount()
{
return callParticipants.size();
}
/**
* Dummy implementation of a method (inherited from CallParticipantListener)
* that we don't need.
*
* @param evt unused.
*/
public void participantImageChanged(CallParticipantChangeEvent evt)
{
}
/**
* Dummy implementation of a method (inherited from CallParticipantListener)
* that we don't need.
*
* @param evt unused.
*/
public void participantAddressChanged(CallParticipantChangeEvent evt)
{
}
/**
* Dummy implementation of a method (inherited from CallParticipantListener)
* that we don't need.
*
* @param evt unused.
*/
public void participantTransportAddressChanged(
CallParticipantChangeEvent evt)
{
}
/**
* Dummy implementation of a method (inherited from CallParticipantListener)
* that we don't need.
*
* @param evt unused.
*/
public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
{
}
/**
* Verifies whether the call participant has entered a state.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new status.
*/
public void participantStateChanged(CallParticipantChangeEvent evt)
{
CallParticipantState newState =
(CallParticipantState) evt.getNewValue();
if (newState == CallParticipantState.DISCONNECTED
|| newState == CallParticipantState.FAILED)
{
removeCallParticipant((CallParticipantSipImpl) evt
.getSourceCallParticipant());
}
else if ((newState == CallParticipantState.CONNECTED
|| newState == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA))
{
setCallState(CallState.CALL_IN_PROGRESS);
}
}
/**
* Returns <tt>true</tt> if <tt>dialog</tt> matches the jain sip dialog
* established with one of the participants in this call.
*
* @param dialog the dialog whose corresponding participant we're looking
* for.
* @return true if this call contains a call participant whose jain sip
* dialog is the same as the specified and false otherwise.
*/
public boolean contains(Dialog dialog)
{
return findCallParticipant(dialog) != null;
}
/**
* Returns the call participant whose associated jain sip dialog matches
* <tt>dialog</tt>.
*
* @param dialog the jain sip dialog whose corresponding participant we're
* looking for.
* @return the call participant whose jain sip dialog is the same as the
* specified or null if no such call participant was found.
*/
public CallParticipantSipImpl findCallParticipant(Dialog dialog)
{
Iterator callParticipants = this.getCallParticipants();
if (logger.isTraceEnabled())
{
logger.trace("Looking for participant with dialog: " + dialog
+ "among " + this.callParticipants.size() + " calls");
}
while (callParticipants.hasNext())
{
CallParticipantSipImpl cp =
(CallParticipantSipImpl) callParticipants.next();
if (cp.getDialog() == dialog)
{
logger.trace("Returing cp=" + cp);
return cp;
}
else
{
logger.trace("Ignoring cp=" + cp + " because cp.dialog="
+ cp.getDialog() + " while dialog=" + dialog);
}
}
return null;
}
/**
* Sets the <tt>CallSession</tt> that the media service has created for this
* call.
*
* @param callSession the <tt>CallSession</tt> that the media service has
* created for this call.
*/
public void setMediaCallSession(CallSession callSession)
{
this.mediaCallSession = callSession;
}
/**
* Sets the <tt>CallSession</tt> that the media service has created for this
* call.
*
* @return the <tt>CallSession</tt> that the media service has created for
* this call or null if no call session has been created so far.
*/
public CallSession getMediaCallSession()
{
return this.mediaCallSession;
}
}