001/*
002 * Copyright 2007-2022 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2022 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) 2007-2022 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.controls;
037
038
039
040import com.unboundid.asn1.ASN1Element;
041import com.unboundid.asn1.ASN1Integer;
042import com.unboundid.asn1.ASN1OctetString;
043import com.unboundid.asn1.ASN1Sequence;
044import com.unboundid.ldap.sdk.Control;
045import com.unboundid.ldap.sdk.LDAPException;
046import com.unboundid.ldap.sdk.ResultCode;
047import com.unboundid.util.Debug;
048import com.unboundid.util.NotMutable;
049import com.unboundid.util.NotNull;
050import com.unboundid.util.Nullable;
051import com.unboundid.util.StaticUtils;
052import com.unboundid.util.ThreadSafety;
053import com.unboundid.util.ThreadSafetyLevel;
054import com.unboundid.util.Validator;
055
056import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
057
058
059
060/**
061 * This class provides an implementation of the LDAP virtual list view (VLV)
062 * request control as defined in draft-ietf-ldapext-ldapv3-vlv.  This control
063 * may be used to retrieve arbitrary "pages" of entries from the complete set of
064 * search results.  It is similar to the {@link SimplePagedResultsControl}, with
065 * the exception that the simple paged results control requires scrolling
066 * through the results in sequential order, while the VLV control allows
067 * starting and resuming at any arbitrary point in the result set.  The starting
068 * point may be specified using either a positional offset, or based on the
069 * first entry with a value that is greater than or equal to a specified value.
070 * <BR><BR>
071 * When the start of the result set is to be specified using an offset, then the
072 * virtual list view request control should include the following elements:
073 * <UL>
074 *   <LI>{@code targetOffset} -- The position in the result set of the entry to
075 *       target for the next page of results to return.  Note that the offset is
076 *       one-based (so the first entry has offset 1, the second entry has offset
077 *       2, etc.).</LI>
078 *   <LI>{@code beforeCount} -- The number of entries before the entry specified
079 *       as the target offset that should be retrieved.</LI>
080 *   <LI>{@code afterCount} -- The number of entries after the entry specified
081 *       as the target offset that should be retrieved.</LI>
082 *   <LI>{@code contentCount} -- The estimated total number of entries that
083 *       are in the total result set.  This should be zero for the first request
084 *       in a VLV search sequence, but should be the value returned by the
085 *       server in the corresponding response control for subsequent searches as
086 *       part of the VLV sequence.</LI>
087 *   <LI>{@code contextID} -- This is an optional cookie that may be used to
088 *       help the server resume processing on a VLV search.  It should be absent
089 *       from the initial request, but for subsequent requests should be the
090 *       value returned in the previous VLV response control.</LI>
091 * </UL>
092 * When the start of the result set is to be specified using a search string,
093 * then the virtual list view request control should include the following
094 * elements:
095 * <UL>
096 *   <LI>{@code assertionValue} -- The value that specifies the start of the
097 *       page of results to retrieve.  The target entry will be the first entry
098 *       in which the value for the primary sort attribute is greater than or
099 *       equal to this assertion value.</LI>
100 *   <LI>{@code beforeCount} -- The number of entries before the entry specified
101 *        by the assertion value that should be retrieved.</LI>
102 *   <LI>{@code afterCount} -- The number of entries after the entry specified
103 *       by the assertion value that should be retrieved.</LI>
104 *   <LI>{@code contentCount} -- The estimated total number of entries that
105 *       are in the total result set.  This should be zero for the first request
106 *       in a VLV search sequence, but should be the value returned by the
107 *       server in the corresponding response control for subsequent searches as
108 *       part of the VLV sequence.</LI>
109 *   <LI>{@code contextID} -- This is an optional cookie that may be used to
110 *       help the server resume processing on a VLV search.  It should be absent
111 *       from the initial request, but for subsequent requests should be the
112 *       value returned in the previous VLV response control.</LI>
113 * </UL>
114 * Note that the virtual list view request control may only be included in a
115 * search request if that search request also includes the
116 * {@link ServerSideSortRequestControl}.  This is necessary to ensure that a
117 * consistent order is used for the resulting entries.
118 * <BR><BR>
119 * If the search is successful, then the search result done response may include
120 * a {@link VirtualListViewResponseControl} to provide information about the
121 * state of the virtual list view processing.
122 * <BR><BR>
123 * <H2>Example</H2>
124 * The following example demonstrates the use of the virtual list view request
125 * control to iterate through all users, retrieving up to 10 entries at a time:
126 * <PRE>
127 * // Perform a search to retrieve all users in the server, but only retrieving
128 * // ten at a time.  Ensure that the users are sorted in ascending order by
129 * // last name, then first name.
130 * int numSearches = 0;
131 * int totalEntriesReturned = 0;
132 * SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
133 *      SearchScope.SUB, Filter.createEqualityFilter("objectClass", "person"));
134 * int vlvOffset = 1;
135 * int vlvContentCount = 0;
136 * ASN1OctetString vlvContextID = null;
137 * while (true)
138 * {
139 *   // Note that the VLV control always requires the server-side sort
140 *   // control.
141 *   searchRequest.setControls(
142 *        new ServerSideSortRequestControl(new SortKey("sn"),
143 *             new SortKey("givenName")),
144 *        new VirtualListViewRequestControl(vlvOffset, 0, 9, vlvContentCount,
145 *             vlvContextID));
146 *   SearchResult searchResult = connection.search(searchRequest);
147 *   numSearches++;
148 *   totalEntriesReturned += searchResult.getEntryCount();
149 *   for (SearchResultEntry e : searchResult.getSearchEntries())
150 *   {
151 *     // Do something with each entry...
152 *   }
153 *
154 *   LDAPTestUtils.assertHasControl(searchResult,
155 *        VirtualListViewResponseControl.VIRTUAL_LIST_VIEW_RESPONSE_OID);
156 *   VirtualListViewResponseControl vlvResponseControl =
157 *        VirtualListViewResponseControl.get(searchResult);
158 *   vlvContentCount = vlvResponseControl.getContentCount();
159 *   vlvOffset += 10;
160 *   vlvContextID = vlvResponseControl.getContextID();
161 *   if (vlvOffset &gt; vlvContentCount)
162 *   {
163 *     break;
164 *   }
165 * }
166 * </PRE>
167 */
168@NotMutable()
169@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
170public final class VirtualListViewRequestControl
171       extends Control
172{
173  /**
174   * The OID (2.16.840.1.113730.3.4.9) for the virtual list view request
175   * control.
176   */
177  @NotNull public static final String VIRTUAL_LIST_VIEW_REQUEST_OID =
178       "2.16.840.1.113730.3.4.9";
179
180
181
182  /**
183   * The BER type that will be used for the target element when the target is
184   * specified by offset.
185   */
186  private static final byte TARGET_TYPE_OFFSET = (byte) 0xA0;
187
188
189
190  /**
191   * The BER type that will be used for the target element when the target is
192   * specified by an assertion value.
193   */
194  private static final byte TARGET_TYPE_GREATER_OR_EQUAL = (byte) 0x81;
195
196
197
198  /**
199   * The serial version UID for this serializable class.
200   */
201  private static final long serialVersionUID = 4348423177859960815L;
202
203
204
205  // The assertion value that will be used to identify the start of the
206  // requested page of results for a greater-or-equal target type.
207  @Nullable private final ASN1OctetString assertionValue;
208
209  // The context ID that may be used to help the server continue in the same
210  // result set for subsequent searches.
211  @Nullable private final ASN1OctetString contextID;
212
213  // The maximum number of entries to return after the target entry.
214  private final int afterCount;
215
216  // The maximum number of entries to return before the target entry.
217  private final int beforeCount;
218
219  // The estimated number of entries in the complete result set.
220  private final int contentCount;
221
222  // The position of the entry at the start of the requested page of results for
223  // an offset-based target type.
224  private final int targetOffset;
225
226
227
228  /**
229   * Creates a new virtual list view request control that will identify the
230   * beginning of the result set by a target offset.  It will be marked
231   * critical.
232   *
233   * @param  targetOffset  The position of the entry that should be used as the
234   *                       start of the result set.
235   * @param  beforeCount   The maximum number of entries that should be returned
236   *                       before the entry with the specified target offset.
237   * @param  afterCount    The maximum number of entries that should be returned
238   *                       after the entry with the specified target offset.
239   * @param  contentCount  The estimated number of entries in the result set.
240   *                       For the first request in a series of searches with
241   *                       the VLV control, it should be zero.  For subsequent
242   *                       searches in the VLV sequence, it should be the
243   *                       content count included in the response control from
244   *                       the previous search.
245   * @param  contextID     The context ID that may be used to help the server
246   *                       continue in the same result set for subsequent
247   *                       searches.  For the first request in a series of
248   *                       searches with the VLV control, it should be
249   *                       {@code null}.  For subsequent searches in the VLV
250   *                       sequence, it should be the (possibly {@code null})
251   *                       context ID included in the response control from the
252   *                       previous search.
253   */
254  public VirtualListViewRequestControl(final int targetOffset,
255              final int beforeCount, final int afterCount,
256              final int contentCount,
257              @Nullable final ASN1OctetString contextID)
258  {
259    this(targetOffset, beforeCount, afterCount, contentCount, contextID, true);
260  }
261
262
263
264  /**
265   * Creates a new virtual list view request control that will identify the
266   * beginning of the result set by an assertion value.  It will be marked
267   * critical.
268   *
269   * @param  assertionValue  The assertion value that will be used to identify
270   *                         the start of the result set.  The target entry will
271   *                         be the first entry with a value for the primary
272   *                         sort attribute that is greater than or equal to
273   *                         this assertion value.  It must not be {@code null}.
274   * @param  beforeCount     The maximum number of entries that should be
275   *                         returned before the first entry with a value
276   *                         greater than or equal to the provided assertion
277   *                         value.
278   * @param  afterCount      The maximum number of entries that should be
279   *                         returned after the first entry with a value
280   *                         greater than or equal to the provided assertion
281   *                         value.
282   * @param  contextID       The context ID that may be used to help the server
283   *                         continue in the same result set for subsequent
284   *                         searches.  For the first request in a series of
285   *                         searches with the VLV control, it should be
286   *                         {@code null}.  For subsequent searches in the VLV
287   *                         sequence, it should be the (possibly {@code null})
288   *                         context ID included in the response control from
289   *                         the previous search.
290   */
291  public VirtualListViewRequestControl(@NotNull final String assertionValue,
292              final int beforeCount, final int afterCount,
293              @Nullable final ASN1OctetString contextID)
294  {
295    this(new ASN1OctetString(assertionValue), beforeCount, afterCount,
296         contextID, true);
297  }
298
299
300
301  /**
302   * Creates a new virtual list view request control that will identify the
303   * beginning of the result set by an assertion value.  It will be marked
304   * critical.
305   *
306   * @param  assertionValue  The assertion value that will be used to identify
307   *                         the start of the result set.  The target entry will
308   *                         be the first entry with a value for the primary
309   *                         sort attribute that is greater than or equal to
310   *                         this assertion value.  It must not be {@code null}.
311   * @param  beforeCount     The maximum number of entries that should be
312   *                         returned before the first entry with a value
313   *                         greater than or equal to the provided assertion
314   *                         value.
315   * @param  afterCount      The maximum number of entries that should be
316   *                         returned after the first entry with a value
317   *                         greater than or equal to the provided assertion
318   *                         value.
319   * @param  contextID       The context ID that may be used to help the server
320   *                         continue in the same result set for subsequent
321   *                         searches.  For the first request in a series of
322   *                         searches with the VLV control, it should be
323   *                         {@code null}.  For subsequent searches in the VLV
324   *                         sequence, it should be the (possibly {@code null})
325   *                         context ID included in the response control from
326   *                         the previous search.
327   */
328  public VirtualListViewRequestControl(@NotNull final byte[] assertionValue,
329              final int beforeCount, final int afterCount,
330              @Nullable final ASN1OctetString contextID)
331  {
332    this(new ASN1OctetString(assertionValue), beforeCount, afterCount,
333         contextID, true);
334  }
335
336
337
338  /**
339   * Creates a new virtual list view request control that will identify the
340   * beginning of the result set by an assertion value.  It will be marked
341   * critical.
342   *
343   * @param  assertionValue  The assertion value that will be used to identify
344   *                         the start of the result set.  The target entry will
345   *                         be the first entry with a value for the primary
346   *                         sort attribute that is greater than or equal to
347   *                         this assertion value.  It must not be {@code null}.
348   * @param  beforeCount     The maximum number of entries that should be
349   *                         returned before the first entry with a value
350   *                         greater than or equal to the provided assertion
351   *                         value.
352   * @param  afterCount      The maximum number of entries that should be
353   *                         returned after the first entry with a value
354   *                         greater than or equal to the provided assertion
355   *                         value.
356   * @param  contextID       The context ID that may be used to help the server
357   *                         continue in the same result set for subsequent
358   *                         searches.  For the first request in a series of
359   *                         searches with the VLV control, it should be
360   *                         {@code null}.  For subsequent searches in the VLV
361   *                         sequence, it should be the (possibly {@code null})
362   *                         context ID included in the response control from
363   *                         the previous search.
364   */
365  public VirtualListViewRequestControl(
366              @NotNull final ASN1OctetString assertionValue,
367              final int beforeCount, final int afterCount,
368              @Nullable final ASN1OctetString contextID)
369  {
370    this(assertionValue, beforeCount, afterCount, contextID, true);
371  }
372
373
374
375  /**
376   * Creates a new virtual list view request control that will identify the
377   * beginning of the result set by a target offset.
378   *
379   * @param  targetOffset  The position of the entry that should be used as the
380   *                       start of the result set.
381   * @param  beforeCount   The maximum number of entries that should be returned
382   *                       before the entry with the specified target offset.
383   * @param  afterCount    The maximum number of entries that should be returned
384   *                       after the entry with the specified target offset.
385   * @param  contentCount  The estimated number of entries in the result set.
386   *                       For the first request in a series of searches with
387   *                       the VLV control, it should be zero.  For subsequent
388   *                       searches in the VLV sequence, it should be the
389   *                       content count included in the response control from
390   *                       the previous search.
391   * @param  contextID     The context ID that may be used to help the server
392   *                       continue in the same result set for subsequent
393   *                       searches.  For the first request in a series of
394   *                       searches with the VLV control, it should be
395   *                       {@code null}.  For subsequent searches in the VLV
396   *                       sequence, it should be the (possibly {@code null})
397   *                       context ID included in the response control from the
398   *                       previous search.
399   * @param  isCritical    Indicates whether this control should be marked
400   *                       critical.
401   */
402  public VirtualListViewRequestControl(final int targetOffset,
403              final int beforeCount, final int afterCount,
404              final int contentCount,
405              @Nullable final ASN1OctetString contextID,
406              final boolean isCritical)
407  {
408    super(VIRTUAL_LIST_VIEW_REQUEST_OID, isCritical,
409          encodeValue(targetOffset, beforeCount, afterCount, contentCount,
410                      contextID));
411
412    this.targetOffset = targetOffset;
413    this.beforeCount  = beforeCount;
414    this.afterCount   = afterCount;
415    this.contentCount = contentCount;
416    this.contextID    = contextID;
417
418    assertionValue = null;
419  }
420
421
422
423  /**
424   * Creates a new virtual list view request control that will identify the
425   * beginning of the result set by an assertion value.  It will be marked
426   * critical.
427   *
428   * @param  assertionValue  The assertion value that will be used to identify
429   *                         the start of the result set.  The target entry will
430   *                         be the first entry with a value for the primary
431   *                         sort attribute that is greater than or equal to
432   *                         this assertion value.  It must not be {@code null}.
433   * @param  beforeCount     The maximum number of entries that should be
434   *                         returned before the first entry with a value
435   *                         greater than or equal to the provided assertion
436   *                         value.
437   * @param  afterCount      The maximum number of entries that should be
438   *                         returned after the first entry with a value
439   *                         greater than or equal to the provided assertion
440   *                         value.
441   * @param  contextID       The context ID that may be used to help the server
442   *                         continue in the same result set for subsequent
443   *                         searches.  For the first request in a series of
444   *                         searches with the VLV control, it should be
445   *                         {@code null}.  For subsequent searches in the VLV
446   *                         sequence, it should be the (possibly {@code null})
447   *                         context ID included in the response control from
448   *                         the previous search.
449   * @param  isCritical    Indicates whether this control should be marked
450   *                       critical.
451   */
452  public VirtualListViewRequestControl(@NotNull final String assertionValue,
453              final int beforeCount, final int afterCount,
454              @Nullable final ASN1OctetString contextID,
455              final boolean isCritical)
456  {
457    this(new ASN1OctetString(assertionValue), beforeCount, afterCount,
458                             contextID, isCritical);
459  }
460
461
462
463  /**
464   * Creates a new virtual list view request control that will identify the
465   * beginning of the result set by an assertion value.  It will be marked
466   * critical.
467   *
468   * @param  assertionValue  The assertion value that will be used to identify
469   *                         the start of the result set.  The target entry will
470   *                         be the first entry with a value for the primary
471   *                         sort attribute that is greater than or equal to
472   *                         this assertion value.  It must not be {@code null}.
473   * @param  beforeCount     The maximum number of entries that should be
474   *                         returned before the first entry with a value
475   *                         greater than or equal to the provided assertion
476   *                         value.
477   * @param  afterCount      The maximum number of entries that should be
478   *                         returned after the first entry with a value
479   *                         greater than or equal to the provided assertion
480   *                         value.
481   * @param  contextID       The context ID that may be used to help the server
482   *                         continue in the same result set for subsequent
483   *                         searches.  For the first request in a series of
484   *                         searches with the VLV control, it should be
485   *                         {@code null}.  For subsequent searches in the VLV
486   *                         sequence, it should be the (possibly {@code null})
487   *                         context ID included in the response control from
488   *                         the previous search.
489   * @param  isCritical    Indicates whether this control should be marked
490   *                       critical.
491   */
492  public VirtualListViewRequestControl(@NotNull final byte[] assertionValue,
493              final int beforeCount, final int afterCount,
494              @Nullable final ASN1OctetString contextID,
495              final boolean isCritical)
496  {
497    this(new ASN1OctetString(assertionValue), beforeCount, afterCount,
498                             contextID, isCritical);
499  }
500
501
502
503  /**
504   * Creates a new virtual list view request control that will identify the
505   * beginning of the result set by an assertion value.  It will be marked
506   * critical.
507   *
508   * @param  assertionValue  The assertion value that will be used to identify
509   *                         the start of the result set.  The target entry will
510   *                         be the first entry with a value for the primary
511   *                         sort attribute that is greater than or equal to
512   *                         this assertion value.  It must not be {@code null}.
513   * @param  beforeCount     The maximum number of entries that should be
514   *                         returned before the first entry with a value
515   *                         greater than or equal to the provided assertion
516   *                         value.
517   * @param  afterCount      The maximum number of entries that should be
518   *                         returned after the first entry with a value
519   *                         greater than or equal to the provided assertion
520   *                         value.
521   * @param  contextID       The context ID that may be used to help the server
522   *                         continue in the same result set for subsequent
523   *                         searches.  For the first request in a series of
524   *                         searches with the VLV control, it should be
525   *                         {@code null}.  For subsequent searches in the VLV
526   *                         sequence, it should be the (possibly {@code null})
527   *                         context ID included in the response control from
528   *                         the previous search.
529   * @param  isCritical    Indicates whether this control should be marked
530   *                       critical.
531   */
532  public VirtualListViewRequestControl(
533              @NotNull final ASN1OctetString assertionValue,
534              final int beforeCount, final int afterCount,
535              @Nullable final ASN1OctetString contextID,
536              final boolean isCritical)
537  {
538    super(VIRTUAL_LIST_VIEW_REQUEST_OID, isCritical,
539          encodeValue(assertionValue, beforeCount, afterCount, contextID));
540
541    this.assertionValue = assertionValue;
542    this.beforeCount    = beforeCount;
543    this.afterCount     = afterCount;
544    this.contextID      = contextID;
545
546    targetOffset = -1;
547    contentCount = -1;
548  }
549
550
551
552  /**
553   * Creates a new virtual list view request control which is decoded from the
554   * provided generic control.
555   *
556   * @param  control  The generic control to be decoded as a virtual list view
557   *                  request control.
558   *
559   * @throws  LDAPException  If the provided control cannot be decoded as a
560   *                         virtual list view request control.
561   */
562  public VirtualListViewRequestControl(@NotNull final Control control)
563         throws LDAPException
564  {
565    super(control);
566
567    final ASN1OctetString value = control.getValue();
568    if (value == null)
569    {
570      throw new LDAPException(ResultCode.DECODING_ERROR,
571                              ERR_VLV_REQUEST_NO_VALUE.get());
572    }
573
574    try
575    {
576      final ASN1Element valueElement = ASN1Element.decode(value.getValue());
577      final ASN1Element[] elements =
578           ASN1Sequence.decodeAsSequence(valueElement).elements();
579
580      beforeCount = ASN1Integer.decodeAsInteger(elements[0]).intValue();
581      afterCount  = ASN1Integer.decodeAsInteger(elements[1]).intValue();
582
583      switch (elements[2].getType())
584      {
585        case TARGET_TYPE_OFFSET:
586          assertionValue = null;
587          final ASN1Element[] offsetElements =
588               ASN1Sequence.decodeAsSequence(elements[2]).elements();
589          targetOffset =
590               ASN1Integer.decodeAsInteger(offsetElements[0]).intValue();
591          contentCount =
592               ASN1Integer.decodeAsInteger(offsetElements[1]).intValue();
593          break;
594
595        case TARGET_TYPE_GREATER_OR_EQUAL:
596          assertionValue = ASN1OctetString.decodeAsOctetString(elements[2]);
597          targetOffset   = -1;
598          contentCount   = -1;
599          break;
600
601        default:
602          throw new LDAPException(ResultCode.DECODING_ERROR,
603               ERR_VLV_REQUEST_INVALID_ELEMENT_TYPE.get(
604                    StaticUtils.toHex(elements[2].getType())));
605      }
606
607      if (elements.length == 4)
608      {
609        contextID = ASN1OctetString.decodeAsOctetString(elements[3]);
610      }
611      else
612      {
613        contextID = null;
614      }
615    }
616    catch (final LDAPException le)
617    {
618      Debug.debugException(le);
619      throw le;
620    }
621    catch (final Exception e)
622    {
623      Debug.debugException(e);
624      throw new LDAPException(ResultCode.DECODING_ERROR,
625           ERR_VLV_REQUEST_CANNOT_DECODE.get(e), e);
626    }
627  }
628
629
630
631  /**
632   * Encodes the provided information into an octet string that can be used as
633   * the value for this control.
634   *
635   * @param  targetOffset  The position of the entry that should be used as the
636   *                       start of the result set.
637   * @param  beforeCount   The maximum number of entries that should be returned
638   *                       before the entry with the specified target offset.
639   * @param  afterCount    The maximum number of entries that should be returned
640   *                       after the entry with the specified target offset.
641   * @param  contentCount  The estimated number of entries in the result set.
642   *                       For the first request in a series of searches with
643   *                       the VLV control, it should be zero.  For subsequent
644   *                       searches in the VLV sequence, it should be the
645   *                       content count included in the response control from
646   *                       the previous search.
647   * @param  contextID     The context ID that may be used to help the server
648   *                       continue in the same result set for subsequent
649   *                       searches.  For the first request in a series of
650   *                       searches with the VLV control, it should be
651   *                       {@code null}.  For subsequent searches in the VLV
652   *                       sequence, it should be the (possibly {@code null})
653   *                       context ID included in the response control from the
654   *                       previous search.
655   *
656   * @return  An ASN.1 octet string that can be used as the value for this
657   *          control.
658   */
659  @NotNull()
660  private static ASN1OctetString encodeValue(final int targetOffset,
661                      final int beforeCount, final int afterCount,
662                      final int contentCount,
663                      @Nullable final ASN1OctetString contextID)
664  {
665    final ASN1Element[] targetElements =
666    {
667      new ASN1Integer(targetOffset),
668      new ASN1Integer(contentCount)
669    };
670
671    final ASN1Element[] vlvElements;
672    if (contextID == null)
673    {
674      vlvElements = new ASN1Element[]
675      {
676        new ASN1Integer(beforeCount),
677        new ASN1Integer(afterCount),
678        new ASN1Sequence(TARGET_TYPE_OFFSET, targetElements)
679      };
680    }
681    else
682    {
683      vlvElements = new ASN1Element[]
684      {
685        new ASN1Integer(beforeCount),
686        new ASN1Integer(afterCount),
687        new ASN1Sequence(TARGET_TYPE_OFFSET, targetElements),
688        contextID
689      };
690    }
691
692    return new ASN1OctetString(new ASN1Sequence(vlvElements).encode());
693  }
694
695
696
697  /**
698   * Encodes the provided information into an octet string that can be used as
699   * the value for this control.
700   *
701   * @param  assertionValue  The assertion value that will be used to identify
702   *                         the start of the result set.  The target entry will
703   *                         be the first entry with a value for the primary
704   *                         sort attribute that is greater than or equal to
705   *                         this assertion value.
706   * @param  beforeCount     The maximum number of entries that should be
707   *                         returned before the first entry with a value
708   *                         greater than or equal to the provided assertion
709   *                         value.
710   * @param  afterCount      The maximum number of entries that should be
711   *                         returned after the first entry with a value
712   *                         greater than or equal to the provided assertion
713   *                         value.
714   * @param  contextID       The context ID that may be used to help the server
715   *                         continue in the same result set for subsequent
716   *                         searches.  For the first request in a series of
717   *                         searches with the VLV control, it should be
718   *                         {@code null}.  For subsequent searches in the VLV
719   *                         sequence, it should be the (possibly {@code null})
720   *                         context ID included in the response control from
721   *                         the previous search.
722   *
723   * @return  An ASN.1 octet string that can be used as the value for this
724   *          control.
725   */
726  @NotNull()
727  private static ASN1OctetString encodeValue(
728                      @NotNull final ASN1OctetString assertionValue,
729                      final int beforeCount,
730                      final int afterCount,
731                      @Nullable final ASN1OctetString contextID)
732  {
733    Validator.ensureNotNull(assertionValue);
734
735    final ASN1Element[] vlvElements;
736    if (contextID == null)
737    {
738      vlvElements = new ASN1Element[]
739      {
740        new ASN1Integer(beforeCount),
741        new ASN1Integer(afterCount),
742        new ASN1OctetString(TARGET_TYPE_GREATER_OR_EQUAL,
743                            assertionValue.getValue())
744      };
745    }
746    else
747    {
748      vlvElements = new ASN1Element[]
749      {
750        new ASN1Integer(beforeCount),
751        new ASN1Integer(afterCount),
752        new ASN1OctetString(TARGET_TYPE_GREATER_OR_EQUAL,
753                            assertionValue.getValue()),
754        contextID
755      };
756    }
757
758    return new ASN1OctetString(new ASN1Sequence(vlvElements).encode());
759  }
760
761
762
763  /**
764   * Retrieves the target offset position for this virtual list view request
765   * control, if applicable.
766   *
767   * @return  The target offset position for this virtual list view request
768   *          control, or -1 if the target is specified by an assertion value.
769   */
770  public int getTargetOffset()
771  {
772    return targetOffset;
773  }
774
775
776
777  /**
778   * Retrieves the string representation of the assertion value for this virtual
779   * list view request control, if applicable.
780   *
781   * @return  The string representation of the assertion value for this virtual
782   *          list view request control, or {@code null} if the target is
783   *          specified by offset.
784   */
785  @Nullable()
786  public String getAssertionValueString()
787  {
788    if (assertionValue == null)
789    {
790      return null;
791    }
792    else
793    {
794      return assertionValue.stringValue();
795    }
796  }
797
798
799
800  /**
801   * Retrieves the byte array representation of the assertion value for this
802   * virtual list view request control, if applicable.
803   *
804   * @return  The byte array representation of the assertion value for this
805   *          virtual list view request control, or {@code null} if the target
806   *          is specified by offset.
807   */
808  @Nullable()
809  public byte[] getAssertionValueBytes()
810  {
811    if (assertionValue == null)
812    {
813      return null;
814    }
815    else
816    {
817      return assertionValue.getValue();
818    }
819  }
820
821
822
823  /**
824   * Retrieves the assertion value for this virtual list view request control,
825   * if applicable.
826   *
827   * @return  The assertion value for this virtual list view request control, or
828   *          {@code null} if the target is specified by offset.
829   */
830  @Nullable()
831  public ASN1OctetString getAssertionValue()
832  {
833    return assertionValue;
834  }
835
836
837
838  /**
839   * Retrieves the number of entries that should be retrieved before the target
840   * entry.
841   *
842   * @return  The number of entries that should be retrieved before the target
843   *          entry.
844   */
845  public int getBeforeCount()
846  {
847    return beforeCount;
848  }
849
850
851
852  /**
853   * Retrieves the number of entries that should be retrieved after the target
854   * entry.
855   *
856   * @return  The number of entries that should be retrieved after the target
857   *          entry.
858   */
859  public int getAfterCount()
860  {
861    return afterCount;
862  }
863
864
865
866  /**
867   * Retrieves the estimated number of entries in the result set, if applicable.
868   *
869   * @return  The estimated number of entries in the result set, zero if it
870   *          is not known (for the first search in a sequence where the
871   *          target is specified by offset), or -1 if the target is specified
872   *          by an assertion value.
873   */
874  public int getContentCount()
875  {
876    return contentCount;
877  }
878
879
880
881  /**
882   * Retrieves the context ID for this virtual list view request control, if
883   * available.
884   *
885   * @return  The context ID for this virtual list view request control, or
886   *          {@code null} if there is none.
887   */
888  @Nullable()
889  public ASN1OctetString getContextID()
890  {
891    return contextID;
892  }
893
894
895
896  /**
897   * {@inheritDoc}
898   */
899  @Override()
900  @NotNull()
901  public String getControlName()
902  {
903    return INFO_CONTROL_NAME_VLV_REQUEST.get();
904  }
905
906
907
908  /**
909   * {@inheritDoc}
910   */
911  @Override()
912  public void toString(@NotNull final StringBuilder buffer)
913  {
914    buffer.append("VirtualListViewRequestControl(beforeCount=");
915    buffer.append(beforeCount);
916    buffer.append(", afterCount=");
917    buffer.append(afterCount);
918
919    if (assertionValue == null)
920    {
921      buffer.append(", targetOffset=");
922      buffer.append(targetOffset);
923      buffer.append(", contentCount=");
924      buffer.append(contentCount);
925    }
926    else
927    {
928      buffer.append(", assertionValue='");
929      buffer.append(assertionValue.stringValue());
930      buffer.append('\'');
931    }
932
933    buffer.append(", isCritical=");
934    buffer.append(isCritical());
935    buffer.append(')');
936  }
937}