001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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 java.io.Serializable; 026import java.util.ArrayList; 027 028import com.unboundid.asn1.ASN1Boolean; 029import com.unboundid.asn1.ASN1Element; 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.asn1.ASN1Sequence; 032import com.unboundid.ldap.sdk.LDAPException; 033import com.unboundid.ldap.sdk.ResultCode; 034import com.unboundid.util.NotMutable; 035import com.unboundid.util.ThreadSafety; 036import com.unboundid.util.ThreadSafetyLevel; 037 038import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 039import static com.unboundid.util.Debug.*; 040import static com.unboundid.util.StaticUtils.*; 041import 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) 065public 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}