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
055  /** The indication of whether the sort should be ascending. */
056  private boolean ascending;
057
058  /** The ordering matching rule to use with this sort key. */
059  private MatchingRule orderingRule;
060
061
062
063  /**
064   * Creates a new sort key with the provided information.
065   *
066   * @param  attributeType  The attribute type for this sort key.
067   * @param  ascending      Indicates whether the sort should be in
068   *                        ascending order rather than descending.
069   */
070  public SortKey(AttributeType attributeType, boolean ascending)
071  {
072    this.attributeType = attributeType;
073    this.ascending     = ascending;
074
075    orderingRule = null;
076  }
077
078
079
080  /**
081   * Creates a new sort key with the provided information.
082   *
083   * @param  attributeType  The attribute type for this sort key.
084   * @param  ascending      Indicates whether the sort should be in
085   *                        ascending order rather than descending.
086   * @param  orderingRule   The ordering matching rule to use with
087   *                        this sort key.
088   */
089  public SortKey(AttributeType attributeType, boolean ascending, MatchingRule orderingRule)
090  {
091    this.attributeType = attributeType;
092    this.ascending     = ascending;
093    this.orderingRule  = orderingRule;
094  }
095
096
097
098  /**
099   * Retrieves the attribute type for this sort key.
100   *
101   * @return  The attribute type for this sort key.
102   */
103  public AttributeType getAttributeType()
104  {
105    return attributeType;
106  }
107
108
109
110  /**
111   * Indicates whether the specified attribute should be sorted in
112   * ascending order.
113   *
114   * @return  {@code true} if the attribute should be sorted in
115   *          ascending order, or {@code false} if it should be sorted
116   *          in descending order.
117   */
118  public boolean ascending()
119  {
120    return ascending;
121  }
122
123
124
125  /**
126   * Retrieves the ordering matching rule to use with this sort key.
127   *
128   * @return  The ordering matching rule to use with this sort key.
129   */
130  public MatchingRule getOrderingRule()
131  {
132    return orderingRule;
133  }
134
135
136
137  /**
138   * Compares the provided values using this sort key.
139   *
140   * @param  value1  The first value to be compared.
141   * @param  value2  The second value to be compared.
142   *
143   * @return  A negative value if the first value should come before
144   *          the second in a sorted list, a positive value if the
145   *          first value should come after the second in a sorted
146   *          list, or zero if there is no relative difference between
147   *          the values.
148   */
149  public int compareValues(ByteString value1, ByteString value2)
150  {
151    // A null value will always come after a non-null value.
152    if (value1 == null)
153    {
154      if (value2 == null)
155      {
156        return 0;
157      }
158      else
159      {
160        return 1;
161      }
162    }
163    else if (value2 == null)
164    {
165      return -1;
166    }
167
168
169    // Use the ordering matching rule if one is provided.
170    // Otherwise, fall back on the default ordering rule for the attribute type.
171    if (orderingRule != null)
172    {
173      return compareValues(orderingRule, value1, value2);
174    }
175    final MatchingRule rule = attributeType.getOrderingMatchingRule();
176    if (rule != null)
177    {
178      return compareValues(rule, value1, value2);
179    }
180    return 0;
181  }
182
183  private int compareValues(MatchingRule rule, ByteString value1,
184      ByteString value2)
185  {
186    try
187    {
188      final ByteString val1 = rule.normalizeAttributeValue(value1);
189      final ByteString val2 = rule.normalizeAttributeValue(value2);
190      return ascending ? val1.compareTo(val2) : val2.compareTo(val1);
191    }
192    catch (Exception e)
193    {
194      logger.traceException(e);
195      return 0;
196    }
197  }
198
199
200
201  /**
202   * Retrieves a string representation of this sort key.
203   *
204   * @return  A string representation of this sort key.
205   */
206  @Override
207  public String toString()
208  {
209    StringBuilder buffer = new StringBuilder();
210    toString(buffer);
211    return buffer.toString();
212  }
213
214
215
216  /**
217   * Appends a string representation of this sort key to the
218   * provided buffer.
219   *
220   * @param  buffer  The buffer to which the information should be
221   *                 appended.
222   */
223  public void toString(StringBuilder buffer)
224  {
225    buffer.append("SortKey(");
226    if (ascending)
227    {
228      buffer.append("+");
229    }
230    else
231    {
232      buffer.append("-");
233    }
234    buffer.append(attributeType.getNameOrOID());
235
236    if (orderingRule != null)
237    {
238      buffer.append(":");
239      buffer.append(orderingRule.getNameOrOID());
240    }
241
242    buffer.append(")");
243  }
244
245  /**
246   * Retrieves the hash code for this sort key.
247   *
248   * @return  The hash code for this sort key.
249   */
250  @Override
251  public int hashCode()
252  {
253    int hashCode = 0;
254
255    if(ascending)
256    {
257      hashCode += 1;
258    }
259
260    hashCode += attributeType.hashCode();
261
262    if(orderingRule != null)
263    {
264      hashCode += orderingRule.hashCode();
265    }
266
267    return hashCode;
268  }
269
270  /**
271   * Indicates whether this sort key is equal to the provided
272   * object.
273   *
274   * @param  o  The object for which to make the determination.
275   *
276   * @return  <CODE>true</CODE> if the provide object is equal to this
277   *          sort key, or <CODE>false</CODE> if it is not.
278   */
279  @Override
280  public boolean equals(Object o)
281  {
282    if(o == null)
283    {
284      return false;
285    }
286
287    if (o == this)
288    {
289      return true;
290    }
291
292    if (! (o instanceof SortKey))
293    {
294      return false;
295    }
296
297    SortKey s = (SortKey) o;
298
299    if(ascending != s.ascending)
300    {
301      return false;
302    }
303
304    if(!attributeType.equals(s.attributeType))
305    {
306      return false;
307    }
308
309    if(orderingRule != null)
310    {
311      if(s.orderingRule != null)
312      {
313        if(!orderingRule.equals(s.orderingRule))
314        {
315          return false;
316        }
317      }
318      else if(!orderingRule.equals(
319          s.attributeType.getOrderingMatchingRule()))
320      {
321        return false;
322      }
323    }
324    else if(s.orderingRule != null)
325    {
326      if(!attributeType.getOrderingMatchingRule().equals(
327          s.orderingRule))
328      {
329        return false;
330      }
331    }
332
333    return true;
334  }
335}
336