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