001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2018 Ping Identity Corporation
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   */
111  public ServerSideSortResponseControl(final ResultCode resultCode,
112                                       final String attributeName)
113  {
114    this(resultCode, attributeName, false);
115  }
116
117
118
119  /**
120   * Creates a new server-side sort response control with the provided
121   * information.
122   *
123   * @param  resultCode     The result code for this server-side sort response.
124   * @param  attributeName  The name of the attribute associated with this
125   *                        result.  It may be {@code null} if there is no
126   *                        associated attribute name.
127   * @param  isCritical     Indicates whether this control should be marked
128   *                        critical.  Response controls should generally not be
129   *                        critical.
130   */
131  public ServerSideSortResponseControl(final ResultCode resultCode,
132                                       final String attributeName,
133                                       final boolean isCritical)
134  {
135    super(SERVER_SIDE_SORT_RESPONSE_OID, isCritical,
136          encodeValue(resultCode, attributeName));
137
138    this.resultCode    = resultCode;
139    this.attributeName = attributeName;
140  }
141
142
143
144  /**
145   * Creates a new server-side sort response control from the information
146   * contained in the provided control.
147   *
148   * @param  oid         The OID for the control.
149   * @param  isCritical  Indicates whether the control should be marked
150   *                     critical.
151   * @param  value       The encoded value for the control.  This may be
152   *                     {@code null} if no value was provided.
153   *
154   * @throws  LDAPException  If a problem occurs while attempting to decode the
155   *                         provided control as a server-side sort response
156   *                         control.
157   */
158  public ServerSideSortResponseControl(final String oid,
159                                       final boolean isCritical,
160                                       final ASN1OctetString value)
161         throws LDAPException
162  {
163    super(oid, isCritical, value);
164
165    if (value == null)
166    {
167      throw new LDAPException(ResultCode.DECODING_ERROR,
168                              ERR_SORT_RESPONSE_NO_VALUE.get());
169    }
170
171    final ASN1Sequence valueSequence;
172    try
173    {
174      final ASN1Element valueElement =
175           ASN1Element.decode(value.getValue());
176      valueSequence = ASN1Sequence.decodeAsSequence(valueElement);
177    }
178    catch (final ASN1Exception ae)
179    {
180      debugException(ae);
181      throw new LDAPException(ResultCode.DECODING_ERROR,
182                              ERR_SORT_RESPONSE_VALUE_NOT_SEQUENCE.get(ae), ae);
183    }
184
185    final ASN1Element[] valueElements = valueSequence.elements();
186    if ((valueElements.length < 1) || (valueElements.length > 2))
187    {
188      throw new LDAPException(ResultCode.DECODING_ERROR,
189                              ERR_SORT_RESPONSE_INVALID_ELEMENT_COUNT.get(
190                                   valueElements.length));
191    }
192
193    try
194    {
195      final int rc =
196           ASN1Enumerated.decodeAsEnumerated(valueElements[0]).intValue();
197      resultCode = ResultCode.valueOf(rc);
198    }
199    catch (final ASN1Exception ae)
200    {
201      debugException(ae);
202      throw new LDAPException(ResultCode.DECODING_ERROR,
203                              ERR_SORT_RESPONSE_FIRST_NOT_ENUM.get(ae), ae);
204    }
205
206    if (valueElements.length == 2)
207    {
208      attributeName =
209           ASN1OctetString.decodeAsOctetString(valueElements[1]).stringValue();
210    }
211    else
212    {
213      attributeName = null;
214    }
215  }
216
217
218
219  /**
220   * {@inheritDoc}
221   */
222  @Override()
223  public ServerSideSortResponseControl
224              decodeControl(final String oid, final boolean isCritical,
225                            final ASN1OctetString value)
226         throws LDAPException
227  {
228    return new ServerSideSortResponseControl(oid, isCritical, value);
229  }
230
231
232
233  /**
234   * Extracts a server-side sort response control from the provided result.
235   *
236   * @param  result  The result from which to retrieve the server-side sort
237   *                 response control.
238   *
239   * @return  The server-side sort response control contained in the provided
240   *          result, or {@code null} if the result did not contain a
241   *          server-side sort response control.
242   *
243   * @throws  LDAPException  If a problem is encountered while attempting to
244   *                         decode the server-side sort response control
245   *                         contained in the provided result.
246   */
247  public static ServerSideSortResponseControl get(final SearchResult result)
248         throws LDAPException
249  {
250    final Control c = result.getResponseControl(SERVER_SIDE_SORT_RESPONSE_OID);
251    if (c == null)
252    {
253      return null;
254    }
255
256    if (c instanceof ServerSideSortResponseControl)
257    {
258      return (ServerSideSortResponseControl) c;
259    }
260    else
261    {
262      return new ServerSideSortResponseControl(c.getOID(), c.isCritical(),
263           c.getValue());
264    }
265  }
266
267
268
269  /**
270   * Encodes the provided information into an octet string that can be used as
271   * the value for this control.
272   *
273   * @param  resultCode     The result code for this server-side sort response
274   *                        control.
275   * @param  attributeName  The attribute name to include in the control, or
276   *                        {@code null} if it should not be provided.
277   *
278   * @return  An ASN.1 octet string that can be used as the value for this
279   *          control.
280   */
281  private static ASN1OctetString encodeValue(final ResultCode resultCode,
282                                             final String attributeName)
283  {
284    final ASN1Element[] valueElements;
285    if (attributeName == null)
286    {
287      valueElements = new ASN1Element[]
288      {
289        new ASN1Enumerated(resultCode.intValue())
290      };
291    }
292    else
293    {
294      valueElements = new ASN1Element[]
295      {
296        new ASN1Enumerated(resultCode.intValue()),
297        new ASN1OctetString(TYPE_ATTRIBUTE_TYPE, attributeName)
298      };
299    }
300
301    return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
302  }
303
304
305
306  /**
307   * Retrieves the result code for this server-side sort response control.
308   *
309   * @return  The result code for this server-side sort response control.
310   */
311  public ResultCode getResultCode()
312  {
313    return resultCode;
314  }
315
316
317
318  /**
319   * Retrieves the attribute name for this server-side sort response control, if
320   * available.
321   *
322   * @return  The attribute name for this server-side sort response control, or
323   *          {@code null} if none was provided.
324   */
325  public String getAttributeName()
326  {
327    return attributeName;
328  }
329
330
331
332  /**
333   * {@inheritDoc}
334   */
335  @Override()
336  public String getControlName()
337  {
338    return INFO_CONTROL_NAME_SORT_RESPONSE.get();
339  }
340
341
342
343  /**
344   * {@inheritDoc}
345   */
346  @Override()
347  public void toString(final StringBuilder buffer)
348  {
349    buffer.append("ServerSideSortResponseControl(resultCode=");
350    buffer.append(resultCode);
351
352    if (attributeName != null)
353    {
354      buffer.append(", attributeName='");
355      buffer.append(attributeName);
356      buffer.append('\'');
357    }
358
359    buffer.append(')');
360  }
361}