001/*
002 * Copyright 2014-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2014-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) 2014-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.util.ArrayList;
041import java.util.LinkedHashMap;
042import java.util.List;
043import java.util.Map;
044
045import com.unboundid.asn1.ASN1Boolean;
046import com.unboundid.asn1.ASN1Element;
047import com.unboundid.asn1.ASN1Integer;
048import com.unboundid.asn1.ASN1Long;
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.asn1.ASN1Sequence;
051import com.unboundid.ldap.sdk.Control;
052import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
053import com.unboundid.ldap.sdk.LDAPException;
054import com.unboundid.ldap.sdk.LDAPInterface;
055import com.unboundid.ldap.sdk.ResultCode;
056import com.unboundid.ldap.sdk.RootDSE;
057import com.unboundid.util.Debug;
058import com.unboundid.util.NotMutable;
059import com.unboundid.util.NotNull;
060import com.unboundid.util.Nullable;
061import com.unboundid.util.StaticUtils;
062import com.unboundid.util.ThreadSafety;
063import com.unboundid.util.ThreadSafetyLevel;
064import com.unboundid.util.Validator;
065import com.unboundid.util.json.JSONBoolean;
066import com.unboundid.util.json.JSONField;
067import com.unboundid.util.json.JSONNumber;
068import com.unboundid.util.json.JSONObject;
069import com.unboundid.util.json.JSONValue;
070
071import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
072
073
074
075/**
076 * This class provides a request control which may be included in a search
077 * request to indicate that the server should provide the number of entries that
078 * match the search criteria.  The count will be included in the search result
079 * done message, and all search result entries will be suppressed.
080 * <BR>
081 * <BLOCKQUOTE>
082 *   <B>NOTE:</B>  This class, and other classes within the
083 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
084 *   supported for use against Ping Identity, UnboundID, and
085 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
086 *   for proprietary functionality or for external specifications that are not
087 *   considered stable or mature enough to be guaranteed to work in an
088 *   interoperable way with other types of LDAP servers.
089 * </BLOCKQUOTE>
090 * <BR>
091 * Whenever possible, the server will use index information to quickly identify
092 * entries matching the criteria of the associated search request.  However, if
093 * the count is only determined using index information, then that count may
094 * include entries that would not actually be returned to the client in the
095 * course of processing that search (e.g., because the client doesn't have
096 * permission to access the entry, or because it is a special "operational"
097 * entry like an LDAP subentry, replication conflict entry, or soft-deleted
098 * entry).  Indicating that the server should always examine candidate entries
099 * will increase the length of time to obtain the matching entry count, but will
100 * ensure that the count will not include entries that would not otherwise be
101 * returned by that search.
102 * <BR><BR>
103 * Also note that this control is not compatible for use with other controls
104 * that may cause only a subset of entries to be returned, including the simple
105 * paged results control and the virtual list view control.  It is also not
106 * compatible for use with other controls that may cause the server to return
107 * more entries than those that match the search criteria, like the LDAP join
108 * control.
109 * <BR><BR>
110 * The OID for a matching entry count request control is
111 * "1.3.6.1.4.1.30221.2.5.36", and it may have a criticality of either
112 * {@code true} or {@code false}.  It must include a value with the following
113 * encoding:
114 * <PRE>
115 *   MatchingEntryCountRequest ::= SEQUENCE {
116 *        maxCandidatesToExamine           [0] INTEGER (0 .. MAX) DEFAULT 0,
117 *        alwaysExamineCandidates          [1] BOOLEAN DEFAULT FALSE,
118 *        processSearchIfUnindexed         [2] BOOLEAN DEFAULT FALSE,
119 *        includeDebugInfo                 [3] BOOLEAN DEFAULT FALSE,
120 *        skipResolvingExplodedIndexes     [4] BOOLEAN DEFAULT FALSE,
121 *        fastShortCircuitThreshold        [5] INTEGER (0 .. MAX) OPTIONAL,
122 *        slowShortCircuitThreshold        [6] INTEGER (0 .. MAX) OPTIONAL,
123 *        includeExtendedResponseData      [7] BOOLEAN DEFAULT FALSE,
124 *        ... }
125 * </PRE>
126 *
127 * @see  MatchingEntryCountResponseControl
128 */
129@NotMutable()
130@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
131public final class MatchingEntryCountRequestControl
132       extends Control
133{
134  /**
135   * The OID (1.3.6.1.4.1.30221.2.5.36) for the matching entry count request
136   * control.
137   */
138  @NotNull public static final String MATCHING_ENTRY_COUNT_REQUEST_OID =
139       "1.3.6.1.4.1.30221.2.5.36";
140
141
142
143  /**
144   * The OID (1.3.6.1.4.1.30221.2.12.7) for the supportedFeature value that a
145   * server should advertise in its root DSE if it supports returning extended
146   * information in the response control that older clients may not be able to
147   * handle.  Clients that wish to use the {@code includeExtendedResponseData}
148   * element of the request control should check the target server's root DSE
149   * to determine whether it supports this feature before requesting it because
150   * older versions of the server that do not support it may not accept a
151   * control that requests it.
152   */
153  @NotNull public static final String EXTENDED_RESPONSE_DATA_FEATURE_OID =
154       "1.3.6.1.4.1.30221.2.12.7";
155
156
157
158  /**
159   * The BER type for the element that specifies the maximum number of candidate
160   * entries to examine.
161   */
162  private static final byte TYPE_MAX_CANDIDATES_TO_EXAMINE = (byte) 0x80;
163
164
165
166  /**
167   * The BER type for the element that indicates whether always examine
168   * candidate entries to determine whether they would actually be returned to
169   * the client.
170   */
171  private static final byte TYPE_ALWAYS_EXAMINE_CANDIDATES = (byte) 0x81;
172
173
174
175  /**
176   * The BER type for the element that indicates whether to process an unindexed
177   * search to determine the number of matching entries.
178   */
179  private static final byte TYPE_PROCESS_SEARCH_IF_UNINDEXED = (byte) 0x82;
180
181
182
183  /**
184   * The BER type for the element that indicates whether to include debug
185   * information in the response.
186   */
187  private static final byte TYPE_INCLUDE_DEBUG_INFO = (byte) 0x83;
188
189
190
191  /**
192   * The BER type for the element that indicates whether to skip resolving
193   * exploded indexes if the number of matching entries is known.
194   */
195  private static final byte TYPE_SKIP_RESOLVING_EXPLODED_INDEXES = (byte) 0x84;
196
197
198
199  /**
200   * The BER type for the element that specifies the short-circuit threshold to
201   * use when performing index processing that is expected to be very fast
202   * (e.g., filter components that can be evaluated with a single index lookup,
203   * like presence, equality, and approximate match components).
204   */
205  private static final byte TYPE_FAST_SHORT_CIRCUIT_THRESHOLD = (byte) 0x85;
206
207
208
209  /**
210   * The BER type for the element that specifies the short-circuit threshold to
211   * use when evaluating filter components that are not covered by the fast
212   * short-circuit threshold.
213   */
214  private static final byte TYPE_SLOW_SHORT_CIRCUIT_THRESHOLD = (byte) 0x86;
215
216
217
218  /**
219   * The BER type for the element that indicates whether the client wants the
220   * server to return extended information in the response, including elements
221   * that may indicate whether all of the identified candidate entries are
222   * within the scope of the search and any portion of the filter that is
223   * unindexed or unevaluated.
224   */
225  private static final byte TYPE_INCLUDE_EXTENDED_RESPONSE_DATA = (byte) 0x87;
226
227
228
229  /**
230   * The name of the field used to hold the always examine candidates flag in
231   * the JSON representation of this control.
232   */
233  @NotNull private static final String JSON_FIELD_ALWAYS_EXAMINE_CANDIDATES =
234       "always-examine-candidates";
235
236
237
238  /**
239   * The name of the field used to hold the fast short-circuit threshold in the
240   * JSON representation of this control.
241   */
242  @NotNull private static final String JSON_FIELD_FAST_SHORT_CIRCUIT_THRESHOLD =
243       "fast-short-circuit-threshold";
244
245
246
247  /**
248   * The name of the field used to hold the include debug info flag in the JSON
249   * representation of this control.
250   */
251  @NotNull private static final String JSON_FIELD_INCLUDE_DEBUG_INFO =
252       "include-debug-info";
253
254
255
256  /**
257   * The name of the field used to hold the include extended response data flag
258   * in the JSON representation of this control.
259   */
260  @NotNull private static final String
261       JSON_FIELD_INCLUDE_EXTENDED_RESPONSE_DATA =
262            "include-extended-response-data";
263
264
265
266  /**
267   * The name of the field used to hold the maximum candidates to examine in the
268   * JSON representation of this control.
269   */
270  @NotNull private static final String
271       JSON_FIELD_MAXIMUM_CANDIDATES_TO_EXAMINE =
272            "maximum-candidates-to-examine";
273
274
275
276  /**
277   * The name of the field used to hold the process search if unindexed flag in
278   * the JSON representation of this control.
279   */
280  @NotNull private static final String JSON_FIELD_PROCESS_SEARCH_IF_UNINDEXED =
281       "process-search-if-unindexed";
282
283
284
285  /**
286   * The name of the field used to hold the skip resolving exploded indexes flag
287   * in the JSON representation of this control.
288   */
289  @NotNull private static final String
290       JSON_FIELD_SKIP_RESOLVING_EXPLODED_INDEXES =
291            "skip-resolving-exploded-indexes";
292
293
294
295  /**
296   * The name of the field used to hold the slow short-circuit threshold in the
297   * JSON representation of this control.
298   */
299  @NotNull private static final String JSON_FIELD_SLOW_SHORT_CIRCUIT_THRESHOLD =
300       "slow-short-circuit-threshold";
301
302
303
304  /**
305   * The serial version UID for this serializable class.
306   */
307  private static final long serialVersionUID = 8670611963939571953L;
308
309
310
311  // Indicates whether the server should internally retrieve and examine
312  // candidate entries to determine whether they would actually be returned to
313  // the client.
314  private final boolean alwaysExamineCandidates;
315
316  // Indicates whether to include debug information in the response control.
317  private final boolean includeDebugInfo;
318
319  // Indicates whether to include extended information in the response.
320  private final boolean includeExtendedResponseData;
321
322  // Indicates whether the server should attempt to actually iterate through the
323  // entries in the backend in order to obtain the count if the search criteria
324  // is not indexed.
325  private final boolean processSearchIfUnindexed;
326
327  // Indicates whether the server should skip retrieving the entry ID set for
328  // an exploded index key if the number of matching entries is known.
329  private final boolean skipResolvingExplodedIndexes;
330
331  // The maximum number of candidate entries that should be examined if it is
332  // not possible to obtain an exact count using only information contained in
333  // the server indexes.
334  private final int maxCandidatesToExamine;
335
336  // The short-circuit threshold that the server will use when evaluating filter
337  // components that are not categorized as fast.
338  @Nullable private final Long slowShortCircuitThreshold;
339
340  // The short-circuit threshold that the server will for index processing that
341  // should be very fast.
342  @Nullable private final Long fastShortCircuitThreshold;
343
344
345
346  /**
347   * Creates a new matching entry count request control with the default
348   * settings.  The control will be critical, no candidate entries will be
349   * examined, and the search will not be processed if it is unindexed.
350   */
351  public MatchingEntryCountRequestControl()
352  {
353    this(true, 0, false, false, false);
354  }
355
356
357
358  /**
359   * Creates a new matching entry count request control with the provided
360   * information.
361   *
362   * @param  isCritical                Indicates whether this control should be
363   *                                   critical.
364   * @param  maxCandidatesToExamine    The maximum number of candidate entries
365   *                                   that the server should retrieve and
366   *                                   examine to determine whether they
367   *                                   actually match the search criteria.  If
368   *                                   the search is partially indexed and the
369   *                                   total number of candidate entries is less
370   *                                   than or equal to this value, then these
371   *                                   candidate entries will be examined to
372   *                                   determine which of them match the search
373   *                                   criteria so that an accurate count can
374   *                                   be determined.  If the search is fully
375   *                                   indexed such that the all candidate
376   *                                   entries are known to match the search
377   *                                   criteria, then the server may still
378   *                                   examine each of these entries if the
379   *                                   number of candidates is less than
380   *                                   {@code maxCandidatesToExamine} and
381   *                                   {@code alwaysExamineCandidates} is true
382   *                                   in order to allow the entry count that
383   *                                   is returned to be restricted to only
384   *                                   those entries that would actually be
385   *                                   returned to the client.  This will be
386   *                                   ignored for searches that are completely
387   *                                   unindexed.
388   *                                   <BR><BR>
389   *                                   The value for this argument must be
390   *                                   greater than or equal to zero.  If it
391   *                                   is zero, then the server will not
392   *                                   examine any entries, so a
393   *                                   partially-indexed search will only be
394   *                                   able to return a count that is an upper
395   *                                   bound, and a fully-indexed search will
396   *                                   only be able to return an unexamined
397   *                                   exact count.  If there should be no bound
398   *                                   on the number of entries to retrieve,
399   *                                   then a value of {@code Integer.MAX_VALUE}
400   *                                   may be specified.
401   * @param  alwaysExamineCandidates   Indicates whether the server should
402   *                                   always examine candidate entries to
403   *                                   determine whether they would actually
404   *                                   be returned to the client in a normal
405   *                                   search.  This will only be used for
406   *                                   fully-indexed searches in which the
407   *                                   set of matching entries is known.  If the
408   *                                   value is {@code true} and the number of
409   *                                   candidates is smaller than
410   *                                   {@code maxCandidatesToExamine}, then each
411   *                                   matching entry will be internally
412   *                                   retrieved and examined to determine
413   *                                   whether it would be returned to the
414   *                                   client based on the details of the search
415   *                                   request (e.g., whether the requester has
416   *                                   permission to access the entry, whether
417   *                                   it's an LDAP subentry, replication
418   *                                   conflict entry, soft-deleted entry, or
419   *                                   other type of entry that is normally
420   *                                   hidden) so that an exact count can be
421   *                                   returned.  If this is {@code false} or
422   *                                   the number of candidates exceeds
423   *                                   {@code maxCandidatesToExamine}, then the
424   *                                   server will only be able to return an
425   *                                   unexamined count which may include
426   *                                   entries that match the search criteria
427   *                                   but that would not normally be returned
428   *                                   to the requester.
429   * @param  processSearchIfUnindexed  Indicates whether the server should
430   *                                   attempt to determine the number of
431   *                                   matching entries if the search criteria
432   *                                   is completely unindexed.  If this is
433   *                                   {@code true} and the requester has the
434   *                                   unindexed-search privilege, then the
435   *                                   server will iterate through all entries
436   *                                   in the scope (which may take a very long
437   *                                   time to complete) in order to to
438   *                                   determine which of them match the search
439   *                                   criteria so that it can return an
440   *                                   accurate count.  If this is
441   *                                   {@code false} or the requester does not
442   *                                   have the unindexed-search privilege, then
443   *                                   the server will not spend any time
444   *                                   attempting to determine the number of
445   *                                   matching entries and will instead return
446   *                                   a matching entry count response control
447   *                                   indicating that the entry count is
448   *                                   unknown.
449   * @param  includeDebugInfo          Indicates whether the server should
450   *                                   include debug information in the response
451   *                                   that may help better understand how it
452   *                                   arrived at the result.  If any debug
453   *                                   information is returned, it will be in
454   *                                   the form of human-readable text that is
455   *                                   not intended to be machine-parsable.
456   */
457  public MatchingEntryCountRequestControl(final boolean isCritical,
458              final int maxCandidatesToExamine,
459              final boolean alwaysExamineCandidates,
460              final boolean processSearchIfUnindexed,
461              final boolean includeDebugInfo)
462  {
463    this(isCritical, maxCandidatesToExamine, alwaysExamineCandidates,
464         processSearchIfUnindexed, false, null, null, includeDebugInfo);
465  }
466
467
468
469  /**
470   * Creates a new matching entry count request control with the provided
471   * information.
472   *
473   * @param  isCritical                    Indicates whether this control should
474   *                                       be critical.
475   * @param  maxCandidatesToExamine        The maximum number of candidate
476   *                                       entries that the server should
477   *                                       retrieve and examine to determine
478   *                                       whether they actually match the
479   *                                       search criteria.  If the search is
480   *                                       partially indexed and the total
481   *                                       number of candidate entries is less
482   *                                       than or equal to this value, then
483   *                                       these candidate entries will be
484   *                                       examined to determine which of them
485   *                                       match the search criteria so that an
486   *                                       accurate count can be determined.  If
487   *                                       the search is fully indexed such that
488   *                                       the all candidate entries are known
489   *                                       to match the search criteria, then
490   *                                       the server may still examine each of
491   *                                       these entries if the number of
492   *                                       candidates is less than
493   *                                       {@code maxCandidatesToExamine} and
494   *                                       {@code alwaysExamineCandidates} is
495   *                                       {@code true} in order to allow the
496   *                                       entry count that is returned to be
497   *                                       restricted to only those entries that
498   *                                       would actually be returned to the
499   *                                       client.  This will be ignored for
500   *                                       searches that are completely
501   *                                       unindexed.
502   *                                       <BR><BR>
503   *                                       The value for this argument must be
504   *                                       greater than or equal to zero.  If it
505   *                                       is zero, then the server will not
506   *                                       examine any entries, so a
507   *                                       partially-indexed search will only be
508   *                                       able to return a count that is an
509   *                                       upper bound, and a fully-indexed
510   *                                       search will only be able to return an
511   *                                       unexamined exact count.  If there
512   *                                       should be no bound on the number of
513   *                                       entries to retrieve, then a value of
514   *                                       {@code Integer.MAX_VALUE} may be
515   *                                       specified.
516   * @param  alwaysExamineCandidates       Indicates whether the server should
517   *                                       always examine candidate entries to
518   *                                       determine whether they would actually
519   *                                       be returned to the client in a normal
520   *                                       search.  This will only be used for
521   *                                       fully-indexed searches in which the
522   *                                       set of matching entries is known.  If
523   *                                       the value is {@code true} and the
524   *                                       number of candidates is smaller than
525   *                                       {@code maxCandidatesToExamine}, then
526   *                                       each matching entry will be
527   *                                       internally retrieved and examined to
528   *                                       determine whether it would be
529   *                                       returned to the client based on the
530   *                                       details of the search request (e.g.,
531   *                                       whether the requester has permission
532   *                                       to access the entry, whether it's an
533   *                                       LDAP subentry, replication conflict
534   *                                       entry, soft-deleted entry, or other
535   *                                       type of entry that is normally
536   *                                       hidden, etc.) so that an exact count
537   *                                       can be returned.  If this is
538   *                                       {@code false} or the number of
539   *                                       candidates exceeds
540   *                                       {@code maxCandidatesToExamine}, then
541   *                                       the server will only be able to
542   *                                       return an unexamined count which may
543   *                                       include entries that match the search
544   *                                       criteria but that would not normally
545   *                                       be returned to the requester.
546   * @param  processSearchIfUnindexed      Indicates whether the server should
547   *                                       attempt to determine the number of
548   *                                       matching entries if the search
549   *                                       criteria is completely unindexed.  If
550   *                                       this is {@code true} and the
551   *                                       requester has the unindexed-search
552   *                                       privilege, then the server will
553   *                                       iterate through all entries in the
554   *                                       scope (which may take a very long
555   *                                       time to complete) in order to to
556   *                                       determine which of them match the
557   *                                       search criteria so that it can return
558   *                                       an accurate count.  If this is
559   *                                       {@code false} or the requester does
560   *                                       not have the unindexed-search
561   *                                       privilege, then the server will not
562   *                                       spend any time attempting to
563   *                                       determine the number of matching
564   *                                       entries and will instead return a
565   *                                       matching entry count response control
566   *                                       indicating that the entry count is
567   *                                       unknown.
568   * @param  skipResolvingExplodedIndexes  Indicates whether the server should
569   *                                       skip the effort of actually
570   *                                       retrieving the candidate entry IDs
571   *                                       for exploded index keys in which the
572   *                                       number of matching entries is known.
573   *                                       Skipping the process of retrieving
574   *                                       the candidate entry IDs can allow the
575   *                                       server to more quickly estimate the
576   *                                       matching entry count, but the
577   *                                       resulting estimate may be less
578   *                                       accurate.
579   * @param  fastShortCircuitThreshold     Specifies the short-circuit threshold
580   *                                       that the server should use when
581   *                                       determining whether to continue with
582   *                                       index processing in an attempt to
583   *                                       further pare down a candidate set
584   *                                       that already has a defined superset
585   *                                       of the entries that actually match
586   *                                       the filter.  Short-circuiting may
587   *                                       allow the server to skip
588   *                                       potentially-costly index processing
589   *                                       and allow it to obtain the matching
590   *                                       entry count estimate faster, but the
591   *                                       resulting estimate may be less
592   *                                       accurate.  The fast short-circuit
593   *                                       threshold will be used for index
594   *                                       processing that is expected to be
595   *                                       very fast (e.g., when performing
596   *                                       index lookups for presence, equality,
597   *                                       and approximate-match components,
598   *                                       which should only require accessing a
599   *                                       single index key).  A value that is
600   *                                       less than or equal to zero indicates
601   *                                       that the server should never short
602   *                                       circuit when performing fast index
603   *                                       processing.  A value of {@code null}
604   *                                       indicates that the server should
605   *                                       determine the appropriate fast
606   *                                       short-circuit threshold to use.
607   * @param  slowShortCircuitThreshold     Specifies the short-circuit threshold
608   *                                       that the server should use when
609   *                                       determining whether to continue with
610   *                                       index processing for evaluation that
611   *                                       may be more expensive than what falls
612   *                                       into the "fast" category (e.g.,
613   *                                       substring and range filter
614   *                                       components).  A value that is less
615   *                                       than or equal to zero indicates that
616   *                                       the server should never short circuit
617   *                                       when performing slow index
618   *                                       processing.  A value of {@code null}
619   *                                       indicates that the server should
620   *                                       determine the appropriate slow
621   *                                       short-circuit threshold to use.
622   * @param  includeDebugInfo              Indicates whether the server should
623   *                                       include debug information in the
624   *                                       response that may help better
625   *                                       understand how it arrived at the
626   *                                       result.  If any debug information is
627   *                                       returned, it will be in the form of
628   *                                       human-readable text that is not
629   *                                       intended to be machine-parsable.
630   */
631  public MatchingEntryCountRequestControl(final boolean isCritical,
632              final int maxCandidatesToExamine,
633              final boolean alwaysExamineCandidates,
634              final boolean processSearchIfUnindexed,
635              final boolean skipResolvingExplodedIndexes,
636              @Nullable final Long fastShortCircuitThreshold,
637              @Nullable final Long slowShortCircuitThreshold,
638              final boolean includeDebugInfo)
639  {
640    super(MATCHING_ENTRY_COUNT_REQUEST_OID, isCritical,
641         encodeValue(maxCandidatesToExamine, alwaysExamineCandidates,
642              processSearchIfUnindexed, skipResolvingExplodedIndexes,
643              fastShortCircuitThreshold, slowShortCircuitThreshold, false,
644              includeDebugInfo));
645
646    Validator.ensureTrue(maxCandidatesToExamine >= 0);
647
648    this.maxCandidatesToExamine       = maxCandidatesToExamine;
649    this.alwaysExamineCandidates      = alwaysExamineCandidates;
650    this.processSearchIfUnindexed     = processSearchIfUnindexed;
651    this.skipResolvingExplodedIndexes = skipResolvingExplodedIndexes;
652    this.includeDebugInfo             = includeDebugInfo;
653
654    if (fastShortCircuitThreshold == null)
655    {
656      this.fastShortCircuitThreshold = null;
657    }
658    else
659    {
660      this.fastShortCircuitThreshold = Math.max(0L, fastShortCircuitThreshold);
661    }
662
663    if (slowShortCircuitThreshold == null)
664    {
665      this.slowShortCircuitThreshold = null;
666    }
667    else
668    {
669      this.slowShortCircuitThreshold = Math.max(0L, slowShortCircuitThreshold);
670    }
671
672    includeExtendedResponseData = false;
673  }
674
675
676
677  /**
678   * Creates a new matching entry count request control with the provided
679   * properties.
680   *
681   * @param  isCritical  Indicates whether the control should be critical.
682   * @param  properties  The properties that should be used to create this
683   *                     matching entry count request control.  It must not be
684   *                     {@code null}.
685   */
686  public MatchingEntryCountRequestControl(final boolean isCritical,
687       @NotNull final MatchingEntryCountRequestControlProperties properties)
688  {
689    super(MATCHING_ENTRY_COUNT_REQUEST_OID, isCritical,
690         encodeValue(properties.getMaxCandidatesToExamine(),
691              properties.alwaysExamineCandidates(),
692              properties.processSearchIfUnindexed(),
693              properties.skipResolvingExplodedIndexes(),
694              properties.getFastShortCircuitThreshold(),
695              properties.getSlowShortCircuitThreshold(),
696              properties.includeExtendedResponseData(),
697              properties.includeDebugInfo()));
698
699    maxCandidatesToExamine = properties.getMaxCandidatesToExamine();
700    alwaysExamineCandidates = properties.alwaysExamineCandidates();
701    processSearchIfUnindexed = properties.processSearchIfUnindexed();
702    skipResolvingExplodedIndexes = properties.skipResolvingExplodedIndexes();
703    fastShortCircuitThreshold = properties.getFastShortCircuitThreshold();
704    slowShortCircuitThreshold = properties.getSlowShortCircuitThreshold();
705    includeExtendedResponseData = properties.includeExtendedResponseData();
706    includeDebugInfo = properties.includeDebugInfo();
707  }
708
709
710
711  /**
712   * Creates a new matching entry count request control that is decoded from the
713   * provided generic control.
714   *
715   * @param  control  The control to decode as a matching entry count request
716   *                  control.
717   *
718   * @throws  LDAPException  If the provided control cannot be decoded as a
719   *                         matching entry count request control.
720   */
721  public MatchingEntryCountRequestControl(@NotNull final Control control)
722         throws LDAPException
723  {
724    super(control);
725
726    final ASN1OctetString value = control.getValue();
727    if (value == null)
728    {
729      throw new LDAPException(ResultCode.DECODING_ERROR,
730           ERR_MATCHING_ENTRY_COUNT_REQUEST_MISSING_VALUE.get());
731    }
732
733    try
734    {
735      boolean alwaysExamine    = false;
736      boolean debug            = false;
737      boolean includeExtended  = false;
738      boolean processUnindexed = false;
739      boolean skipExploded     = false;
740      int     maxCandidates    = 0;
741      Long    fastSCThreshold  = null;
742      Long    slowSCThreshold  = null;
743      final ASN1Element[] elements =
744           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
745      for (final ASN1Element e : elements)
746      {
747        switch (e.getType())
748        {
749          case TYPE_MAX_CANDIDATES_TO_EXAMINE:
750            maxCandidates = ASN1Integer.decodeAsInteger(e).intValue();
751            if (maxCandidates < 0)
752            {
753              throw new LDAPException(ResultCode.DECODING_ERROR,
754                   ERR_MATCHING_ENTRY_COUNT_REQUEST_INVALID_MAX.get());
755            }
756            break;
757
758          case TYPE_ALWAYS_EXAMINE_CANDIDATES:
759            alwaysExamine = ASN1Boolean.decodeAsBoolean(e).booleanValue();
760            break;
761
762          case TYPE_PROCESS_SEARCH_IF_UNINDEXED:
763            processUnindexed = ASN1Boolean.decodeAsBoolean(e).booleanValue();
764            break;
765
766          case TYPE_INCLUDE_DEBUG_INFO:
767            debug = ASN1Boolean.decodeAsBoolean(e).booleanValue();
768            break;
769
770          case TYPE_SKIP_RESOLVING_EXPLODED_INDEXES:
771            skipExploded = ASN1Boolean.decodeAsBoolean(e).booleanValue();
772            break;
773
774          case TYPE_FAST_SHORT_CIRCUIT_THRESHOLD:
775            fastSCThreshold =
776                 Math.max(0L, ASN1Long.decodeAsLong(e).longValue());
777            break;
778
779          case TYPE_SLOW_SHORT_CIRCUIT_THRESHOLD:
780            slowSCThreshold =
781                 Math.max(0L, ASN1Long.decodeAsLong(e).longValue());
782            break;
783
784          case TYPE_INCLUDE_EXTENDED_RESPONSE_DATA:
785            includeExtended = ASN1Boolean.decodeAsBoolean(e).booleanValue();
786            break;
787        }
788      }
789
790      maxCandidatesToExamine       = maxCandidates;
791      alwaysExamineCandidates      = alwaysExamine;
792      processSearchIfUnindexed     = processUnindexed;
793      includeDebugInfo             = debug;
794      includeExtendedResponseData  = includeExtended;
795      skipResolvingExplodedIndexes = skipExploded;
796      fastShortCircuitThreshold    = fastSCThreshold;
797      slowShortCircuitThreshold    = slowSCThreshold;
798    }
799    catch (final LDAPException le)
800    {
801      Debug.debugException(le);
802      throw le;
803    }
804    catch (final Exception e)
805    {
806      Debug.debugException(e);
807      throw new LDAPException(ResultCode.DECODING_ERROR,
808           ERR_MATCHING_ENTRY_COUNT_REQUEST_CANNOT_DECODE.get(
809                StaticUtils.getExceptionMessage(e)),
810           e);
811    }
812  }
813
814
815
816  /**
817   * Encodes the provided information into an ASN.1 octet string suitable for
818   * use as the control value.
819   *
820   * @param  maxCandidatesToExamine        The maximum number of candidate
821   *                                       entries that the server should
822   *                                       retrieve and examine to determine
823   *                                       whether they actually match the
824   *                                       search criteria.
825   * @param  alwaysExamineCandidates       Indicates whether the server should
826   *                                       always examine candidate entries to
827   *                                       determine whether they would actually
828   *                                       be returned to the client in a normal
829   *                                       search with the same criteria.
830   * @param  processSearchIfUnindexed      Indicates whether the server should
831   *                                       attempt to determine the number of
832   *                                       matching entries if the search
833   *                                       criteria is completely unindexed.
834   * @param  skipResolvingExplodedIndexes  Indicates whether the server should
835   *                                       skip the effort of actually
836   *                                       retrieving the candidate entry IDs
837   *                                       for exploded index keys in which the
838   *                                       number of matching entries is known.
839   * @param  fastShortCircuitThreshold     Specifies the short-circuit threshold
840   *                                       that the server should use when
841   *                                       determining whether to continue with
842   *                                       index processing for fast index
843   *                                       processing.
844   * @param  slowShortCircuitThreshold     Specifies the short-circuit threshold
845   *                                       that the server should use when
846   *                                       determining whether to continue with
847   *                                       index processing for slow index
848   *                                       processing.
849   * @param  includeExtendedResponseData  Indicates whether the server may
850   *                                      include extended response data in the
851   *                                      corresponding response control.
852   * @param  includeDebugInfo              Indicates whether the server should
853   *                                       include debug information in the
854   *                                       response that may help better
855   *                                       understand how it arrived at the
856   *                                       result.
857   *
858   * @return  The ASN.1 octet string containing the encoded control value.
859   */
860  @NotNull()
861  private static ASN1OctetString encodeValue(
862                      final int maxCandidatesToExamine,
863                      final boolean alwaysExamineCandidates,
864                      final boolean processSearchIfUnindexed,
865                      final boolean skipResolvingExplodedIndexes,
866                      @Nullable final Long fastShortCircuitThreshold,
867                      @Nullable final Long slowShortCircuitThreshold,
868                      final boolean includeExtendedResponseData,
869                      final boolean includeDebugInfo)
870  {
871    final ArrayList<ASN1Element> elements = new ArrayList<>(4);
872
873    if (maxCandidatesToExamine > 0)
874    {
875      elements.add(new ASN1Integer(TYPE_MAX_CANDIDATES_TO_EXAMINE,
876           maxCandidatesToExamine));
877    }
878
879    if (alwaysExamineCandidates)
880    {
881      elements.add(new ASN1Boolean(TYPE_ALWAYS_EXAMINE_CANDIDATES, true));
882    }
883
884    if (processSearchIfUnindexed)
885    {
886      elements.add(new ASN1Boolean(TYPE_PROCESS_SEARCH_IF_UNINDEXED, true));
887    }
888
889    if (includeDebugInfo)
890    {
891      elements.add(new ASN1Boolean(TYPE_INCLUDE_DEBUG_INFO, true));
892    }
893
894    if (skipResolvingExplodedIndexes)
895    {
896      elements.add(new ASN1Boolean(TYPE_SKIP_RESOLVING_EXPLODED_INDEXES, true));
897    }
898
899    if (fastShortCircuitThreshold != null)
900    {
901      elements.add(new ASN1Long(TYPE_FAST_SHORT_CIRCUIT_THRESHOLD,
902           Math.max(0L, fastShortCircuitThreshold)));
903    }
904
905    if (slowShortCircuitThreshold != null)
906    {
907      elements.add(new ASN1Long(TYPE_SLOW_SHORT_CIRCUIT_THRESHOLD,
908           Math.max(0L, slowShortCircuitThreshold)));
909    }
910
911    if (includeExtendedResponseData)
912    {
913      elements.add(new ASN1Boolean(TYPE_INCLUDE_EXTENDED_RESPONSE_DATA, true));
914    }
915
916    return new ASN1OctetString(new ASN1Sequence(elements).encode());
917  }
918
919
920
921  /**
922   * Retrieves the maximum number of candidate entries that should be examined
923   * in order to determine accurate count of the number of matching entries.
924   * <BR><BR>
925   * For a fully-indexed search, this property will only be used if
926   * {@link #alwaysExamineCandidates} is true.  If the number of candidate
927   * entries identified is less than the maximum number of candidates to
928   * examine, then the server will return an {@code EXAMINED_COUNT} result that
929   * indicates the number of entries matching the criteria that would actually
930   * be returned in a normal search with the same criteria.  If the number of
931   * candidate entries exceeds the maximum number of candidates to examine, then
932   * the server will return an {@code UNEXAMINED_COUNT} result that indicates
933   * the number of entries matching the search criteria but that may include
934   * entries that would not actually be returned to the client.
935   * <BR><BR>
936   * For a partially-indexed search, if the upper bound on the number of
937   * candidates is less than or equal to the maximum number of candidates to
938   * examine, then the server will internally retrieve and examine each of those
939   * candidates to determine which of them match the search criteria and would
940   * actually be returned to the client, and will then return an
941   * {@code EXAMINED_COUNT} result with that count.  If the upper bound on the
942   * number of candidates is greater than the maximum number of candidates to
943   * examine, then the server will return an {@code UPPER_BOUND} result to
944   * indicate that the exact count is not known but an upper bound is available.
945   *
946   * @return  The maximum number of candidate entries to examine in order to
947   *          determine an accurate count of the number of matching entries.
948   */
949  public int getMaxCandidatesToExamine()
950  {
951    return maxCandidatesToExamine;
952  }
953
954
955
956  /**
957   * Indicates whether the server should always examine candidate entries in
958   * fully-indexed searches to determine whether they would actually be returned
959   * to the client in a normal search with the same criteria.
960   *
961   * @return  {@code true} if the server should attempt to internally retrieve
962   *          and examine matching entries to determine whether they would
963   *          normally be returned to the client (e.g., that the client has
964   *          permission to access the entry and that it is not a
965   *          normally-hidden entry like an LDAP subentry, a replication
966   *          conflict entry, or a soft-deleted entry), or {@code false} if the
967   *          server should return an unverified count.
968   */
969  public boolean alwaysExamineCandidates()
970  {
971    return alwaysExamineCandidates;
972  }
973
974
975
976  /**
977   * Indicates whether the server should internally retrieve and examine all
978   * entries within the search scope in order to obtain an exact matching entry
979   * count for an unindexed search.  Note that this value will not be considered
980   * for completely-indexed or partially-indexed searches, nor for searches in
981   * which matching entries should be returned.
982   *
983   * @return  {@code true} if the server should internally retrieve and examine
984   *          all entries within the search scope in order to obtain an exact
985   *          matching entry count for an unindexed search, or {@code false} if
986   *          not.
987   */
988  public boolean processSearchIfUnindexed()
989  {
990    return processSearchIfUnindexed;
991  }
992
993
994
995  /**
996   * Indicates whether the server should skip the effort of actually retrieving
997   * the candidate entry IDs for exploded index keys in which the number of
998   * matching entries is known.  Skipping the process of accessing an exploded
999   * index can allow the server to more quickly arrive at the matching entry
1000   * count estimate, but that estimate may be less accurate than if it had
1001   * actually retrieved those candidates.
1002   *
1003   * @return  {@code true} if the server should skip the effort of actually
1004   *          retrieving the candidate entry IDs for exploded index keys in
1005   *          which the number of matching entries is known, or {@code false} if
1006   *          it may retrieve candidates from an exploded index in the course of
1007   *          determining the matching entry count.
1008   */
1009  public boolean skipResolvingExplodedIndexes()
1010  {
1011    return skipResolvingExplodedIndexes;
1012  }
1013
1014
1015
1016  /**
1017   * Retrieves the short-circuit threshold that the server should use when
1018   * determining whether to continue with index processing in an attempt to
1019   * further pare down a candidate set that already has a defined superset of
1020   * the entries that actually match the filter.  If the number of entries in
1021   * that candidate set is less than or equal to the short-circuit threshold,
1022   * then the server may simply use that candidate set in the course of
1023   * determining the matching entry count, even if there may be additional
1024   * processing that can be performed (e.g., further filter components to
1025   * evaluate) that may allow the server to pare down the results even further.
1026   * Short-circuiting may allow the server to obtain the matching entry count
1027   * estimate faster, but may also cause the resulting estimate to be less
1028   * accurate.
1029   * <BR><BR>
1030   * The value returned by this method will be used for cases in which the
1031   * server is performing the fastest types of index processing.  For example,
1032   * this may include evaluating presence, equality, or approximate match
1033   * components, which should only require retrieving a single index key to
1034   * obtain the candidate set.
1035   *
1036   * @return  The short-circuit threshold that should be used for fast index
1037   *          processing, zero if the server should not short-circuit at all
1038   *          during fast index processing, or {@code null} if the server should
1039   *          determine the appropriate fast short-circuit threshold to use.
1040   */
1041  @Nullable()
1042  public Long getFastShortCircuitThreshold()
1043  {
1044    return fastShortCircuitThreshold;
1045  }
1046
1047
1048
1049  /**
1050   * Retrieves the short-circuit threshold that the server should use when
1051   * determining whether to continue with index processing in an attempt to
1052   * further pare down a candidate set that already has a defined superset of
1053   * the entries that actually match the filter.  If the number of entries in
1054   * that candidate set is less than or equal to the short-circuit threshold,
1055   * then the server may simply use that candidate set in the course of
1056   * determining the matching entry count, even if there may be additional
1057   * processing that can be performed (e.g., further filter components to
1058   * evaluate) that may allow the server to pare down the results even further.
1059   * Short-circuiting may allow the server to obtain the matching entry count
1060   * estimate faster, but may also cause the resulting estimate to be less
1061   * accurate.
1062   * <BR><BR>
1063   * The value returned by this method will be used for cases in which the
1064   * server is performing index processing that is not considered to be among
1065   * the fastest types of processing.  For example, this may include evaluating
1066   * substring and range components, as they may require retrieving many index
1067   * keys to obtain the full candidate set.
1068   *
1069   * @return  The short-circuit threshold that should be used for slow index
1070   *          processing, or zero if the server should not short-circuit at all
1071   *          during slow index processing, or {@code null} if the server should
1072   *          determine the appropriate slow short-circuit threshold to use.
1073   */
1074  @Nullable()
1075  public Long getSlowShortCircuitThreshold()
1076  {
1077    return slowShortCircuitThreshold;
1078  }
1079
1080
1081
1082  /**
1083   * Indicates whether the server may include extended response data in the
1084   * corresponding response control, which may provide information like whether
1085   * all of the identified candidate entries are within the scope of the search
1086   * and any unindexed or unevaluated portion of the search filter.
1087   *
1088   * @return  {@code true} if the server may include extended response data
1089   *          in the corresponding response control, or {@code false} if not.
1090   */
1091  public boolean includeExtendedResponseData()
1092  {
1093    return includeExtendedResponseData;
1094  }
1095
1096
1097
1098  /**
1099   * Attempts to determine whether the server to which the provided connection
1100   * is established supports including extended response data in the matching
1101   * entry count response control.
1102   *
1103   * @param  connection  The connection (or connection pool or other interface)
1104   *                     to use to communicate with the server.  It must not be
1105   *                     {@code null} and must be established.
1106   *
1107   * @return  {@code true} if the server reports that supports including
1108   *          extended response data in the matching entry count response
1109   *          control, or {@code false} if it does not indicate that it is
1110   *          supported.
1111   *
1112   * @throws  LDAPException  If a problem occurs while attempting to communicate
1113   *                         with the server.
1114   */
1115  public static boolean serverSupportsExtendedResponseData(
1116              @NotNull final LDAPInterface connection)
1117         throws LDAPException
1118  {
1119    final RootDSE rootDSE = connection.getRootDSE();
1120    return ((rootDSE != null) && serverSupportsExtendedResponseData(rootDSE));
1121  }
1122
1123
1124
1125  /**
1126   * Determines whether the provided root DSE indicates that the associated
1127   * server supports including extended response data in the matching entry
1128   * count response control.
1129   *
1130   * @param  rootDSE  The root DSE retrieved from the server for which to make
1131   *                  the determination.  It must not be {@code null}.
1132   *
1133   * @return  {@code true} if the root DSE indicates that supports including
1134   *          extended response data in the matching entry count response
1135   *          control, or {@code false} if not.
1136   */
1137  public static boolean serverSupportsExtendedResponseData(
1138              @NotNull final RootDSE rootDSE)
1139  {
1140    return rootDSE.supportsFeature(EXTENDED_RESPONSE_DATA_FEATURE_OID);
1141  }
1142
1143
1144
1145  /**
1146   * Indicates whether the server should include debug information in the
1147   * response control that provides additional information about how the server
1148   * arrived at the result.  If debug information is to be provided, it will be
1149   * in a human-readable rather than machine-parsable form.
1150   *
1151   * @return  {@code true} if the server should include debug information in
1152   *          the response control, or {@code false} if not.
1153   */
1154  public boolean includeDebugInfo()
1155  {
1156    return includeDebugInfo;
1157  }
1158
1159
1160
1161  /**
1162   * {@inheritDoc}
1163   */
1164  @Override()
1165  @NotNull()
1166  public String getControlName()
1167  {
1168    return INFO_CONTROL_NAME_MATCHING_ENTRY_COUNT_REQUEST.get();
1169  }
1170
1171
1172
1173  /**
1174   * Retrieves a representation of this matching entry count request control as
1175   * a JSON object.  The JSON object uses the following fields:
1176   * <UL>
1177   *   <LI>
1178   *     {@code oid} -- A mandatory string field whose value is the object
1179   *     identifier for this control.  For the matching entry count request
1180   *     control, the OID is "1.3.6.1.4.1.30221.2.5.36".
1181   *   </LI>
1182   *   <LI>
1183   *     {@code control-name} -- An optional string field whose value is a
1184   *     human-readable name for this control.  This field is only intended for
1185   *     descriptive purposes, and when decoding a control, the {@code oid}
1186   *     field should be used to identify the type of control.
1187   *   </LI>
1188   *   <LI>
1189   *     {@code criticality} -- A mandatory Boolean field used to indicate
1190   *     whether this control is considered critical.
1191   *   </LI>
1192   *   <LI>
1193   *     {@code value-base64} -- An optional string field whose value is a
1194   *     base64-encoded representation of the raw value for this matching entry
1195   *     count request control.  Exactly one of the {@code value-base64} and
1196   *     {@code value-json} fields must be present.
1197   *   </LI>
1198   *   <LI>
1199   *     {@code value-json} -- An optional JSON object field whose value is a
1200   *     user-friendly representation of the value for this matching entry count
1201   *     request control.  Exactly one of the {@code value-base64} and
1202   *     {@code value-json} fields must be present, and if the
1203   *     {@code value-json} field is used, then it will use the following
1204   *     fields:
1205   *     <UL>
1206   *       <LI>
1207   *         {@code maximum-candidates-to-examine} -- An optional integer field
1208   *         whose value indicates the maximum number of entries that the server
1209   *         should retrieve and examine to determine whether they actually
1210   *         match the search criteria and would be returned to the client.
1211   *       </LI>
1212   *       <LI>
1213   *         {@code always-examine-candidates} -- An optional Boolean field that
1214   *         indicates whether the server should always examine candidates to
1215   *         determine whether they would actually be returned to the client,
1216   *         even if the search is fully indexed and the server knows that all
1217   *         candidates match the search criteria.
1218   *       </LI>
1219   *       <LI>
1220   *         {@code process-search-if-unindexed} -- An optional Boolean field
1221   *         that indicates whether the server should attempt to process the
1222   *         search even if the search criteria is completely unindexed.
1223   *       </LI>
1224   *       <LI>
1225   *         {@code include-debug-info} -- An optional Boolean field
1226   *         that indicates whether the server should return debug information
1227   *         in the response control about the processing it performed in the
1228   *         course of determining the matching entry count estimate.
1229   *       </LI>
1230   *       <LI>
1231   *         {@code skip-resolving-exploded-indexes} -- An optional Boolean
1232   *         field that indicates whether the server should skip the step of
1233   *         resolving exploded indexes in cases where the size of the ID set is
1234   *         known.
1235   *       </LI>
1236   *       <LI>
1237   *         {@code fast-short-circuit-threshold} -- An optional integer field
1238   *         whose value is the short-circuit threshold that the server should
1239   *         use for "fast" filter components.
1240   *       </LI>
1241   *       <LI>
1242   *         {@code slow-short-circuit-threshold} -- An optional integer field
1243   *         whose value is the short-circuit threshold that the server should
1244   *         use for "slow" filter components.
1245   *       </LI>
1246   *       <LI>
1247   *         {@code include-extended-response-data} -- An optional Boolean field
1248   *         that indicates whether the server should include extended
1249   *         information in the response control.
1250   *       </LI>
1251   *     </UL>
1252   *   </LI>
1253   * </UL>
1254   *
1255   * @return  A JSON object that contains a representation of this control.
1256   */
1257  @Override()
1258  @NotNull()
1259  public JSONObject toJSONControl()
1260  {
1261    final Map<String,JSONValue> valueFields = new LinkedHashMap<>();
1262
1263    if (maxCandidatesToExamine > 0)
1264    {
1265      valueFields.put(JSON_FIELD_MAXIMUM_CANDIDATES_TO_EXAMINE,
1266           new JSONNumber(maxCandidatesToExamine));
1267    }
1268    else
1269    {
1270      valueFields.put(JSON_FIELD_MAXIMUM_CANDIDATES_TO_EXAMINE,
1271           new JSONNumber(0));
1272    }
1273
1274    valueFields.put(JSON_FIELD_ALWAYS_EXAMINE_CANDIDATES,
1275         new JSONBoolean(alwaysExamineCandidates));
1276    valueFields.put(JSON_FIELD_PROCESS_SEARCH_IF_UNINDEXED,
1277         new JSONBoolean(processSearchIfUnindexed));
1278    valueFields.put(JSON_FIELD_INCLUDE_DEBUG_INFO,
1279         new JSONBoolean(includeDebugInfo));
1280    valueFields.put(JSON_FIELD_SKIP_RESOLVING_EXPLODED_INDEXES,
1281         new JSONBoolean(skipResolvingExplodedIndexes));
1282
1283    if (fastShortCircuitThreshold != null)
1284    {
1285      valueFields.put(JSON_FIELD_FAST_SHORT_CIRCUIT_THRESHOLD,
1286           new JSONNumber(fastShortCircuitThreshold));
1287    }
1288
1289    if (slowShortCircuitThreshold != null)
1290    {
1291      valueFields.put(JSON_FIELD_SLOW_SHORT_CIRCUIT_THRESHOLD,
1292           new JSONNumber(slowShortCircuitThreshold));
1293    }
1294
1295    valueFields.put(JSON_FIELD_INCLUDE_EXTENDED_RESPONSE_DATA,
1296         new JSONBoolean(includeExtendedResponseData));
1297
1298    return new JSONObject(
1299         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
1300              MATCHING_ENTRY_COUNT_REQUEST_OID),
1301         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
1302              INFO_CONTROL_NAME_MATCHING_ENTRY_COUNT_REQUEST.get()),
1303         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
1304              isCritical()),
1305         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
1306              new JSONObject(valueFields)));
1307  }
1308
1309
1310
1311  /**
1312   * Attempts to decode the provided object as a JSON representation of a
1313   * matching entry count request control.
1314   *
1315   * @param  controlObject  The JSON object to be decoded.  It must not be
1316   *                        {@code null}.
1317   * @param  strict         Indicates whether to use strict mode when decoding
1318   *                        the provided JSON object.  If this is {@code true},
1319   *                        then this method will throw an exception if the
1320   *                        provided JSON object contains any unrecognized
1321   *                        fields.  If this is {@code false}, then unrecognized
1322   *                        fields will be ignored.
1323   *
1324   * @return  The matching entry count request control that was decoded from the
1325   *          provided JSON object.
1326   *
1327   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
1328   *                         valid matching entry count request control.
1329   */
1330  @NotNull()
1331  public static MatchingEntryCountRequestControl decodeJSONControl(
1332              @NotNull final JSONObject controlObject,
1333              final boolean strict)
1334         throws LDAPException
1335  {
1336    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
1337         controlObject, strict, true, true);
1338
1339    final ASN1OctetString rawValue = jsonControl.getRawValue();
1340    if (rawValue != null)
1341    {
1342      return new MatchingEntryCountRequestControl(new Control(
1343           jsonControl.getOID(), jsonControl.getCriticality(), rawValue));
1344    }
1345
1346
1347    final JSONObject valueObject = jsonControl.getValueObject();
1348
1349    final MatchingEntryCountRequestControlProperties properties =
1350         new MatchingEntryCountRequestControlProperties();
1351
1352    final Integer maximumCandidatesToExamine = valueObject.getFieldAsInteger(
1353         JSON_FIELD_MAXIMUM_CANDIDATES_TO_EXAMINE);
1354    if (maximumCandidatesToExamine == null)
1355    {
1356      properties.setMaxCandidatesToExamine(0);
1357    }
1358    else
1359    {
1360      properties.setMaxCandidatesToExamine(maximumCandidatesToExamine);
1361    }
1362
1363
1364    final Boolean alwaysExamineCandidates =
1365         valueObject.getFieldAsBoolean(JSON_FIELD_ALWAYS_EXAMINE_CANDIDATES);
1366    if (alwaysExamineCandidates == null)
1367    {
1368      properties.setAlwaysExamineCandidates(false);
1369    }
1370    else
1371    {
1372      properties.setAlwaysExamineCandidates(alwaysExamineCandidates);
1373    }
1374
1375
1376    final Boolean processSearchIfUnindexed =
1377         valueObject.getFieldAsBoolean(JSON_FIELD_PROCESS_SEARCH_IF_UNINDEXED);
1378    if (processSearchIfUnindexed == null)
1379    {
1380      properties.setProcessSearchIfUnindexed(false);
1381    }
1382    else
1383    {
1384      properties.setProcessSearchIfUnindexed(processSearchIfUnindexed);
1385    }
1386
1387
1388    final Boolean includeDebugInfo =
1389         valueObject.getFieldAsBoolean(JSON_FIELD_INCLUDE_DEBUG_INFO);
1390    if (includeDebugInfo == null)
1391    {
1392      properties.setIncludeDebugInfo(false);
1393    }
1394    else
1395    {
1396      properties.setIncludeDebugInfo(includeDebugInfo);
1397    }
1398
1399
1400    final Boolean skipResolvingExplodedIndexes =
1401         valueObject.getFieldAsBoolean(
1402              JSON_FIELD_SKIP_RESOLVING_EXPLODED_INDEXES);
1403    if (skipResolvingExplodedIndexes == null)
1404    {
1405      properties.setSkipResolvingExplodedIndexes(false);
1406    }
1407    else
1408    {
1409      properties.setSkipResolvingExplodedIndexes(skipResolvingExplodedIndexes);
1410    }
1411
1412
1413    final Long fastShortCircuitThreshold =
1414         valueObject.getFieldAsLong(JSON_FIELD_FAST_SHORT_CIRCUIT_THRESHOLD);
1415    if (fastShortCircuitThreshold != null)
1416    {
1417      properties.setFastShortCircuitThreshold(fastShortCircuitThreshold);
1418    }
1419
1420
1421    final Long slowShortCircuitThreshold =
1422         valueObject.getFieldAsLong(JSON_FIELD_SLOW_SHORT_CIRCUIT_THRESHOLD);
1423    if (slowShortCircuitThreshold != null)
1424    {
1425      properties.setSlowShortCircuitThreshold(slowShortCircuitThreshold);
1426    }
1427
1428
1429    final Boolean includeExtendedResponseData = valueObject.getFieldAsBoolean(
1430         JSON_FIELD_INCLUDE_EXTENDED_RESPONSE_DATA);
1431    if (includeExtendedResponseData == null)
1432    {
1433      properties.setIncludeExtendedResponseData(false);
1434    }
1435    else
1436    {
1437      properties.setIncludeExtendedResponseData(includeExtendedResponseData);
1438    }
1439
1440
1441    if (strict)
1442    {
1443      final List<String> unrecognizedFields =
1444           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
1445                valueObject, JSON_FIELD_MAXIMUM_CANDIDATES_TO_EXAMINE,
1446                JSON_FIELD_ALWAYS_EXAMINE_CANDIDATES,
1447                JSON_FIELD_PROCESS_SEARCH_IF_UNINDEXED,
1448                JSON_FIELD_INCLUDE_DEBUG_INFO,
1449                JSON_FIELD_SKIP_RESOLVING_EXPLODED_INDEXES,
1450                JSON_FIELD_FAST_SHORT_CIRCUIT_THRESHOLD,
1451                JSON_FIELD_SLOW_SHORT_CIRCUIT_THRESHOLD,
1452                JSON_FIELD_INCLUDE_EXTENDED_RESPONSE_DATA);
1453      if (! unrecognizedFields.isEmpty())
1454      {
1455        throw new LDAPException(ResultCode.DECODING_ERROR,
1456             ERR_MATCHING_ENTRY_COUNT_RESPONSE_JSON_UNRECOGNIZED_FIELD.get(
1457                  controlObject.toSingleLineString(),
1458                  unrecognizedFields.get(0)));
1459      }
1460    }
1461
1462
1463    return new MatchingEntryCountRequestControl(jsonControl.getCriticality(),
1464         properties);
1465  }
1466
1467
1468
1469  /**
1470   * {@inheritDoc}
1471   */
1472  @Override()
1473  public void toString(@NotNull final StringBuilder buffer)
1474  {
1475    buffer.append("MatchingEntryCountRequestControl(isCritical=");
1476    buffer.append(isCritical());
1477    buffer.append(", maxCandidatesToExamine=");
1478    buffer.append(maxCandidatesToExamine);
1479    buffer.append(", alwaysExamineCandidates=");
1480    buffer.append(alwaysExamineCandidates);
1481    buffer.append(", processSearchIfUnindexed=");
1482    buffer.append(processSearchIfUnindexed);
1483    buffer.append(", skipResolvingExplodedIndexes=");
1484    buffer.append(skipResolvingExplodedIndexes);
1485    buffer.append(", fastShortCircuitThreshold=");
1486    buffer.append(fastShortCircuitThreshold);
1487    buffer.append(", slowShortCircuitThreshold=");
1488    buffer.append(slowShortCircuitThreshold);
1489    buffer.append(", includeExtendedResponseData=");
1490    buffer.append(includeExtendedResponseData);
1491    buffer.append(", includeDebugInfo=");
1492    buffer.append(includeDebugInfo);
1493    buffer.append(')');
1494  }
1495}