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.ASN1OctetString;
029    import com.unboundid.asn1.ASN1Sequence;
030    import com.unboundid.ldap.sdk.Control;
031    import com.unboundid.ldap.sdk.DecodeableControl;
032    import com.unboundid.ldap.sdk.LDAPException;
033    import com.unboundid.ldap.sdk.ResultCode;
034    import com.unboundid.ldap.sdk.SearchResult;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
040    import 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)
056    public 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    }