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.ASN1OctetString;
029import com.unboundid.asn1.ASN1Sequence;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.DecodeableControl;
032import com.unboundid.ldap.sdk.LDAPException;
033import com.unboundid.ldap.sdk.ResultCode;
034import com.unboundid.ldap.sdk.SearchResult;
035import com.unboundid.util.NotMutable;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038
039import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
040import static com.unboundid.util.Debug.*;
041
042
043
044/**
045 * This class provides an implementation of the server-side sort response
046 * control, as defined in
047 * <A HREF="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</A>.  It may be used
048 * to provide information about the result of server-side sort processing.  If
049 * the corresponding search request included the
050 * {@link ServerSideSortRequestControl}, then the search result done message
051 * may include this response control to provide information about the state of
052 * the sorting.
053 */
054@NotMutable()
055@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
056public final class ServerSideSortResponseControl
057       extends Control
058       implements DecodeableControl
059{
060  /**
061   * The OID (1.2.840.113556.1.4.474) for the server-side sort response control.
062   */
063  public static final String SERVER_SIDE_SORT_RESPONSE_OID =
064       "1.2.840.113556.1.4.474";
065
066
067
068  /**
069   * The BER type to use for the element that holds the attribute type.
070   */
071  private static final byte TYPE_ATTRIBUTE_TYPE = (byte) 0x80;
072
073
074
075  /**
076   * The serial version UID for this serializable class.
077   */
078  private static final long serialVersionUID = -8707533262822875822L;
079
080
081
082  // The result code for this server-side sort response control.
083  private final ResultCode resultCode;
084
085  // The name of the attribute associated with this result, if available.
086  private final String attributeName;
087
088
089
090  /**
091   * Creates a new empty control instance that is intended to be used only for
092   * decoding controls via the {@code DecodeableControl} interface.
093   */
094  ServerSideSortResponseControl()
095  {
096    resultCode    = null;
097    attributeName = null;
098  }
099
100
101
102  /**
103   * Creates a new server-side sort response control with the provided
104   * information.
105   *
106   * @param  resultCode     The result code for this server-side sort response.
107   * @param  attributeName  The name of the attribute associated with this
108   *                        result.  It may be {@code null} if there is no
109   *                        associated attribute name.
110   * @param  isCritical     Indicates whether this control should be marked
111   *                        critical.
112   */
113  public ServerSideSortResponseControl(final ResultCode resultCode,
114                                       final String attributeName,
115                                       final boolean isCritical)
116  {
117    super(SERVER_SIDE_SORT_RESPONSE_OID, isCritical,
118          encodeValue(resultCode, attributeName));
119
120    this.resultCode    = resultCode;
121    this.attributeName = attributeName;
122  }
123
124
125
126  /**
127   * Creates a new server-side sort response control from the information
128   * contained in the provided control.
129   *
130   * @param  oid         The OID for the control.
131   * @param  isCritical  Indicates whether the control should be marked
132   *                     critical.
133   * @param  value       The encoded value for the control.  This may be
134   *                     {@code null} if no value was provided.
135   *
136   * @throws  LDAPException  If a problem occurs while attempting to decode the
137   *                         provided control as a server-side sort response
138   *                         control.
139   */
140  public ServerSideSortResponseControl(final String oid,
141                                       final boolean isCritical,
142                                       final ASN1OctetString value)
143         throws LDAPException
144  {
145    super(oid, isCritical, value);
146
147    if (value == null)
148    {
149      throw new LDAPException(ResultCode.DECODING_ERROR,
150                              ERR_SORT_RESPONSE_NO_VALUE.get());
151    }
152
153    final ASN1Sequence valueSequence;
154    try
155    {
156      final ASN1Element valueElement =
157           ASN1Element.decode(value.getValue());
158      valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
159    }
160    catch (final ASN1Exception ae)
161    {
162      debugException(ae);
163      throw new LDAPException(ResultCode.DECODING_ERROR,
164                              ERR_SORT_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
165    }
166
167    final ASN1Element[] valueElements = valueSequence.elements();
168    if ((valueElements.length < 1) || (valueElements.length > 2))
169    {
170      throw new LDAPException(ResultCode.DECODING_ERROR,
171                              ERR_SORT_RESPONSE_INVALID_ELEMENT_COUNT.get(
172                                   valueElements.length));
173    }
174
175    try
176    {
177      final int rc =
178           ASN1Enumerated.decodeAsEnumerated(valueElements[0]).intValue();
179      resultCode = ResultCode.valueOf(rc);
180    }
181    catch (final ASN1Exception ae)
182    {
183      debugException(ae);
184      throw new LDAPException(ResultCode.DECODING_ERROR,
185                              ERR_SORT_RESPONSE_FIRST_NOT_ENUM.get(ae), ae);
186    }
187
188    if (valueElements.length == 2)
189    {
190      attributeName =
191           ASN1OctetString.decodeAsOctetString(valueElements[1]).stringValue();
192    }
193    else
194    {
195      attributeName = null;
196    }
197  }
198
199
200
201  /**
202   * {@inheritDoc}
203   */
204  public ServerSideSortResponseControl
205              decodeControl(final String oid, final boolean isCritical,
206                            final ASN1OctetString value)
207         throws LDAPException
208  {
209    return new ServerSideSortResponseControl(oid, isCritical, value);
210  }
211
212
213
214  /**
215   * Extracts a server-side sort response control from the provided result.
216   *
217   * @param  result  The result from which to retrieve the server-side sort
218   *                 response control.
219   *
220   * @return  The server-side sort response control contained in the provided
221   *          result, or {@code null} if the result did not contain a
222   *          server-side sort response control.
223   *
224   * @throws  LDAPException  If a problem is encountered while attempting to
225   *                         decode the server-side sort response control
226   *                         contained in the provided result.
227   */
228  public static ServerSideSortResponseControl get(final SearchResult result)
229         throws LDAPException
230  {
231    final Control c = result.getResponseControl(SERVER_SIDE_SORT_RESPONSE_OID);
232    if (c == null)
233    {
234      return null;
235    }
236
237    if (c instanceof ServerSideSortResponseControl)
238    {
239      return (ServerSideSortResponseControl) c;
240    }
241    else
242    {
243      return new ServerSideSortResponseControl(c.getOID(), c.isCritical(),
244           c.getValue());
245    }
246  }
247
248
249
250  /**
251   * Encodes the provided information into an octet string that can be used as
252   * the value for this control.
253   *
254   * @param  resultCode     The result code for this server-side sort response
255   *                        control.
256   * @param  attributeName  The attribute name to include in the control, or
257   *                        {@code null} if it should not be provided.
258   *
259   * @return  An ASN.1 octet string that can be used as the value for this
260   *          control.
261   */
262  private static ASN1OctetString encodeValue(final ResultCode resultCode,
263                                             final String attributeName)
264  {
265    final ASN1Element[] valueElements;
266    if (attributeName == null)
267    {
268      valueElements = new ASN1Element[]
269      {
270        new ASN1Enumerated(resultCode.intValue())
271      };
272    }
273    else
274    {
275      valueElements = new ASN1Element[]
276      {
277        new ASN1Enumerated(resultCode.intValue()),
278        new ASN1OctetString(TYPE_ATTRIBUTE_TYPE, attributeName)
279      };
280    }
281
282    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
283  }
284
285
286
287  /**
288   * Retrieves the result code for this server-side sort response control.
289   *
290   * @return  The result code for this server-side sort response control.
291   */
292  public ResultCode getResultCode()
293  {
294    return resultCode;
295  }
296
297
298
299  /**
300   * Retrieves the attribute name for this server-side sort response control, if
301   * available.
302   *
303   * @return  The attribute name for this server-side sort response control, or
304   *          {@code null} if none was provided.
305   */
306  public String getAttributeName()
307  {
308    return attributeName;
309  }
310
311
312
313  /**
314   * {@inheritDoc}
315   */
316  @Override()
317  public String getControlName()
318  {
319    return INFO_CONTROL_NAME_SORT_RESPONSE.get();
320  }
321
322
323
324  /**
325   * {@inheritDoc}
326   */
327  @Override()
328  public void toString(final StringBuilder buffer)
329  {
330    buffer.append("ServerSideSortResponseControl(resultCode=");
331    buffer.append(resultCode);
332
333    if (attributeName != null)
334    {
335      buffer.append(", attributeName='");
336      buffer.append(attributeName);
337      buffer.append('\'');
338    }
339
340    buffer.append(')');
341  }
342}