001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.types; 028 029import org.forgerock.i18n.slf4j.LocalizedLogger; 030import org.forgerock.opendj.ldap.ByteString; 031import org.forgerock.opendj.ldap.schema.MatchingRule; 032 033/** 034 * This class defines a data structure that may be used as a sort key. 035 * It includes an attribute type and a boolean value that indicates 036 * whether the sort should be ascending or descending. It may also 037 * contain a specific ordering matching rule that should be used for 038 * the sorting process, although if none is provided it will use the 039 * default ordering matching rule for the attribute type. 040 * <p> 041 * FIXME: replace with the equivalent SDK type. 042 */ 043@org.opends.server.types.PublicAPI( 044 stability=org.opends.server.types.StabilityLevel.VOLATILE, 045 mayInstantiate=true, 046 mayExtend=false, 047 mayInvoke=true) 048public final class SortKey 049{ 050 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 051 052 /** The attribute type for this sort key. */ 053 private AttributeType attributeType; 054 /** The indication of whether the sort should be ascending. */ 055 private boolean ascending; 056 /** The ordering matching rule to use with this sort key. */ 057 private MatchingRule orderingRule; 058 059 060 061 /** 062 * Creates a new sort key with the provided information. 063 * 064 * @param attributeType The attribute type for this sort key. 065 * @param ascending Indicates whether the sort should be in 066 * ascending order rather than descending. 067 */ 068 public SortKey(AttributeType attributeType, boolean ascending) 069 { 070 this(attributeType, ascending, null); 071 } 072 073 074 075 /** 076 * Creates a new sort key with the provided information. 077 * 078 * @param attributeType The attribute type for this sort key. 079 * @param ascending Indicates whether the sort should be in 080 * ascending order rather than descending. 081 * @param orderingRule The ordering matching rule to use with 082 * this sort key. 083 */ 084 public SortKey(AttributeType attributeType, boolean ascending, MatchingRule orderingRule) 085 { 086 this.attributeType = attributeType; 087 this.ascending = ascending; 088 this.orderingRule = orderingRule; 089 } 090 091 092 093 /** 094 * Retrieves the attribute type for this sort key. 095 * 096 * @return The attribute type for this sort key. 097 */ 098 public AttributeType getAttributeType() 099 { 100 return attributeType; 101 } 102 103 104 105 /** 106 * Indicates whether the specified attribute should be sorted in 107 * ascending order. 108 * 109 * @return {@code true} if the attribute should be sorted in 110 * ascending order, or {@code false} if it should be sorted 111 * in descending order. 112 */ 113 public boolean ascending() 114 { 115 return ascending; 116 } 117 118 119 120 /** 121 * Retrieves the ordering matching rule to use with this sort key. 122 * 123 * @return The ordering matching rule to use with this sort key. 124 */ 125 public MatchingRule getOrderingRule() 126 { 127 return orderingRule; 128 } 129 130 /** 131 * Retrieves the ordering matching rule to use with this sort key. 132 * 133 * @return The ordering matching rule to use with this sort key. 134 */ 135 public MatchingRule getEffectiveOrderingRule() 136 { 137 return orderingRule != null ? orderingRule : attributeType.getOrderingMatchingRule(); 138 } 139 140 /** 141 * Compares the provided values using this sort key. 142 * 143 * @param value1 The first value to be compared. 144 * @param value2 The second value to be compared. 145 * 146 * @return A negative value if the first value should come before 147 * the second in a sorted list, a positive value if the 148 * first value should come after the second in a sorted 149 * list, or zero if there is no relative difference between 150 * the values. 151 */ 152 public int compareValues(ByteString value1, ByteString value2) 153 { 154 // A null value will always come after a non-null value. 155 if (value1 == null) 156 { 157 if (value2 == null) 158 { 159 return 0; 160 } 161 else 162 { 163 return 1; 164 } 165 } 166 else if (value2 == null) 167 { 168 return -1; 169 } 170 171 172 // Use the ordering matching rule if one is provided. 173 // Otherwise, fall back on the default ordering rule for the attribute type. 174 if (orderingRule != null) 175 { 176 return compareValues(orderingRule, value1, value2); 177 } 178 final MatchingRule rule = attributeType.getOrderingMatchingRule(); 179 if (rule != null) 180 { 181 return compareValues(rule, value1, value2); 182 } 183 return 0; 184 } 185 186 private int compareValues(MatchingRule rule, ByteString value1, 187 ByteString value2) 188 { 189 try 190 { 191 final ByteString val1 = rule.normalizeAttributeValue(value1); 192 final ByteString val2 = rule.normalizeAttributeValue(value2); 193 return ascending ? val1.compareTo(val2) : val2.compareTo(val1); 194 } 195 catch (Exception e) 196 { 197 logger.traceException(e); 198 return 0; 199 } 200 } 201 202 203 204 /** 205 * Retrieves a string representation of this sort key. 206 * 207 * @return A string representation of this sort key. 208 */ 209 @Override 210 public String toString() 211 { 212 StringBuilder buffer = new StringBuilder(); 213 toString(buffer); 214 return buffer.toString(); 215 } 216 217 218 219 /** 220 * Appends a string representation of this sort key to the 221 * provided buffer. 222 * 223 * @param buffer The buffer to which the information should be 224 * appended. 225 */ 226 public void toString(StringBuilder buffer) 227 { 228 buffer.append("SortKey("); 229 if (ascending) 230 { 231 buffer.append("+"); 232 } 233 else 234 { 235 buffer.append("-"); 236 } 237 buffer.append(attributeType.getNameOrOID()); 238 239 if (orderingRule != null) 240 { 241 buffer.append(":"); 242 buffer.append(orderingRule.getNameOrOID()); 243 } 244 245 buffer.append(")"); 246 } 247 248 /** 249 * Retrieves the hash code for this sort key. 250 * 251 * @return The hash code for this sort key. 252 */ 253 @Override 254 public int hashCode() 255 { 256 int hashCode = 0; 257 258 if(ascending) 259 { 260 hashCode += 1; 261 } 262 263 hashCode += attributeType.hashCode(); 264 265 if(orderingRule != null) 266 { 267 hashCode += orderingRule.hashCode(); 268 } 269 270 return hashCode; 271 } 272 273 /** 274 * Indicates whether this sort key is equal to the provided 275 * object. 276 * 277 * @param o The object for which to make the determination. 278 * 279 * @return <CODE>true</CODE> if the provide object is equal to this 280 * sort key, or <CODE>false</CODE> if it is not. 281 */ 282 @Override 283 public boolean equals(Object o) 284 { 285 if(o == null) 286 { 287 return false; 288 } 289 290 if (o == this) 291 { 292 return true; 293 } 294 295 if (! (o instanceof SortKey)) 296 { 297 return false; 298 } 299 300 SortKey s = (SortKey) o; 301 302 if(ascending != s.ascending) 303 { 304 return false; 305 } 306 307 if(!attributeType.equals(s.attributeType)) 308 { 309 return false; 310 } 311 312 if(orderingRule != null) 313 { 314 if(s.orderingRule != null) 315 { 316 if(!orderingRule.equals(s.orderingRule)) 317 { 318 return false; 319 } 320 } 321 else if(!orderingRule.equals( 322 s.attributeType.getOrderingMatchingRule())) 323 { 324 return false; 325 } 326 } 327 else if(s.orderingRule != null) 328 { 329 if(!attributeType.getOrderingMatchingRule().equals( 330 s.orderingRule)) 331 { 332 return false; 333 } 334 } 335 336 return true; 337 } 338} 339