001/* 002 * Copyright 2007-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.controls; 037 038 039 040import java.io.Serializable; 041import java.util.ArrayList; 042 043import com.unboundid.asn1.ASN1Boolean; 044import com.unboundid.asn1.ASN1Element; 045import com.unboundid.asn1.ASN1OctetString; 046import com.unboundid.asn1.ASN1Sequence; 047import com.unboundid.ldap.sdk.LDAPException; 048import com.unboundid.ldap.sdk.ResultCode; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.Validator; 055 056import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 057 058 059 060/** 061 * This class provides a data structure for representing a sort key that is to 062 * be used in conjunction with the {@link ServerSideSortRequestControl} for 063 * requesting that the server sort the results before returning them to the 064 * client. 065 * <BR><BR> 066 * A sort key includes the following elements: 067 * <UL> 068 * <LI>The name of the attribute for which sorting is to be performed.</LI> 069 * <LI>A {@code reverseOrder} flag that indicates whether the results should 070 * be sorted in ascending order (if the value is {@code false}) or 071 * descending order (if the value is {@code true}).</LI> 072 * <LI>An optional matching rule ID, which specifies the ordering matching 073 * rule that should be used to perform the sorting. If this is not 074 * provided, then the default ordering matching rule for the specified 075 * attribute will be used.</LI> 076 * </UL> 077 */ 078@NotMutable() 079@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 080public final class SortKey 081 implements Serializable 082{ 083 /** 084 * The BER type that should be used for the matching rule ID element. 085 */ 086 private static final byte TYPE_MATCHING_RULE_ID = (byte) 0x80; 087 088 089 090 /** 091 * The BER type that should be used for the reverse order element. 092 */ 093 private static final byte TYPE_REVERSE_ORDER = (byte) 0x81; 094 095 096 097 /** 098 * The serial version UID for this serializable class. 099 */ 100 private static final long serialVersionUID = -8631224188301402858L; 101 102 103 104 // Indicates whether the sort should be performed in reverse order. 105 private final boolean reverseOrder; 106 107 // The attribute name for this sort key. 108 private final String attributeName; 109 110 // The matching rule ID for this sort key. 111 private final String matchingRuleID; 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, and it will 118 * not use reverse order. 119 * 120 * @param attributeName The attribute name for this sort key. It must not 121 * be {@code null}. 122 */ 123 public SortKey(final String attributeName) 124 { 125 this(attributeName, null, false); 126 } 127 128 129 130 /** 131 * Creates a new sort key with the specified attribute name. It will use the 132 * default ordering matching rule associated with that attribute. 133 * 134 * @param attributeName The attribute name for this sort key. It must not 135 * be {@code null}. 136 * @param reverseOrder Indicates whether the sort should be performed in 137 * reverse order. 138 */ 139 public SortKey(final String attributeName, final boolean reverseOrder) 140 { 141 this(attributeName, null, reverseOrder); 142 } 143 144 145 146 /** 147 * Creates a new sort key with the provided information. 148 * 149 * @param attributeName The attribute name for this sort key. It must not 150 * be {@code null}. 151 * @param matchingRuleID The name or OID of the ordering matching rule that 152 * should be used to perform the sort. It may be 153 * {@code null} if the default ordering matching rule 154 * for the specified attribute is to be used. 155 * @param reverseOrder Indicates whether the sort should be performed in 156 * reverse order. 157 */ 158 public SortKey(final String attributeName, final String matchingRuleID, 159 final boolean reverseOrder) 160 { 161 Validator.ensureNotNull(attributeName); 162 163 this.attributeName = attributeName; 164 this.matchingRuleID = matchingRuleID; 165 this.reverseOrder = reverseOrder; 166 } 167 168 169 170 /** 171 * Retrieves the attribute name for this sort key. 172 * 173 * @return The attribute name for this sort key. 174 */ 175 public String getAttributeName() 176 { 177 return attributeName; 178 } 179 180 181 182 /** 183 * Retrieves the name or OID of the ordering matching rule that should be used 184 * to perform the sort, if defined. 185 * 186 * @return The name or OID of the ordering matching rule that should be used 187 * to perform the sort, or {@code null} if the sort should use the 188 * default ordering matching rule associated with the specified 189 * attribute. 190 */ 191 public String getMatchingRuleID() 192 { 193 return matchingRuleID; 194 } 195 196 197 198 /** 199 * Indicates whether the sort should be performed in reverse order. 200 * 201 * @return {@code true} if the sort should be performed in reverse order, or 202 * {@code false} if it should be performed in the standard order for 203 * the associated ordering matching rule. 204 */ 205 public boolean reverseOrder() 206 { 207 return reverseOrder; 208 } 209 210 211 212 /** 213 * Encodes this sort key into an ASN.1 sequence suitable for use in the 214 * server-side sort control. 215 * 216 * @return An ASN.1 sequence containing the encoded representation of this 217 * sort key. 218 */ 219 ASN1Sequence encode() 220 { 221 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 222 elements.add(new ASN1OctetString(attributeName)); 223 224 if (matchingRuleID != null) 225 { 226 elements.add(new ASN1OctetString(TYPE_MATCHING_RULE_ID, matchingRuleID)); 227 } 228 229 if (reverseOrder) 230 { 231 elements.add(new ASN1Boolean(TYPE_REVERSE_ORDER, reverseOrder)); 232 } 233 234 return new ASN1Sequence(elements); 235 } 236 237 238 239 /** 240 * Decodes the provided ASN.1 element as a sort key. 241 * 242 * @param element The ASN.1 element to decode as a sort key. 243 * 244 * @return The decoded sort key. 245 * 246 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 247 * a sort key. 248 */ 249 public static SortKey decode(final ASN1Element element) 250 throws LDAPException 251 { 252 final ASN1Element[] elements; 253 try 254 { 255 elements = ASN1Sequence.decodeAsSequence(element).elements(); 256 } 257 catch (final Exception e) 258 { 259 Debug.debugException(e); 260 throw new LDAPException(ResultCode.DECODING_ERROR, 261 ERR_SORT_KEY_NOT_SEQUENCE.get(e), e); 262 } 263 264 if ((elements.length < 1) || (elements.length > 3)) 265 { 266 throw new LDAPException(ResultCode.DECODING_ERROR, 267 ERR_SORT_KEY_INVALID_ELEMENT_COUNT.get(elements.length)); 268 } 269 270 boolean reverseOrder = false; 271 String matchingRuleID = null; 272 final String attributeName = 273 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 274 for (int i=1; i < elements.length; i++) 275 { 276 switch (elements[i].getType()) 277 { 278 case TYPE_MATCHING_RULE_ID: 279 matchingRuleID = 280 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue(); 281 break; 282 283 case TYPE_REVERSE_ORDER: 284 try 285 { 286 reverseOrder = 287 ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue(); 288 } 289 catch (final Exception e) 290 { 291 Debug.debugException(e); 292 throw new LDAPException(ResultCode.DECODING_ERROR, 293 ERR_SORT_KEY_REVERSE_NOT_BOOLEAN.get(e), e); 294 } 295 break; 296 297 default: 298 throw new LDAPException(ResultCode.DECODING_ERROR, 299 ERR_SORT_KEY_ELEMENT_INVALID_TYPE.get( 300 StaticUtils.toHex(elements[i].getType()))); 301 } 302 } 303 304 return new SortKey(attributeName, matchingRuleID, reverseOrder); 305 } 306 307 308 309 /** 310 * Retrieves a string representation of this sort key. 311 * 312 * @return A string representation of this sort key. 313 */ 314 @Override() 315 public String toString() 316 { 317 final StringBuilder buffer = new StringBuilder(); 318 toString(buffer); 319 return buffer.toString(); 320 } 321 322 323 324 /** 325 * Appends a string representation of this sort key to the provided buffer. 326 * 327 * @param buffer The buffer to which to append a string representation of 328 * this sort key. 329 */ 330 public void toString(final StringBuilder buffer) 331 { 332 buffer.append("SortKey(attributeName="); 333 buffer.append(attributeName); 334 335 if (matchingRuleID != null) 336 { 337 buffer.append(", matchingRuleID="); 338 buffer.append(matchingRuleID); 339 } 340 341 buffer.append(", reverseOrder="); 342 buffer.append(reverseOrder); 343 buffer.append(')'); 344 } 345}