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