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.ASN1Enumerated;
027    import com.unboundid.asn1.ASN1Exception;
028    import com.unboundid.asn1.ASN1Integer;
029    import com.unboundid.asn1.ASN1OctetString;
030    import com.unboundid.asn1.ASN1Sequence;
031    import com.unboundid.ldap.sdk.Control;
032    import com.unboundid.ldap.sdk.DecodeableControl;
033    import com.unboundid.ldap.sdk.LDAPException;
034    import com.unboundid.ldap.sdk.ResultCode;
035    import com.unboundid.ldap.sdk.SearchResult;
036    import com.unboundid.util.NotMutable;
037    import com.unboundid.util.ThreadSafety;
038    import com.unboundid.util.ThreadSafetyLevel;
039    
040    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
041    import static com.unboundid.util.Debug.*;
042    
043    
044    
045    /**
046     * This class provides an implementation of the virtual list view (VLV) response
047     * control, as defined in draft-ietf-ldapext-ldapv3-vlv.  It may be used to
048     * provide information about the result of virtual list view processing for a
049     * search containing the {@link VirtualListViewRequestControl}.
050     * <BR><BR>
051     * The virtual list view response control may include the following elements:
052     * <UL>
053     *   <LI>{@code resultCode} -- A result code that indicates the result of the
054     *       virtual list view processing.  It may be the same as or different from
055     *       the result code contained in the search result done message.</LI>
056     *   <LI>{@code targetPosition} -- The offset of the target entry specified by
057     *       the client in the result set.</LI>
058     *   <LI>{@code contentCount} -- The estimated total number of entries in the
059     *       entire result set.</LI>
060     *   <LI>{@code contextID} -- An optional cookie that the client should include
061     *       in the next request as part of the virtual list view sequence.</LI>
062     * </UL>
063     */
064    @NotMutable()
065    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
066    public final class VirtualListViewResponseControl
067           extends Control
068           implements DecodeableControl
069    {
070      /**
071       * The OID (2.16.840.1.113730.3.4.10) for the virtual list view response
072       * control.
073       */
074      public static final String VIRTUAL_LIST_VIEW_RESPONSE_OID =
075           "2.16.840.1.113730.3.4.10";
076    
077    
078    
079      /**
080       * The serial version UID for this serializable class.
081       */
082      private static final long serialVersionUID = -534656674756287217L;
083    
084    
085    
086      // The context ID for this VLV response control, if available.
087      private final ASN1OctetString contextID;
088    
089      // The estimated total number of entries in the result set.
090      private final int contentCount;
091    
092      // The result code for this VLV response control.
093      private final ResultCode resultCode;
094    
095      // The offset of the target entry for this VLV response control.
096      private final int targetPosition;
097    
098    
099    
100      /**
101       * Creates a new empty control instance that is intended to be used only for
102       * decoding controls via the {@code DecodeableControl} interface.
103       */
104      VirtualListViewResponseControl()
105      {
106        targetPosition = -1;
107        contentCount   = -1;
108        resultCode     = null;
109        contextID      = null;
110      }
111    
112    
113    
114      /**
115       * Creates a new virtual list view response control with the provided
116       * information.  It will not be marked critical.
117       *
118       * @param  targetPosition  The offset of the target entry for this VLV
119       *                         response control.
120       * @param  contentCount    The estimated total number of entries in the
121       *                         result set.
122       * @param  resultCode      The result code for this VLV response control.
123       * @param  contextID       The context ID for this VLV response control.  It
124       *                         may be {@code null} if no context ID is available.
125       */
126      public VirtualListViewResponseControl(final int targetPosition,
127                  final int contentCount, final ResultCode resultCode,
128                  final ASN1OctetString contextID)
129      {
130        super(VIRTUAL_LIST_VIEW_RESPONSE_OID, false,
131              encodeValue(targetPosition, contentCount, resultCode, contextID));
132    
133        this.targetPosition = targetPosition;
134        this.contentCount   = contentCount;
135        this.resultCode     = resultCode;
136        this.contextID      = contextID;
137      }
138    
139    
140    
141      /**
142       * Creates a new virtual list view response control from the information
143       * contained in the provided control.
144       *
145       * @param  oid         The OID for the control.
146       * @param  isCritical  Indicates whether the control should be marked
147       *                     critical.
148       * @param  value       The encoded value for the control.  This may be
149       *                     {@code null} if no value was provided.
150       *
151       * @throws  LDAPException  If a problem occurs while attempting to decode the
152       *                         provided control as a virtual list view response
153       *                         control.
154       */
155      public VirtualListViewResponseControl(final String oid,
156                                            final boolean isCritical,
157                                            final ASN1OctetString value)
158             throws LDAPException
159      {
160        super(oid, isCritical, value);
161    
162        if (value == null)
163        {
164          throw new LDAPException(ResultCode.DECODING_ERROR,
165                                  ERR_VLV_RESPONSE_NO_VALUE.get());
166        }
167    
168        final ASN1Sequence valueSequence;
169        try
170        {
171          final ASN1Element valueElement =
172               ASN1Element.decode(value.getValue());
173          valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
174        }
175        catch (final ASN1Exception ae)
176        {
177          debugException(ae);
178          throw new LDAPException(ResultCode.DECODING_ERROR,
179                                  ERR_VLV_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
180        }
181    
182        final ASN1Element[] valueElements = valueSequence.elements();
183        if ((valueElements.length < 3) || (valueElements.length > 4))
184        {
185          throw new LDAPException(ResultCode.DECODING_ERROR,
186                                  ERR_VLV_RESPONSE_INVALID_ELEMENT_COUNT.get(
187                                       valueElements.length));
188        }
189    
190        try
191        {
192          targetPosition = ASN1Integer.decodeAsInteger(valueElements[0]).intValue();
193        }
194        catch (final ASN1Exception ae)
195        {
196          debugException(ae);
197          throw new LDAPException(ResultCode.DECODING_ERROR,
198                                  ERR_VLV_RESPONSE_FIRST_NOT_INTEGER.get(ae), ae);
199        }
200    
201        try
202        {
203          contentCount = ASN1Integer.decodeAsInteger(valueElements[1]).intValue();
204        }
205        catch (final ASN1Exception ae)
206        {
207          debugException(ae);
208          throw new LDAPException(ResultCode.DECODING_ERROR,
209                                  ERR_VLV_RESPONSE_SECOND_NOT_INTEGER.get(ae), ae);
210        }
211    
212        try
213        {
214          final int rc =
215               ASN1Enumerated.decodeAsEnumerated(valueElements[2]).intValue();
216          resultCode = ResultCode.valueOf(rc);
217        }
218        catch (final ASN1Exception ae)
219        {
220          debugException(ae);
221          throw new LDAPException(ResultCode.DECODING_ERROR,
222                                  ERR_VLV_RESPONSE_THIRD_NOT_ENUM.get(ae), ae);
223        }
224    
225        if (valueElements.length == 4)
226        {
227          contextID = ASN1OctetString.decodeAsOctetString(valueElements[3]);
228        }
229        else
230        {
231          contextID = null;
232        }
233      }
234    
235    
236    
237      /**
238       * {@inheritDoc}
239       */
240      public VirtualListViewResponseControl
241                  decodeControl(final String oid, final boolean isCritical,
242                                final ASN1OctetString value)
243             throws LDAPException
244      {
245        return new VirtualListViewResponseControl(oid, isCritical, value);
246      }
247    
248    
249    
250      /**
251       * Extracts a virtual list view response control from the provided result.
252       *
253       * @param  result  The result from which to retrieve the virtual list view
254       *                 response control.
255       *
256       * @return  The virtual list view response  control contained in the provided
257       *          result, or {@code null} if the result did not contain a virtual
258       *          list view response control.
259       *
260       * @throws  LDAPException  If a problem is encountered while attempting to
261       *                         decode the virtual list view response  control
262       *                         contained in the provided result.
263       */
264      public static VirtualListViewResponseControl get(final SearchResult result)
265             throws LDAPException
266      {
267        final Control c = result.getResponseControl(VIRTUAL_LIST_VIEW_RESPONSE_OID);
268        if (c == null)
269        {
270          return null;
271        }
272    
273        if (c instanceof VirtualListViewResponseControl)
274        {
275          return (VirtualListViewResponseControl) c;
276        }
277        else
278        {
279          return new VirtualListViewResponseControl(c.getOID(), c.isCritical(),
280               c.getValue());
281        }
282      }
283    
284    
285    
286      /**
287       * Encodes the provided information into an octet string that can be used as
288       * the value for this control.
289       *
290       * @param  targetPosition  The offset of the target entry for this VLV
291       *                         response control.
292       * @param  contentCount    The estimated total number of entries in the
293       *                         result set.
294       * @param  resultCode      The result code for this VLV response control.
295       * @param  contextID       The context ID for this VLV response control.  It
296       *                         may be {@code null} if no context ID is available.
297       *
298       * @return  An ASN.1 octet string that can be used as the value for this
299       *          control.
300       */
301      private static ASN1OctetString encodeValue(final int targetPosition,
302                                                 final int contentCount,
303                                                 final ResultCode resultCode,
304                                                 final ASN1OctetString contextID)
305      {
306        final ASN1Element[] vlvElements;
307        if (contextID == null)
308        {
309          vlvElements = new ASN1Element[]
310          {
311            new ASN1Integer(targetPosition),
312            new ASN1Integer(contentCount),
313            new ASN1Enumerated(resultCode.intValue())
314          };
315        }
316        else
317        {
318          vlvElements = new ASN1Element[]
319          {
320            new ASN1Integer(targetPosition),
321            new ASN1Integer(contentCount),
322            new ASN1Enumerated(resultCode.intValue()),
323            contextID
324          };
325        }
326    
327        return new ASN1OctetString(new ASN1Sequence(vlvElements).encode());
328      }
329    
330    
331    
332      /**
333       * Retrieves the offset of the target entry for this virtual list view
334       * response control.
335       *
336       * @return  The offset of the target entry for this virtual list view response
337       *          control.
338       */
339      public int getTargetPosition()
340      {
341        return targetPosition;
342      }
343    
344    
345    
346      /**
347       * Retrieves the estimated total number of entries in the result set.
348       *
349       * @return  The estimated total number of entries in the result set.
350       */
351      public int getContentCount()
352      {
353        return contentCount;
354      }
355    
356    
357    
358      /**
359       * Retrieves the result code for this virtual list view response control.
360       *
361       * @return  The result code for this virtual list view response control.
362       */
363      public ResultCode getResultCode()
364      {
365        return resultCode;
366      }
367    
368    
369    
370      /**
371       * Retrieves the context ID for this virtual list view response control, if
372       * available.
373       *
374       * @return  The context ID for this virtual list view response control, or
375       *          {@code null} if none was provided.
376       */
377      public ASN1OctetString getContextID()
378      {
379        return contextID;
380      }
381    
382    
383    
384      /**
385       * {@inheritDoc}
386       */
387      @Override()
388      public String getControlName()
389      {
390        return INFO_CONTROL_NAME_VLV_RESPONSE.get();
391      }
392    
393    
394    
395      /**
396       * {@inheritDoc}
397       */
398      @Override()
399      public void toString(final StringBuilder buffer)
400      {
401        buffer.append("VirtualListViewResponseControl(targetPosition=");
402        buffer.append(targetPosition);
403        buffer.append(", contentCount=");
404        buffer.append(contentCount);
405        buffer.append(", resultCode=");
406        buffer.append(resultCode);
407        buffer.append(')');
408      }
409    }