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 java.io.Serializable;
026 import java.util.ArrayList;
027
028 import com.unboundid.asn1.ASN1Boolean;
029 import com.unboundid.asn1.ASN1Element;
030 import com.unboundid.asn1.ASN1OctetString;
031 import com.unboundid.asn1.ASN1Sequence;
032 import com.unboundid.ldap.sdk.LDAPException;
033 import com.unboundid.ldap.sdk.ResultCode;
034 import com.unboundid.util.NotMutable;
035 import com.unboundid.util.ThreadSafety;
036 import com.unboundid.util.ThreadSafetyLevel;
037
038 import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
039 import static com.unboundid.util.Debug.*;
040 import static com.unboundid.util.StaticUtils.*;
041 import static com.unboundid.util.Validator.*;
042
043
044
045 /**
046 * This class provides a data structure for representing a sort key that is to
047 * be used in conjunction with the {@link ServerSideSortRequestControl} for
048 * requesting that the server sort the results before returning them to the
049 * client.
050 * <BR><BR>
051 * A sort key includes the following elements:
052 * <UL>
053 * <LI>The name of the attribute for which sorting is to be performed.</LI>
054 * <LI>A {@code reverseOrder} flag that indicates whether the results should
055 * be sorted in ascending order (if the value is {@code false}) or
056 * descending order (if the value is {@code true}).</LI>
057 * <LI>An optional matching rule ID, which specifies the ordering matching
058 * rule that should be used to perform the sorting. If this is not
059 * provided, then the default ordering matching rule for the specified
060 * attribute will be used.</LI>
061 * </UL>
062 */
063 @NotMutable()
064 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
065 public final class SortKey
066 implements Serializable
067 {
068 /**
069 * The BER type that should be used for the matching rule ID element.
070 */
071 private static final byte TYPE_MATCHING_RULE_ID = (byte) 0x80;
072
073
074
075 /**
076 * The BER type that should be used for the reverse order element.
077 */
078 private static final byte TYPE_REVERSE_ORDER = (byte) 0x81;
079
080
081
082 /**
083 * The serial version UID for this serializable class.
084 */
085 private static final long serialVersionUID = -8631224188301402858L;
086
087
088
089 // Indicates whether the sort should be performed in reverse order.
090 private final boolean reverseOrder;
091
092 // The attribute name for this sort key.
093 private final String attributeName;
094
095 // The matching rule ID for this sort key.
096 private final String matchingRuleID;
097
098
099
100 /**
101 * Creates a new sort key with the specified attribute name. It will use the
102 * default ordering matching rule associated with that attribute, and it will
103 * not use reverse order.
104 *
105 * @param attributeName The attribute name for this sort key. It must not
106 * be {@code null}.
107 */
108 public SortKey(final String attributeName)
109 {
110 this(attributeName, null, false);
111 }
112
113
114
115 /**
116 * Creates a new sort key with the specified attribute name. It will use the
117 * default ordering matching rule associated with that attribute.
118 *
119 * @param attributeName The attribute name for this sort key. It must not
120 * be {@code null}.
121 * @param reverseOrder Indicates whether the sort should be performed in
122 * reverse order.
123 */
124 public SortKey(final String attributeName, final boolean reverseOrder)
125 {
126 this(attributeName, null, reverseOrder);
127 }
128
129
130
131 /**
132 * Creates a new sort key with the provided information.
133 *
134 * @param attributeName The attribute name for this sort key. It must not
135 * be {@code null}.
136 * @param matchingRuleID The name or OID of the ordering matching rule that
137 * should be used to perform the sort. It may be
138 * {@code null} if the default ordering matching rule
139 * for the specified attribute is to be used.
140 * @param reverseOrder Indicates whether the sort should be performed in
141 * reverse order.
142 */
143 public SortKey(final String attributeName, final String matchingRuleID,
144 final boolean reverseOrder)
145 {
146 ensureNotNull(attributeName);
147
148 this.attributeName = attributeName;
149 this.matchingRuleID = matchingRuleID;
150 this.reverseOrder = reverseOrder;
151 }
152
153
154
155 /**
156 * Retrieves the attribute name for this sort key.
157 *
158 * @return The attribute name for this sort key.
159 */
160 public String getAttributeName()
161 {
162 return attributeName;
163 }
164
165
166
167 /**
168 * Retrieves the name or OID of the ordering matching rule that should be used
169 * to perform the sort, if defined.
170 *
171 * @return The name or OID of the ordering matching rule that should be used
172 * to perform the sort, or {@code null} if the sort should use the
173 * default ordering matching rule associated with the specified
174 * attribute.
175 */
176 public String getMatchingRuleID()
177 {
178 return matchingRuleID;
179 }
180
181
182
183 /**
184 * Indicates whether the sort should be performed in reverse order.
185 *
186 * @return {@code true} if the sort should be performed in reverse order, or
187 * {@code false} if it should be performed in the standard order for
188 * the associated ordering matching rule.
189 */
190 public boolean reverseOrder()
191 {
192 return reverseOrder;
193 }
194
195
196
197 /**
198 * Encodes this sort key into an ASN.1 sequence suitable for use in the
199 * server-side sort control.
200 *
201 * @return An ASN.1 sequence containing the encoded representation of this
202 * sort key.
203 */
204 ASN1Sequence encode()
205 {
206 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3);
207 elements.add(new ASN1OctetString(attributeName));
208
209 if (matchingRuleID != null)
210 {
211 elements.add(new ASN1OctetString(TYPE_MATCHING_RULE_ID, matchingRuleID));
212 }
213
214 if (reverseOrder)
215 {
216 elements.add(new ASN1Boolean(TYPE_REVERSE_ORDER, reverseOrder));
217 }
218
219 return new ASN1Sequence(elements);
220 }
221
222
223
224 /**
225 * Decodes the provided ASN.1 element as a sort key.
226 *
227 * @param element The ASN.1 element to decode as a sort key.
228 *
229 * @return The decoded sort key.
230 *
231 * @throws LDAPException If the provided ASN.1 element cannot be decoded as
232 * a sort key.
233 */
234 public static SortKey decode(final ASN1Element element)
235 throws LDAPException
236 {
237 final ASN1Element[] elements;
238 try
239 {
240 elements = ASN1Sequence.decodeAsSequence(element).elements();
241 }
242 catch (Exception e)
243 {
244 debugException(e);
245 throw new LDAPException(ResultCode.DECODING_ERROR,
246 ERR_SORT_KEY_NOT_SEQUENCE.get(e), e);
247 }
248
249 if ((elements.length < 1) || (elements.length > 3))
250 {
251 throw new LDAPException(ResultCode.DECODING_ERROR,
252 ERR_SORT_KEY_INVALID_ELEMENT_COUNT.get(
253 elements.length));
254 }
255
256 boolean reverseOrder = false;
257 String matchingRuleID = null;
258 final String attributeName =
259 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
260 for (int i=1; i < elements.length; i++)
261 {
262 switch (elements[i].getType())
263 {
264 case TYPE_MATCHING_RULE_ID:
265 matchingRuleID =
266 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
267 break;
268
269 case TYPE_REVERSE_ORDER:
270 try
271 {
272 reverseOrder =
273 ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue();
274 }
275 catch (Exception e)
276 {
277 debugException(e);
278 throw new LDAPException(ResultCode.DECODING_ERROR,
279 ERR_SORT_KEY_REVERSE_NOT_BOOLEAN.get(e), e);
280 }
281 break;
282
283 default:
284 throw new LDAPException(ResultCode.DECODING_ERROR,
285 ERR_SORT_KEY_ELEMENT_INVALID_TYPE.get(
286 toHex(elements[i].getType())));
287 }
288 }
289
290 return new SortKey(attributeName, matchingRuleID, reverseOrder);
291 }
292
293
294
295 /**
296 * Retrieves a string representation of this sort key.
297 *
298 * @return A string representation of this sort key.
299 */
300 @Override()
301 public String toString()
302 {
303 final StringBuilder buffer = new StringBuilder();
304 toString(buffer);
305 return buffer.toString();
306 }
307
308
309
310 /**
311 * Appends a string representation of this sort key to the provided buffer.
312 *
313 * @param buffer The buffer to which to append a string representation of
314 * this sort key.
315 */
316 public void toString(final StringBuilder buffer)
317 {
318 buffer.append("SortKey(attributeName=");
319 buffer.append(attributeName);
320
321 if (matchingRuleID != null)
322 {
323 buffer.append(", matchingRuleID=");
324 buffer.append(matchingRuleID);
325 }
326
327 buffer.append(", reverseOrder=");
328 buffer.append(reverseOrder);
329 buffer.append(')');
330 }
331 }