//************************************************************************* // Method: DataGridViewRowToEmailParticipantCriteria() // /// <summary> /// Creates a <see cref="EmailParticipantCriteria" /> object from the /// contents of a DataGridViewRow from the dgvParticipants DataGridView. /// </summary> /// /// <returns> /// A <see cref="EmailParticipantCriteria" /> object. Important: The /// DataGridViewRow is not validated and the returned object may contain /// invalid data. /// </returns> //************************************************************************* protected EmailParticipantCriteria DataGridViewRowToEmailParticipantCriteria( DataGridViewRow oRow ) { Debug.Assert(oRow != null); AssertValid(); EmailParticipantCriteria oEmailParticipantCriteria = new EmailParticipantCriteria(); String sParticipant = (String)oRow.Cells[this.colParticipant.Name].Value; if ( !String.IsNullOrEmpty(sParticipant) ) { // Trim the participant string. sParticipant = sParticipant.Trim(); oRow.Cells[this.colParticipant.Name].Value = sParticipant; } if ( !String.IsNullOrEmpty(sParticipant) ) { sParticipant = ParticipantToAnalyzer(sParticipant); } oEmailParticipantCriteria.Participant = sParticipant; IncludedIn eIncludedIn = IncludedIn.None; if ( DataGridViewCheckBoxCellIsChecked(oRow, this.colFrom.Name) ) { eIncludedIn |= IncludedIn.From; } if ( DataGridViewCheckBoxCellIsChecked(oRow, this.colTo.Name) ) { eIncludedIn |= IncludedIn.To; } if ( DataGridViewCheckBoxCellIsChecked(oRow, this.colCc.Name) ) { eIncludedIn |= IncludedIn.Cc; } if ( DataGridViewCheckBoxCellIsChecked(oRow, this.colBcc.Name) ) { eIncludedIn |= IncludedIn.Bcc; } oEmailParticipantCriteria.IncludedIn = eIncludedIn; return (oEmailParticipantCriteria); }
//************************************************************************* // Method: AnalyzeEmailNetworkInternal() // /// <summary> /// Analyzes the part of a user's email social network that satisfies /// specified criteria. /// </summary> /// /// <param name="participantsCriteria"> /// See the synchronous method. /// </param> /// /// <param name="startTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="endTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="subjectText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="bodyText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="folder"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="minimumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="maximumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="attachmentFilter"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="hasCc"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="hasBcc"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="isReplyFromParticipant1"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="useCcForEdgeWeights"> /// See the synchronous method. /// </param> /// /// <param name="useBccForEdgeWeights"> /// See the synchronous method. /// </param> /// /// <param name="backgroundWorker"> /// A BackgroundWorker object if this method is being called /// asynchronously, or null if it is being called synchronously. /// </param> /// /// <param name="doWorkEventArgs"> /// A DoWorkEventArgs object if this method is being called /// asynchronously, or null if it is being called synchronously. /// </param> /// /// <returns> /// See AnalyzeEmailNetwork(). /// </returns> //************************************************************************* protected EmailParticipantPair[] AnalyzeEmailNetworkInternal( EmailParticipantCriteria [] participantsCriteria, Nullable<DateTime> startTime, Nullable<DateTime> endTime, String subjectText, String bodyText, String folder, Nullable<Int64> minimumSize, Nullable<Int64> maximumSize, Nullable<AttachmentFilter> attachmentFilter, Nullable<Boolean> hasCc, Nullable<Boolean> hasBcc, Nullable<Boolean> isReplyFromParticipant1, Boolean useCcForEdgeWeights, Boolean useBccForEdgeWeights, BackgroundWorker backgroundWorker, DoWorkEventArgs doWorkEventArgs ) { CheckAnalyzeMethodArguments(participantsCriteria, startTime, endTime, subjectText, bodyText, folder, minimumSize, maximumSize, attachmentFilter, isReplyFromParticipant1); Debug.Assert(backgroundWorker == null || doWorkEventArgs != null); AssertValid(); String sQuery = CreateQuery(participantsCriteria, startTime, endTime, subjectText, bodyText, folder, minimumSize, maximumSize, attachmentFilter, hasCc, hasBcc, isReplyFromParticipant1); OleDbDataReader oDataReader = GetDataReader(sQuery); EmailParticipantPair [] aoEmailParticipantPairs = null; try { aoEmailParticipantPairs = AnalyzeEmailNetworkInternal( oDataReader, useCcForEdgeWeights, useBccForEdgeWeights, backgroundWorker, doWorkEventArgs); } finally { oDataReader.Close(); } return (aoEmailParticipantPairs); }
//************************************************************************* // Method: AnalyzeEmailNetworkAsync() // /// <summary> /// Asynchronously analyzes the part of a user's email social network that /// satisfies specified criteria. /// </summary> /// /// <param name="participantsCriteria"> /// See the synchronous method. /// </param> /// /// <param name="startTime"> /// See the synchronous method. /// </param> /// /// <param name="endTime"> /// See the synchronous method. /// </param> /// /// <param name="subjectText"> /// See the synchronous method. /// </param> /// /// <param name="bodyText"> /// See the synchronous method. /// </param> /// /// <param name="folder"> /// See the synchronous method. /// </param> /// /// <param name="minimumSize"> /// See the synchronous method. /// </param> /// /// <param name="maximumSize"> /// See the synchronous method. /// </param> /// /// <param name="attachmentFilter"> /// See the synchronous method. /// </param> /// /// <param name="hasCc"> /// See the synchronous method. /// </param> /// /// <param name="hasBcc"> /// See the synchronous method. /// </param> /// /// <param name="isReplyFromParticipant1"> /// See the synchronous method. /// </param> /// /// <param name="useCcForEdgeWeights"> /// See the synchronous method. /// </param> /// /// <param name="useBccForEdgeWeights"> /// See the synchronous method. /// </param> /// /// <remarks> /// When the analysis completes, the <see cref="AnalysisCompleted" /> event /// fires. The <see cref="RunWorkerCompletedEventArgs.Result" /> property /// will return an array of zero or more <see cref="EmailParticipantPair" /> /// objects, one for each pair of participants in the user's email social /// network that satisfy the specified criteria. The property is never /// null. /// /// <para> /// To cancel the analysis, call <see cref="CancelAsync" />. /// </para> /// /// </remarks> //************************************************************************* public void AnalyzeEmailNetworkAsync( EmailParticipantCriteria [] participantsCriteria, Nullable<DateTime> startTime, Nullable<DateTime> endTime, String subjectText, String bodyText, String folder, Nullable<Int64> minimumSize, Nullable<Int64> maximumSize, Nullable<AttachmentFilter> attachmentFilter, Nullable<Boolean> hasCc, Nullable<Boolean> hasBcc, Nullable<Boolean> isReplyFromParticipant1, Boolean useCcForEdgeWeights, Boolean useBccForEdgeWeights ) { AssertValid(); const String MethodName = "AnalyzeEmailNetworkAsync"; if (this.IsBusy) { throw new InvalidOperationException( String.Format( "{0}:{1}: An asynchronous operation is already in progress." , this.ClassName, MethodName ) ); } // Wrap the arguments in an object that can be passed to // BackgroundWorker.RunWorkerAsync(). AnalyzeEmailNetworkAsyncArgs oAnalyzeEmailNetworkAsyncArgs = new AnalyzeEmailNetworkAsyncArgs(); oAnalyzeEmailNetworkAsyncArgs.ParticipantsCriteria = participantsCriteria; oAnalyzeEmailNetworkAsyncArgs.StartTime = startTime; oAnalyzeEmailNetworkAsyncArgs.EndTime = endTime; oAnalyzeEmailNetworkAsyncArgs.SubjectText = subjectText; oAnalyzeEmailNetworkAsyncArgs.BodyText = bodyText; oAnalyzeEmailNetworkAsyncArgs.Folder = folder; oAnalyzeEmailNetworkAsyncArgs.MinimumSize = minimumSize; oAnalyzeEmailNetworkAsyncArgs.MaximumSize = maximumSize; oAnalyzeEmailNetworkAsyncArgs.AttachmentFilter = attachmentFilter; oAnalyzeEmailNetworkAsyncArgs.HasCc = hasCc; oAnalyzeEmailNetworkAsyncArgs.HasBcc = hasBcc; oAnalyzeEmailNetworkAsyncArgs.IsReplyFromParticipant1 = isReplyFromParticipant1; oAnalyzeEmailNetworkAsyncArgs.UseCcForEdgeWeights = useCcForEdgeWeights; oAnalyzeEmailNetworkAsyncArgs.UseBccForEdgeWeights = useBccForEdgeWeights; // Create a BackgroundWorker and handle its events. m_oBackgroundWorker = new BackgroundWorker(); m_oBackgroundWorker.WorkerSupportsCancellation = true; m_oBackgroundWorker.DoWork += new DoWorkEventHandler( BackgroundWorker_DoWork); m_oBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( BackgroundWorker_RunWorkerCompleted); m_oBackgroundWorker.RunWorkerAsync(oAnalyzeEmailNetworkAsyncArgs); }
//************************************************************************* // Method: AnalyzeEmailNetwork() // /// <summary> /// Synchronously analyzes the part of a user's email social network that /// satisfies specified criteria. /// </summary> /// /// <param name="participantsCriteria"> /// An array of <see cref="EmailParticipantCriteria" /> objects, one for /// each participant to filter on, or null to not filter on participants. /// For each element in the array, <see /// cref="EmailParticipantCriteria.Participant" /> must be specified, but /// <see cref="EmailParticipantCriteria.IncludedIn" /> can be <see /// cref="IncludedIn.None" />. /// </param> /// /// <param name="startTime"> /// If specified, only emails sent on or after the specified time are /// included in the aggregated results. Use null to specify "no start /// time." /// </param> /// /// <param name="endTime"> /// If specified, only emails sent on or before the specified time are /// included in the aggregated results. Use null to specify "no end time." /// </param> /// /// <param name="subjectText"> /// Subject text to filter on, or null to not filter on subject text. /// Can't be an empty string. If specified, only emails that include the /// specified subject text are included in the aggregated results. /// </param> /// /// <param name="bodyText"> /// Body text to filter on, or null to not filter on body text. Can't be /// an empty string. If specified, only emails that include the specified /// body text are included in the aggregated results. /// </param> /// /// <param name="folder"> /// Email folder to filter on, or null to not filter on the email folder. /// Can't be an empty string. If specified, only emails in the specified /// folder are included in the aggregated results. Sample: "Inbox". /// </param> /// /// <param name="minimumSize"> /// If specified, only emails that have a size greater than or equal to the /// specified value are included in the aggregated results. /// </param> /// /// <param name="maximumSize"> /// If specified, only emails that have a size less than or equal to the /// specified value are included in the aggregated results. /// </param> /// /// <param name="attachmentFilter"> /// If not null, specifies how attachments are used to determine which /// emails are included in the aggregated results. Use null to specify /// "don't care." /// </param> /// /// <param name="hasCc"> /// If specified, only emails that have or don't have a Cc line are /// included in the aggregated results. Use null to specify "don't care." /// </param> /// /// <param name="hasBcc"> /// If specified, only emails that have or don't have a Bcc line are /// included in the aggregated results. Use null to specify "don't care." /// </param> /// /// <param name="isReplyFromParticipant1"> /// If specified, only emails that are or are not replies from <paramref /// name="participant1" /> are included in the aggregated results. /// Use null to specify "don't care." /// /// <para> /// [IMPORTANT NOTE: As of April 2008, the System.Message.IsFwdOrReply /// message property needed to implement this is always null. Specifying /// true or false for this parameter will always return an empty array. /// The parameter is retained in case the missing property is fixed in a /// future version of Windows Desktop Search.] /// </para> /// /// </param> /// /// <param name="useCcForEdgeWeights"> /// If true, an edge weight of one is assigned to the sender and each /// participant on the Cc line. (An edge weight of one is always assigned /// to the sender and each participant on the To line.) /// </param> /// /// <param name="useBccForEdgeWeights"> /// If true, an edge weight of one is assigned to the sender and each /// participant on the Bcc line. (An edge weight of one is always assigned /// to the sender and each participant on the To line.) /// </param> /// /// <returns> /// An array of zero or more <see cref="EmailParticipantPair" /> objects, /// one for each pair of participants in the user's email social network /// that satisfy the specified criteria. The return value is never null. /// </returns> /// /// <remarks> /// This overload does the same thing as the <see /// cref="AnalyzeEmailNetwork()" /> overload, but only those emails that /// match the specified criteria are aggregated. /// </remarks> //************************************************************************* public EmailParticipantPair[] AnalyzeEmailNetwork( EmailParticipantCriteria [] participantsCriteria, Nullable<DateTime> startTime, Nullable<DateTime> endTime, String subjectText, String bodyText, String folder, Nullable<Int64> minimumSize, Nullable<Int64> maximumSize, Nullable<AttachmentFilter> attachmentFilter, Nullable<Boolean> hasCc, Nullable<Boolean> hasBcc, Nullable<Boolean> isReplyFromParticipant1, Boolean useCcForEdgeWeights, Boolean useBccForEdgeWeights ) { AssertValid(); return ( AnalyzeEmailNetworkInternal(participantsCriteria, startTime, endTime, subjectText, bodyText, folder, minimumSize, maximumSize, attachmentFilter, hasCc, hasBcc, isReplyFromParticipant1, useCcForEdgeWeights, useBccForEdgeWeights, null, null) ); }
//************************************************************************* // Method: CreateQuery() // /// <summary> /// Creates a query that uses specified criteria. /// </summary> /// /// <param name="participantsCriteria"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="startTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="endTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="subjectText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="bodyText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="folder"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="minimumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="maximumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="attachmentFilter"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="hasCc"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="hasBcc"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="isReplyFromParticipant1"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <returns> /// A query that uses the specified criteria. /// </returns> //************************************************************************* protected String CreateQuery( EmailParticipantCriteria [] participantsCriteria, Nullable<DateTime> startTime, Nullable<DateTime> endTime, String subjectText, String bodyText, String folder, Nullable<Int64> minimumSize, Nullable<Int64> maximumSize, Nullable<AttachmentFilter> attachmentFilter, Nullable<Boolean> hasCc, Nullable<Boolean> hasBcc, Nullable<Boolean> isReplyFromParticipant1 ) { AssertValid(); StringBuilder oStringBuilder = new StringBuilder(); oStringBuilder.Append(BaseQuery); String sEscapedFirstParticipant = null; Int32 i = 0; StringBuilder oParticipantCriteriaStringBuilder = new StringBuilder(); if (participantsCriteria != null) { foreach (EmailParticipantCriteria oParticipantCriteria in participantsCriteria) { String sEscapedParticipant = EscapeStringForContains(oParticipantCriteria.Participant); Debug.Assert( !String.IsNullOrEmpty(sEscapedParticipant) ); if (i == 0) { sEscapedFirstParticipant = sEscapedParticipant; } i++; IncludedIn eIncludedIn = oParticipantCriteria.IncludedIn; if (eIncludedIn == IncludedIn.None) { // Skip this one. continue; } if (oParticipantCriteriaStringBuilder.Length > 0) { // The participants are ORed together. oParticipantCriteriaStringBuilder.Append(" OR "); } // This part of the query will look like this, in pseudocode: // // ( Contains(To, Participant) // OR Contains(From, Participant) OR Contains(Cc, Participant) // OR Contains(Bcc, Participant) ) // Open the outer clause. oParticipantCriteriaStringBuilder.Append("("); // Append a clause for each IncludedIn flag. Boolean bParticipantClauseAppended = false; AppendParticipantClauseToQuery(sEscapedParticipant, eIncludedIn, IncludedIn.From, oParticipantCriteriaStringBuilder, ref bParticipantClauseAppended); AppendParticipantClauseToQuery(sEscapedParticipant, eIncludedIn, IncludedIn.To, oParticipantCriteriaStringBuilder, ref bParticipantClauseAppended); AppendParticipantClauseToQuery(sEscapedParticipant, eIncludedIn, IncludedIn.Cc, oParticipantCriteriaStringBuilder, ref bParticipantClauseAppended); AppendParticipantClauseToQuery(sEscapedParticipant, eIncludedIn, IncludedIn.Bcc, oParticipantCriteriaStringBuilder, ref bParticipantClauseAppended); // Close the outer clause. oParticipantCriteriaStringBuilder.Append(")"); } if (oParticipantCriteriaStringBuilder.Length > 0) { oStringBuilder.AppendFormat( " AND ({0}) " , oParticipantCriteriaStringBuilder ); } } const String DateSentClause = " AND System.Message.DateSent {0} '{1}'" ; if (startTime.HasValue) { oStringBuilder.AppendFormat( DateSentClause , ">=", FormatTime(startTime.Value) ); } if (endTime.HasValue) { oStringBuilder.AppendFormat( DateSentClause , "<=", FormatTime(endTime.Value) ); } if ( !String.IsNullOrEmpty(subjectText) ) { // Important note: // // System.Subject works for the subject on Vista with its original // search engine, and with Windows Desktop Search 4.0. On XP with // Windows.Desktop.Search, however, System.Subject does not work. // System.Title works in Vista/WDS4.0 and XP/WDS4.0. (It has not // yet been tested on Vista with its original search engine). oStringBuilder.AppendFormat( " AND Contains(System.Title, '\"{0}\"')" , EscapeStringForContains(subjectText) ); } if ( !String.IsNullOrEmpty(bodyText) ) { oStringBuilder.AppendFormat( " AND Contains ('\"{0}\"')" , EscapeStringForContains(bodyText) ); } if ( !String.IsNullOrEmpty(folder) ) { oStringBuilder.AppendFormat( " AND System.ItemFolderPathDisplay = '{0}'" , EscapeStringForEquals(folder) ); } if (minimumSize.HasValue) { oStringBuilder.AppendFormat( " AND System.Size >= {0}" , minimumSize.Value ); } if (maximumSize.HasValue) { oStringBuilder.AppendFormat( " AND System.Size <= {0}" , maximumSize.Value ); } if (attachmentFilter.HasValue) { oStringBuilder.AppendFormat( " AND System.Message.HasAttachments = {0}" , attachmentFilter.Value == AttachmentFilter.NoAttachment ? "FALSE" : "TRUE" ); if (attachmentFilter.Value == AttachmentFilter.HasAttachmentFromParticipant1) { Debug.Assert( !String.IsNullOrEmpty(sEscapedFirstParticipant) ); oStringBuilder.AppendFormat( " AND Contains(System.Message.FromAddress, '\"{0}\"')" , sEscapedFirstParticipant ); } } if (hasCc.HasValue) { oStringBuilder.AppendFormat( " AND System.Message.CcAddress IS {0} NULL" , hasCc.Value ? "NOT": String.Empty ); } if (hasBcc.HasValue) { oStringBuilder.AppendFormat( " AND System.Message.BccAddress IS {0} NULL" , hasBcc.Value ? "NOT": String.Empty ); } if (isReplyFromParticipant1.HasValue) { // IMPORTANT NOTE: As of April 2008, the // System.Message.IsFwdOrReply message property needed to implement // this is always null, so specifying a value here will always // return an empty recordset. // // Note: System.Message.IsFwdOrReply is an Int32, not a Boolean. // The documentation doesn't indicate what the possible values are. // I'm assuming that 1 means true and 0 means false. oStringBuilder.AppendFormat( " AND ( System.Message.IsFwdOrReply = {0} AND" + " Contains(System.Message.FromAddress, '\"{1}\"') )" , isReplyFromParticipant1.Value ? 1 : 0, sEscapedFirstParticipant ); } return ( oStringBuilder.ToString() ); }
//************************************************************************* // Method: CheckAnalyzeMethodArguments() // /// <summary> /// Checks the arguments passed to AnalyzeEmailNetwork(). /// </summary> /// /// <param name="participantsCriteria"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="startTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="endTime"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="subjectText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="bodyText"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="folder"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="minimumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="maximumSize"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="attachmentFilter"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <param name="isReplyFromParticipant1"> /// See AnalyzeEmailNetwork(). /// </param> /// /// <remarks> /// This method throws an exception if any arguments are invalid. /// </remarks> //************************************************************************* protected void CheckAnalyzeMethodArguments( EmailParticipantCriteria [] participantsCriteria, Nullable<DateTime> startTime, Nullable<DateTime> endTime, String subjectText, String bodyText, String folder, Nullable<Int64> minimumSize, Nullable<Int64> maximumSize, Nullable<AttachmentFilter> attachmentFilter, Nullable<Boolean> isReplyFromParticipant1 ) { AssertValid(); ArgumentChecker oArgumentChecker = this.ArgumentChecker; Boolean bHasFirstParticipant = false; if (participantsCriteria != null) { foreach (EmailParticipantCriteria participantCriteria in participantsCriteria) { if ( String.IsNullOrEmpty(participantCriteria.Participant) ) { oArgumentChecker.ThrowArgumentException(AnalyzeMethodName, "participantsCriteria", "An email address wasn't specified.", null ); } bHasFirstParticipant = true; } } if (startTime.HasValue && endTime.HasValue && endTime.Value < startTime.Value) { oArgumentChecker.ThrowArgumentException(AnalyzeMethodName, "endTime", "The end time must be greater than or equal to the start" + " time.", null ); } CheckAnalyzeMethodArgument(subjectText, "subjectText"); CheckAnalyzeMethodArgument(bodyText, "bodyText"); CheckAnalyzeMethodArgument(folder, "folder"); if (minimumSize.HasValue) { oArgumentChecker.CheckArgumentPositive( AnalyzeMethodName, "minimumSize", minimumSize.Value); } if (maximumSize.HasValue) { oArgumentChecker.CheckArgumentPositive( AnalyzeMethodName, "maximumSize", maximumSize.Value); } if (minimumSize.HasValue && maximumSize.HasValue && maximumSize.Value < minimumSize.Value) { oArgumentChecker.ThrowArgumentException(AnalyzeMethodName, "maximumSize", "maximumSize must be greater than or equal to" + " minimumSize." , null ); } if (attachmentFilter.HasValue && attachmentFilter.Value == AttachmentFilter.HasAttachmentFromParticipant1 && !bHasFirstParticipant) { oArgumentChecker.ThrowArgumentException(AnalyzeMethodName, "attachmentFilter", "If attachmentFilter is HasAttachmentsFromParticipant1, a" + " first participant must also be specified.", null ); } if (isReplyFromParticipant1.HasValue && !bHasFirstParticipant) { oArgumentChecker.ThrowArgumentException(AnalyzeMethodName, "optionaIsReplyFromParticipant1", "If isReplyFromParticipant1 is specified, a first participant" + " must also be specified." , null ); } }