001/*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 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 */
021package com.unboundid.ldap.sdk.controls;
022
023
024
025import com.unboundid.asn1.ASN1Element;
026import com.unboundid.asn1.ASN1Enumerated;
027import com.unboundid.asn1.ASN1Exception;
028import com.unboundid.asn1.ASN1Integer;
029import com.unboundid.asn1.ASN1OctetString;
030import com.unboundid.asn1.ASN1Sequence;
031import com.unboundid.ldap.sdk.Control;
032import com.unboundid.ldap.sdk.DecodeableControl;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.ResultCode;
035import com.unboundid.ldap.sdk.SearchResult;
036import com.unboundid.util.NotMutable;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
041import 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)
066public 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}