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