001/*
002 * Copyright 2021-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2021-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2021-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk.unboundidds.controls;
037
038
039
040import java.io.Serializable;
041
042import com.unboundid.util.Mutable;
043import com.unboundid.util.NotNull;
044import com.unboundid.util.Nullable;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047import com.unboundid.util.Validator;
048
049
050
051/**
052 * This class provides a set of properties that can be used in conjunction with
053 * the {@link MatchingEntryCountRequestControl}.
054 * <BR>
055 * <BLOCKQUOTE>
056 *   <B>NOTE:</B>  This class, and other classes within the
057 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
058 *   supported for use against Ping Identity, UnboundID, and
059 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
060 *   for proprietary functionality or for external specifications that are not
061 *   considered stable or mature enough to be guaranteed to work in an
062 *   interoperable way with other types of LDAP servers.
063 * </BLOCKQUOTE>
064 */
065@Mutable()
066@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
067public final class MatchingEntryCountRequestControlProperties
068       implements Serializable
069{
070  /**
071   * The serial version UID for this serializable class.
072   */
073  private static final long serialVersionUID = -7231704969312951204L;
074
075
076
077  // Indicates whether the server should internally retrieve and examine
078  // candidate entries to determine whether they would actually be returned to
079  // the client.
080  private boolean alwaysExamineCandidates;
081
082  // Indicates whether to include debug information in the response control.
083  private boolean includeDebugInfo;
084
085  // Indicates whether to include extended information in the response.
086  private boolean includeExtendedResponseData;
087
088  // Indicates whether the server should attempt to actually iterate through the
089  // entries in the backend in order to obtain the count if the search criteria
090  // is not indexed.
091  private boolean processSearchIfUnindexed;
092
093  // Indicates whether the server should skip retrieving the entry ID set for
094  // an exploded index key if the number of matching entries is known.
095  private boolean skipResolvingExplodedIndexes;
096
097  // The maximum number of candidate entries that should be examined if it is
098  // not possible to obtain an exact count using only information contained in
099  // the server indexes.
100  private int maxCandidatesToExamine;
101
102  // The short-circuit threshold that the server will use when evaluating filter
103  // components that are not categorized as fast.
104  @Nullable private Long slowShortCircuitThreshold;
105
106  // The short-circuit threshold that the server will for index processing that
107  // should be very fast.
108  @Nullable private Long fastShortCircuitThreshold;
109
110
111
112  /**
113   * Creates a new matching entry count request control properties object with
114   * the default settings.
115   */
116  public MatchingEntryCountRequestControlProperties()
117  {
118    maxCandidatesToExamine = 0;
119    alwaysExamineCandidates = false;
120    processSearchIfUnindexed = false;
121    skipResolvingExplodedIndexes = false;
122    fastShortCircuitThreshold = null;
123    slowShortCircuitThreshold = null;
124    includeExtendedResponseData = false;
125    includeDebugInfo = false;
126  }
127
128
129
130  /**
131   * Creates a new matching entry count request control properties object that
132   * is a copy of the provided properties.
133   *
134   * @param  properties  The properties to use to create the new set of
135   *                     properties.
136   */
137  public MatchingEntryCountRequestControlProperties(
138       @NotNull final MatchingEntryCountRequestControlProperties properties)
139  {
140    maxCandidatesToExamine = properties.getMaxCandidatesToExamine();
141    alwaysExamineCandidates = properties.alwaysExamineCandidates();
142    processSearchIfUnindexed = properties.processSearchIfUnindexed();
143    skipResolvingExplodedIndexes = properties.skipResolvingExplodedIndexes();
144    fastShortCircuitThreshold = properties.getFastShortCircuitThreshold();
145    slowShortCircuitThreshold = properties.getSlowShortCircuitThreshold();
146    includeExtendedResponseData = properties.includeExtendedResponseData();
147    includeDebugInfo = properties.includeDebugInfo();
148  }
149
150
151
152  /**
153   * Creates a new matching entry count request control properties object with
154   * the settings used for the provided control.
155   *
156   * @param  control  The matching entry count request control to use to
157   *                  initialize this set of properties.
158   */
159  public MatchingEntryCountRequestControlProperties(
160              @NotNull final MatchingEntryCountRequestControl control)
161  {
162    maxCandidatesToExamine = control.getMaxCandidatesToExamine();
163    alwaysExamineCandidates = control.alwaysExamineCandidates();
164    processSearchIfUnindexed = control.processSearchIfUnindexed();
165    skipResolvingExplodedIndexes = control.skipResolvingExplodedIndexes();
166    fastShortCircuitThreshold = control.getFastShortCircuitThreshold();
167    slowShortCircuitThreshold = control.getSlowShortCircuitThreshold();
168    includeExtendedResponseData = control.includeExtendedResponseData();
169    includeDebugInfo = control.includeDebugInfo();
170  }
171
172
173
174  /**
175   * Retrieves the maximum number of candidate entries that should be examined
176   * in order to determine accurate count of the number of matching entries.
177   * <BR><BR>
178   * For a fully-indexed search, this property will only be used if
179   * {@link #alwaysExamineCandidates} is true.  If the number of candidate
180   * entries identified is less than the maximum number of candidates to
181   * examine, then the server will return an {@code EXAMINED_COUNT} result that
182   * indicates the number of entries matching the criteria that would actually
183   * be returned in a normal search with the same criteria.  If the number of
184   * candidate entries exceeds the maximum number of candidates to examine, then
185   * the server will return an {@code UNEXAMINED_COUNT} result that indicates
186   * the number of entries matching the search criteria but that may include
187   * entries that would not actually be returned to the client.
188   * <BR><BR>
189   * For a partially-indexed search, if the upper bound on the number of
190   * candidates is less than or equal to the maximum number of candidates to
191   * examine, then the server will internally retrieve and examine each of those
192   * candidates to determine which of them match the search criteria and would
193   * actually be returned to the client, and will then return an
194   * {@code EXAMINED_COUNT} result with that count.  If the upper bound on the
195   * number of candidates is greater than the maximum number of candidates to
196   * examine, then the server will return an {@code UPPER_BOUND} result to
197   * indicate that the exact count is not known but an upper bound is available.
198   *
199   * @return  The maximum number of candidate entries to examine in order to
200   *          determine an accurate count of the number of matching entries.
201   */
202  public int getMaxCandidatesToExamine()
203  {
204    return maxCandidatesToExamine;
205  }
206
207
208
209  /**
210   * Specifies the maximum number of candidate entries that should be examined
211   * in order to determine accurate count of the number of matching entries.
212   * <BR><BR>
213   * For a fully-indexed search, this property will only be used if
214   * {@link #alwaysExamineCandidates} is true.  If the number of candidate
215   * entries identified is less than the maximum number of candidates to
216   * examine, then the server will return an {@code EXAMINED_COUNT} result that
217   * indicates the number of entries matching the criteria that would actually
218   * be returned in a normal search with the same criteria.  If the number of
219   * candidate entries exceeds the maximum number of candidates to examine, then
220   * the server will return an {@code UNEXAMINED_COUNT} result that indicates
221   * the number of entries matching the search criteria but that may include
222   * entries that would not actually be returned to the client.
223   * <BR><BR>
224   * For a partially-indexed search, if the upper bound on the number of
225   * candidates is less than or equal to the maximum number of candidates to
226   * examine, then the server will internally retrieve and examine each of those
227   * candidates to determine which of them match the search criteria and would
228   * actually be returned to the client, and will then return an
229   * {@code EXAMINED_COUNT} result with that count.  If the upper bound on the
230   * number of candidates is greater than the maximum number of candidates to
231   * examine, then the server will return an {@code UPPER_BOUND} result to
232   * indicate that the exact count is not known but an upper bound is available.
233   *
234   * @param  maxCandidatesToExamine  The maximum number of candidate entries
235   *                                 that the server should retrieve and examine
236   *                                 to determine whether they actually match
237   *                                 the search criteria.  If the search is
238   *                                 partially indexed and the total number of
239   *                                 candidate entries is less than or equal to
240   *                                 this value, then these candidate entries
241   *                                 will be examined to determine which of them
242   *                                 match the search criteria so that an
243   *                                 accurate count can be determined.  If the
244   *                                 search is fully indexed such that the all
245   *                                 candidate entries are known to match the
246   *                                 search criteria, then the server may still
247   *                                 examine each of these entries if the number
248   *                                 of candidates is less than
249   *                                 {@code maxCandidatesToExamine} and
250   *                                 {@code alwaysExamineCandidates} is
251   *                                 {@code true} in order to allow the entry
252   *                                 count that is returned to be restricted to
253   *                                 only those entries that would actually be
254   *                                 returned to the client.  This will be
255   *                                 ignored for searches that are completely
256   *                                 unindexed.
257   *                                 <BR><BR>
258   *                                 The value for this argument must be greater
259   *                                 than or equal to zero.  If it is zero, then
260   *                                 the server will not examine any entries, so
261   *                                 a partially-indexed search will only be
262   *                                 able to return a count that is an upper
263   *                                 bound, and a fully-indexed search will only
264   *                                 be able to return an unexamined exact
265   *                                 count.  If there should be no bound on the
266   *                                 number of entries to retrieve, then a value
267   *                                 of {@code Integer.MAX_VALUE} may be
268   *                                 specified.
269   */
270  public void setMaxCandidatesToExamine(final int maxCandidatesToExamine)
271  {
272    Validator.ensureTrue((maxCandidatesToExamine >= 0),
273         "MatchingEntryCountRequestControlProperties.maxCandidatesToExamine " +
274              "must be greater than or equal to zero.");
275
276    this.maxCandidatesToExamine = maxCandidatesToExamine;
277  }
278
279
280
281  /**
282   * Indicates whether the server should always examine candidate entries in
283   * fully-indexed searches to determine whether they would actually be returned
284   * to the client in a normal search with the same criteria.
285   *
286   * @return  {@code true} if the server should attempt to internally retrieve
287   *          and examine matching entries to determine whether they would
288   *          normally be returned to the client (e.g., that the client has
289   *          permission to access the entry and that it is not a
290   *          normally-hidden entry like an LDAP subentry, a replication
291   *          conflict entry, or a soft-deleted entry), or {@code false} if the
292   *          server should return an unverified count.
293   */
294  public boolean alwaysExamineCandidates()
295  {
296    return alwaysExamineCandidates;
297  }
298
299
300
301  /**
302   *Specifies whether the server should always examine candidate entries in
303   * fully-indexed searches to determine whether they would actually be returned
304   * to the client in a normal search with the same criteria.
305   *
306   * @param  alwaysExamineCandidates Indicates whether the server should always
307   *                                 examine candidate entries to determine
308   *                                 whether they would actually be returned to
309   *                                 the client in a normal search.  This will
310   *                                 only be used for fully-indexed searches in
311   *                                 which the set of matching entries is known.
312   *                                 If the value is {@code true} and the number
313   *                                 of candidates is smaller than
314   *                                 {@code maxCandidatesToExamine}, then each
315   *                                 matching entry will be internally retrieved
316   *                                 and examined to determine whether it would
317   *                                 be returned to the client based on the
318   *                                 details of the search request (e.g.,
319   *                                 whether the requester has permission to
320   *                                 access the entry, whether it's an LDAP
321   *                                 subentry, replication conflict entry,
322   *                                 soft-deleted entry, or other type of entry
323   *                                 that is normally hidden, etc.) so that an
324   *                                 exact count can be returned.  If this is
325   *                                 {@code false} or the number of candidates
326   *                                 exceeds {@code maxCandidatesToExamine},
327   *                                 then the server will only be able to return
328   *                                 an unexamined count which may include
329   *                                 entries that match the search criteria but
330   *                                 that would not normally be returned to the
331   *                                 requester.
332   */
333  public void setAlwaysExamineCandidates(final boolean alwaysExamineCandidates)
334  {
335    this.alwaysExamineCandidates = alwaysExamineCandidates;
336  }
337
338
339
340  /**
341   * Indicates whether the server should internally retrieve and examine all
342   * entries within the search scope in order to obtain an exact matching entry
343   * count for an unindexed search.  Note that this value will not be considered
344   * for completely-indexed or partially-indexed searches, nor for searches in
345   * which matching entries should be returned.
346   *
347   * @return  {@code true} if the server should internally retrieve and examine
348   *          all entries within the search scope in order to obtain an exact
349   *          matching entry count for an unindexed search, or {@code false} if
350   *          not.
351   */
352  public boolean processSearchIfUnindexed()
353  {
354    return processSearchIfUnindexed;
355  }
356
357
358
359  /**
360   * Specifies whether the server should internally retrieve and examine all
361   * entries within the search scope in order to obtain an exact matching entry
362   * count for an unindexed search.  Note that this value will not be considered
363   * for completely-indexed or partially-indexed searches, nor for searches in
364   * which matching entries should be returned.
365   *
366   * @param  processSearchIfUnindexed  Indicates whether the server should
367   *                                   attempt to determine the number of
368   *                                   matching entries if the search criteria
369   *                                   is completely unindexed.  If this is
370   *                                   {@code true} and the requester has the
371   *                                   unindexed-search privilege, then the
372   *                                   server will iterate through all entries
373   *                                   in the scope (which may take a very long
374   *                                   time to complete) in order to to
375   *                                   determine which of them match the search
376   *                                   criteria so that it can return an
377   *                                   accurate count.  If this is
378   *                                   {@code false} or the requester does not
379   *                                   have the unindexed-search privilege, then
380   *                                   the server will not spend any time
381   *                                   attempting to determine the number of
382   *                                   matching entries and will instead return
383   *                                   a matching entry count response control
384   *                                   indicating that the entry count is
385   *                                   unknown.
386   */
387  public void setProcessSearchIfUnindexed(
388                   final boolean processSearchIfUnindexed)
389  {
390    this.processSearchIfUnindexed = processSearchIfUnindexed;
391  }
392
393
394
395  /**
396   * Indicates whether the server should skip the effort of actually retrieving
397   * the candidate entry IDs for exploded index keys in which the number of
398   * matching entries is known.  Skipping the process of accessing an exploded
399   * index can allow the server to more quickly arrive at the matching entry
400   * count estimate, but that estimate may be less accurate than if it had
401   * actually retrieved those candidates.
402   *
403   * @return  {@code true} if the server should skip the effort of actually
404   *          retrieving the candidate entry IDs for exploded index keys in
405   *          which the number of matching entries is known, or {@code false} if
406   *          it may retrieve candidates from an exploded index in the course of
407   *          determining the matching entry count.
408   */
409  public boolean skipResolvingExplodedIndexes()
410  {
411    return skipResolvingExplodedIndexes;
412  }
413
414
415
416  /**
417   * Specifies whether the server should skip the effort of actually retrieving
418   * the candidate entry IDs for exploded index keys in which the number of
419   * matching entries is known.  Skipping the process of accessing an exploded
420   * index can allow the server to more quickly arrive at the matching entry
421   * count estimate, but that estimate may be less accurate than if it had
422   * actually retrieved those candidates.
423   *
424   * @param  skipResolvingExplodedIndexes  Indicates whether the server should
425   *                                       skip the effort of actually
426   *                                       retrieving the candidate entry IDs
427   *                                       for exploded index keys in which the
428   *                                       number of matching entries is known.
429   */
430  public void setSkipResolvingExplodedIndexes(
431                   final boolean skipResolvingExplodedIndexes)
432  {
433    this.skipResolvingExplodedIndexes = skipResolvingExplodedIndexes;
434  }
435
436
437
438  /**
439   * Retrieves the short-circuit threshold that the server should use when
440   * determining whether to continue with index processing in an attempt to
441   * further pare down a candidate set that already has a defined superset of
442   * the entries that actually match the filter.  If the number of entries in
443   * that candidate set is less than or equal to the short-circuit threshold,
444   * then the server may simply use that candidate set in the course of
445   * determining the matching entry count, even if there may be additional
446   * processing that can be performed (e.g., further filter components to
447   * evaluate) that may allow the server to pare down the results even further.
448   * Short-circuiting may allow the server to obtain the matching entry count
449   * estimate faster, but may also cause the resulting estimate to be less
450   * accurate.
451   * <BR><BR>
452   * The value returned by this method will be used for cases in which the
453   * server is performing the fastest types of index processing.  For example,
454   * this may include evaluating presence, equality, or approximate match
455   * components, which should only require retrieving a single index key to
456   * obtain the candidate set.
457   *
458   * @return  The short-circuit threshold that should be used for fast index
459   *          processing, zero if the server should not short-circuit at all
460   *          during fast index processing, or {@code null} if the server should
461   *          determine the appropriate fast short-circuit threshold to use.
462   */
463  @Nullable()
464  public Long getFastShortCircuitThreshold()
465  {
466    return fastShortCircuitThreshold;
467  }
468
469
470
471  /**
472   * Specifies the short-circuit threshold that the server should use when
473   * determining whether to continue with index processing in an attempt to
474   * further pare down a candidate set that already has a defined superset of
475   * entries that actually match the filter.  Short-circuiting may allow the
476   * server to skip potentially-costly index processing and allow it to obtain
477   * the matching entry count estimate faster, but the resulting estimate may be
478   * less accurate.  The fast short-circuit threshold will be used for index
479   * processing that is expected to be very fast (e.g., when performing index
480   * lookups for presence, equality, and approximate-match components, which
481   * should only require accessing a single index key).
482   *
483   * @param  fastShortCircuitThreshold  The short-circuit threshold that the
484   *                                    server should use when determining
485   *                                    whether to continue with index
486   *                                    processing in an attempt to further pare
487   *                                    down a candidate set that already has a
488   *                                    defined superset of the entries that
489   *                                    actually match the filter.  A value that
490   *                                    is less than or equal to zero indicates
491   *                                    that the server should never short
492   *                                    circuit when performing fast index
493   *                                    processing.  A value of {@code null}
494   *                                    indicates that the server should
495   *                                    determine the appropriate fast
496   *                                    short-circuit threshold to use.
497   */
498  public void setFastShortCircuitThreshold(
499                   @Nullable final Long fastShortCircuitThreshold)
500  {
501    if ((fastShortCircuitThreshold == null) || (fastShortCircuitThreshold >= 0))
502    {
503      this.fastShortCircuitThreshold = fastShortCircuitThreshold;
504    }
505    else
506    {
507      this.fastShortCircuitThreshold = 0L;
508    }
509  }
510
511
512
513  /**
514   * Retrieves the short-circuit threshold that the server should use when
515   * determining whether to continue with index processing for evaluation that
516   * may be more expensive than what falls into the "fast" category (e.g.,
517   * substring and range filter components).
518   *
519   * @return  The short-circuit threshold that the server should use when
520   *          determining whether to continue with index processing for
521   *          evaluation that may be more expensive than what falls into the
522   *          "fast" category, zero if the server should never short circuit
523   *          when performing slow index processing, or {@code null} if the
524   *          server should determine the appropriate slow short-circuit
525   *          threshold to use.
526   */
527  @Nullable()
528  public Long getSlowShortCircuitThreshold()
529  {
530    return slowShortCircuitThreshold;
531  }
532
533
534
535  /**
536   * Specifies the short-circuit threshold that the server should use when
537   * determining whether to continue with index processing for evaluation that
538   * may be more expensive than what falls into the "fast" category (e.g.,
539   * substring and range filter components).
540   *
541   * @param  slowShortCircuitThreshold
542   *              The short-circuit threshold that the server should use when
543   *              determining whether to continue with index processing for
544   *              evaluation that may be more expensive than what falls into the
545   *              "fast" category.  A value that is less than or equal to zero
546   *              indicates that the server should never short circuit when
547   *              performing slow index processing.  A value of {@code null}
548   *              indicates that the server should determine the appropriate
549   *              slow short-circuit threshold to use.
550   */
551  public void setSlowShortCircuitThreshold(
552                   @Nullable final Long slowShortCircuitThreshold)
553  {
554    if ((slowShortCircuitThreshold == null) || (slowShortCircuitThreshold >= 0))
555    {
556      this.slowShortCircuitThreshold = slowShortCircuitThreshold;
557    }
558    else
559    {
560      this.slowShortCircuitThreshold = 0L;
561    }
562  }
563
564
565
566  /**
567   * Indicates whether the server may include extended response data in the
568   * corresponding response control, which may provide information like whether
569   * all of the identified candidate entries are within the scope of the search
570   * and any unindexed or unevaluated portion of the search filter.
571   *
572   * @return  {@code true} if the server may include extended response data
573   *          in the corresponding response control, or {@code false} if not.
574   */
575  public boolean includeExtendedResponseData()
576  {
577    return includeExtendedResponseData;
578  }
579
580
581
582  /**
583   * Indicates whether the server may include extended response data in the
584   * corresponding response control, which may provide information like whether
585   * all of the identified candidate entries are within the scope of the search
586   * and any unindexed or unevaluated portion of the search filter.
587   * <BR><BR>
588   * Note that before setting this to {@code true}, the client should first
589   * verify that the server supports this functionality by checking to see if
590   * {@link com.unboundid.ldap.sdk.RootDSE#supportsFeature} returns {@code true}
591   * for {@link
592   * MatchingEntryCountRequestControl#EXTENDED_RESPONSE_DATA_FEATURE_OID}.
593   * Setting this value to {@code true} for servers that do not support this
594   * feature may cause the server to reject the request.
595   *
596   * @param  includeExtendedResponseData  Indicates whether the server may
597   *                                      include extended response data in the
598   *                                      corresponding response control.
599   */
600  public void setIncludeExtendedResponseData(
601                   final boolean includeExtendedResponseData)
602  {
603    this.includeExtendedResponseData = includeExtendedResponseData;
604  }
605
606
607
608  /**
609   * Indicates whether the server should include debug information in the
610   * response control that provides additional information about how the server
611   * arrived at the result.  If debug information is to be provided, it will be
612   * in a human-readable rather than machine-parsable form.
613   *
614   * @return  {@code true} if the server should include debug information in
615   *          the response control, or {@code false} if not.
616   */
617  public boolean includeDebugInfo()
618  {
619    return includeDebugInfo;
620  }
621
622
623
624  /**
625   * Specifies whether the server should include debug information in the
626   * response control that provides additional information about how the server
627   * arrived at the result.
628   *
629   * @param  includeDebugInfo  Indicates whether the server should include debug
630   *                           information in the response control that provides
631   *                           additional information about how the server
632   *                           arrived at the result.
633   */
634  public void setIncludeDebugInfo(final boolean includeDebugInfo)
635  {
636    this.includeDebugInfo = includeDebugInfo;
637  }
638
639
640
641  /**
642   * Retrieves a string representation of the matching entry count request
643   * control properties.
644   *
645   * @return  A string representation of the matching entry count request
646   *          control properties.
647   */
648  @Override()
649  @NotNull()
650  public String toString()
651  {
652    final StringBuilder buffer = new StringBuilder();
653    toString(buffer);
654    return buffer.toString();
655  }
656
657
658
659  /**
660   * Appends a string representation of the matching entry count request control
661   * properties to the provided buffer.
662   *
663   * @param  buffer  The buffer to which the information should be appended.  It
664   *                 must not be {@code null}.
665   */
666  public void toString(@NotNull final StringBuilder buffer)
667  {
668    buffer.append("MatchingEntryCountRequestControlProperties(" +
669         "maxCandidatesToExamine=");
670    buffer.append(maxCandidatesToExamine);
671    buffer.append(", alwaysExamineCandidates=");
672    buffer.append(alwaysExamineCandidates);
673    buffer.append(", processSearchIfUnindexed=");
674    buffer.append(processSearchIfUnindexed);
675    buffer.append(", skipResolvingExplodedIndexes=");
676    buffer.append(skipResolvingExplodedIndexes);
677
678    if (fastShortCircuitThreshold != null)
679    {
680      buffer.append(", fastShortCircuitThreshold=");
681      buffer.append(fastShortCircuitThreshold);
682    }
683
684    if (slowShortCircuitThreshold != null)
685    {
686      buffer.append(", slowShortCircuitThreshold=");
687      buffer.append(slowShortCircuitThreshold);
688    }
689
690    buffer.append(", includeExtendedResponseData=");
691    buffer.append(includeExtendedResponseData);
692    buffer.append(", includeDebugInfo=");
693    buffer.append(includeDebugInfo);
694    buffer.append(')');
695  }
696}