001    /*
002     * Copyright 2007-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 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.ASN1OctetString;
027    import com.unboundid.asn1.ASN1Sequence;
028    import com.unboundid.ldap.sdk.Control;
029    import com.unboundid.ldap.sdk.LDAPException;
030    import com.unboundid.ldap.sdk.ResultCode;
031    import com.unboundid.util.NotMutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
036    import static com.unboundid.util.Debug.*;
037    import static com.unboundid.util.Validator.*;
038    
039    
040    
041    /**
042     * This class provides an implementation of the server-side sort request
043     * control, as defined in
044     * <A HREF="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</A>.  It may be
045     * included in a search request to indicate that the server should sort the
046     * results before returning them to the client.
047     * <BR><BR>
048     * The order in which the entries are to be sorted is specified by one or more
049     * {@link SortKey} values.  Each sort key includes an attribute name and a flag
050     * that indicates whether to sort in ascending or descending order.  It may also
051     * specify a custom matching rule that should be used to specify which logic
052     * should be used to perform the sorting.
053     * <BR><BR>
054     * If the search is successful, then the search result done message may include
055     * the {@link ServerSideSortResponseControl} to provide information about the
056     * status of the sort processing.
057     * <BR><BR>
058     * <H2>Example</H2>
059     * The following example demonstrates the use of the server-side sort controls
060     * to retrieve all users in the Sales department, sorted by last name and then
061     * by first name:
062     * <PRE>
063     *   SearchRequest searchRequest =
064     *        new SearchRequest("dc=example,dc=com", SearchScope.SUB, "(ou=Sales)");
065     *   searchRequest.addControl(new ServerSideSortRequestControl(
066     *        new SortKey("sn"), new SortKey("givenName")));
067     *   SearchResult searchResult = connection.search(searchRequest);
068     * </PRE>
069     * <BR><BR>
070     * <H2>Client-Side Sorting</H2>
071     * The UnboundID LDAP SDK for Java provides support for client-side sorting as
072     * an alternative to server-side sorting.  Client-side sorting may be useful in
073     * cases in which the target server does not support the use of the server-side
074     * sort control, or when it is desirable to perform the sort processing on the
075     * client systems rather than on the directory server systems.  See the
076     * {@link com.unboundid.ldap.sdk.EntrySorter} class for details on performing
077     * client-side sorting in the LDAP SDK.
078     */
079    @NotMutable()
080    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
081    public final class ServerSideSortRequestControl
082           extends Control
083    {
084      /**
085       * The OID (1.2.840.113556.1.4.473) for the server-side sort request control.
086       */
087      public static final String SERVER_SIDE_SORT_REQUEST_OID =
088           "1.2.840.113556.1.4.473";
089    
090    
091    
092      /**
093       * The serial version UID for this serializable class.
094       */
095      private static final long serialVersionUID = -3021901578330574772L;
096    
097    
098    
099      // The set of sort keys to use with this control.
100      private final SortKey[] sortKeys;
101    
102    
103    
104      /**
105       * Creates a new server-side sort control that will sort the results based on
106       * the provided set of sort keys.
107       *
108       * @param  sortKeys  The set of sort keys to define the desired order in which
109       *                   the results should be returned.  It must not be
110       *                   {@code null} or empty.
111       */
112      public ServerSideSortRequestControl(final SortKey... sortKeys)
113      {
114        super(SERVER_SIDE_SORT_REQUEST_OID, false, encodeValue(sortKeys));
115    
116        this.sortKeys = sortKeys;
117      }
118    
119    
120    
121      /**
122       * Creates a new server-side sort control that will sort the results based on
123       * the provided set of sort keys.
124       *
125       * @param  isCritical  Indicates whether this control should be marked
126       *                     critical.
127       * @param  sortKeys    The set of sort keys to define the desired order in
128       *                     which the results should be returned.  It must not be
129       *                     {@code null} or empty.
130       */
131      public ServerSideSortRequestControl(final boolean isCritical,
132                                          final SortKey... sortKeys)
133      {
134        super(SERVER_SIDE_SORT_REQUEST_OID, isCritical, encodeValue(sortKeys));
135    
136        this.sortKeys = sortKeys;
137      }
138    
139    
140    
141      /**
142       * Creates a new server-side sort request control which is decoded from the
143       * provided generic control.
144       *
145       * @param  control  The generic control to be decoded as a server-side sort
146       *                  request control.
147       *
148       * @throws  LDAPException  If the provided control cannot be decoded as a
149       *                         server-side sort request control.
150       */
151      public ServerSideSortRequestControl(final Control control)
152             throws LDAPException
153      {
154        super(control);
155    
156        final ASN1OctetString value = control.getValue();
157        if (value == null)
158        {
159          throw new LDAPException(ResultCode.DECODING_ERROR,
160                                  ERR_SORT_REQUEST_NO_VALUE.get());
161        }
162    
163        try
164        {
165          final ASN1Element valueElement = ASN1Element.decode(value.getValue());
166          final ASN1Element[] elements =
167               ASN1Sequence.decodeAsSequence(valueElement).elements();
168          sortKeys = new SortKey[elements.length];
169          for (int i=0; i < elements.length; i++)
170          {
171            sortKeys[i] = SortKey.decode(elements[i]);
172          }
173        }
174        catch (Exception e)
175        {
176          debugException(e);
177          throw new LDAPException(ResultCode.DECODING_ERROR,
178                                  ERR_SORT_REQUEST_CANNOT_DECODE.get(e), e);
179        }
180      }
181    
182    
183    
184      /**
185       * Encodes the provided information into an octet string that can be used as
186       * the value for this control.
187       *
188       * @param  sortKeys  The set of sort keys to define the desired order in which
189       *                   the results should be returned.  It must not be
190       *                   {@code null} or empty.
191       *
192       * @return  An ASN.1 octet string that can be used as the value for this
193       *          control.
194       */
195      private static ASN1OctetString encodeValue(final SortKey[] sortKeys)
196      {
197        ensureNotNull(sortKeys);
198        ensureTrue(sortKeys.length > 0,
199                   "ServerSideSortRequestControl.sortKeys must not be empty.");
200    
201        final ASN1Element[] valueElements = new ASN1Element[sortKeys.length];
202        for (int i=0; i < sortKeys.length; i++)
203        {
204          valueElements[i] = sortKeys[i].encode();
205        }
206    
207        return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
208      }
209    
210    
211    
212      /**
213       * Retrieves the set of sort keys that define the desired order in which the
214       * results should be returned.
215       *
216       * @return  The set of sort keys that define the desired order in which the
217       *          results should be returned.
218       */
219      public SortKey[] getSortKeys()
220      {
221        return sortKeys;
222      }
223    
224    
225    
226      /**
227       * {@inheritDoc}
228       */
229      @Override()
230      public String getControlName()
231      {
232        return INFO_CONTROL_NAME_SORT_REQUEST.get();
233      }
234    
235    
236    
237      /**
238       * {@inheritDoc}
239       */
240      @Override()
241      public void toString(final StringBuilder buffer)
242      {
243        buffer.append("ServerSideSortRequestControl(sortKeys={");
244    
245        for (int i=0; i < sortKeys.length; i++)
246        {
247          if (i > 0)
248          {
249            buffer.append(", ");
250          }
251    
252          buffer.append('\'');
253          sortKeys[i].toString(buffer);
254          buffer.append('\'');
255        }
256    
257        buffer.append("})");
258      }
259    }