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 ForgeRock AS
026 */
027package org.opends.server.backends.jeb;
028
029import java.util.List;
030
031import org.forgerock.opendj.ldap.ByteString;
032import org.opends.server.types.Attribute;
033import org.opends.server.types.AttributeType;
034import org.opends.server.types.Entry;
035import org.opends.server.types.SortKey;
036import org.opends.server.types.SortOrder;
037
038/**
039 * This class defines a data structure that holds a set of attribute values that
040 * are associated with a sort order for a given entry.  Any or all of the
041 * attribute values may be {@code null} if the entry does not include any values
042 * for the attribute type targeted by the corresponding sort key.
043 * <BR><BR>
044 * This class implements the {@code Comparable} interface and may therefore be
045 * used to order the elements in components like {@code TreeMap} and
046 * {@code TreeSet}.
047 * <p>
048 * FIXME: replace with the SDK's SortKey?
049 */
050public class SortValues
051       implements Comparable<SortValues>
052{
053  /** The set of sort keys (attribute values) in this sort order. */
054  private ByteString[] values;
055  /**
056   * The types of sort keys.
057   *
058   * @see #values
059   */
060  private AttributeType[] types;
061
062  /** The entry ID for the entry associated with this sort values. */
063  private EntryID entryID;
064
065  /** The sort order for this set of sort values. */
066  private SortOrder sortOrder;
067
068
069
070  /**
071   * Creates a new sort values object with the provided information.
072   *
073   * @param entryID    The entry ID for the entry associated with this set of
074   *                   values.
075   * @param values     The attribute values for this sort values.
076   * @param sortOrder  The sort order to use to obtain the necessary values.
077   */
078  public SortValues(EntryID entryID, ByteString[] values,
079                    SortOrder sortOrder)
080  {
081    this.entryID = entryID;
082    this.sortOrder = sortOrder;
083    this.values = values;
084
085    final SortKey[] sortKeys = sortOrder.getSortKeys();
086    this.types = new AttributeType[sortKeys.length];
087    for (int i = 0; i < sortKeys.length; i++)
088    {
089      types[i] = sortKeys[i].getAttributeType();
090    }
091  }
092
093  /**
094   * Creates a new sort values object with the provided information.
095   *
096   * @param  entryID    The entry ID for the entry associated with this set of
097   *                    values.
098   * @param  entry      The entry containing the values to extract and use when
099   *                    sorting.
100   * @param  sortOrder  The sort order to use to obtain the necessary values.
101   */
102  public SortValues(EntryID entryID, Entry entry, SortOrder sortOrder)
103  {
104    this.entryID   = entryID;
105    this.sortOrder = sortOrder;
106
107    SortKey[] sortKeys = sortOrder.getSortKeys();
108    this.values = new ByteString[sortKeys.length];
109    this.types = new AttributeType[sortKeys.length];
110    for (int i=0; i < sortKeys.length; i++)
111    {
112      SortKey sortKey = sortKeys[i];
113      types[i] = sortKey.getAttributeType();
114      List<Attribute> attrList = entry.getAttribute(types[i]);
115      if (attrList != null)
116      {
117        values[i] = findBestMatchingValue(sortKey, attrList);
118      }
119    }
120  }
121
122  /**
123   * Finds the best matching attribute value for the provided sort key in the
124   * provided attribute list.
125   * <p>
126   * There may be multiple versions of this attribute in the target entry (e.g.,
127   * with different sets of options), and it may also be a multivalued
128   * attribute. In that case, we need to find the value that is the best match
129   * for the corresponding sort key (i.e., for sorting in ascending order, we
130   * want to find the lowest value; for sorting in descending order, we want to
131   * find the highest value). This is handled by the SortKey.compareValues
132   * method.
133   */
134  private ByteString findBestMatchingValue(SortKey sortKey, List<Attribute> attrList)
135  {
136    ByteString sortValue = null;
137    for (Attribute a : attrList)
138    {
139      for (ByteString v : a)
140      {
141        if (sortValue == null || sortKey.compareValues(v, sortValue) < 0)
142        {
143          sortValue = v;
144        }
145      }
146    }
147    return sortValue;
148  }
149
150  /**
151   * Compares this set of sort values with the provided set of values to
152   * determine their relative order in a sorted list.
153   *
154   * @param  sortValues  The set of values to compare against this sort values.
155   *                     It must also have the same sort order as this set of
156   *                     values.
157   *
158   * @return  A negative value if this sort values object should come before the
159   *          provided values in a sorted list, a positive value if this sort
160   *          values object should come after the provided values in a sorted
161   *          list, or zero if there is no significant difference in their
162   *          relative order.
163   */
164  @Override
165  public int compareTo(SortValues sortValues)
166  {
167    SortKey[] sortKeys = sortOrder.getSortKeys();
168
169    for (int i=0; i < values.length; i++)
170    {
171      int compareValue = sortKeys[i].compareValues(values[i], sortValues.values[i]);
172      if (compareValue != 0)
173      {
174        return compareValue;
175      }
176    }
177
178    // If we've gotten here, then we can't tell a difference between the sets of
179    // sort values, so sort based on entry ID.
180    return entryID.compareTo(sortValues.entryID);
181  }
182
183  /**
184   * Compares the first element in this set of sort values with the provided
185   * assertion value to determine whether the assertion value is greater than or
186   * equal to the initial sort value.  This is used during VLV processing to
187   * find the offset by assertion value.
188   *
189   * @param  assertionValue  The assertion value to compare against the first
190   *                         sort value.
191   *
192   * @return  A negative value if the provided assertion value should come
193   *          before the first sort value, zero if the provided assertion value
194   *          is equal to the first sort value, or a positive value if the
195   *          provided assertion value should come after the first sort value.
196   */
197  public int compareTo(ByteString assertionValue)
198  {
199    SortKey sortKey = sortOrder.getSortKeys()[0];
200    return sortKey.compareValues(values[0], assertionValue);
201  }
202
203  /**
204   * Retrieves a string representation of this sort values object.
205   *
206   * @return  A string representation of this sort values object.
207   */
208  @Override
209  public String toString()
210  {
211    StringBuilder buffer = new StringBuilder();
212    toString(buffer);
213    return buffer.toString();
214  }
215
216  /**
217   * Appends a string representation of this sort values object to the provided
218   * buffer.
219   *
220   * @param  buffer  The buffer to which the information should be appended.
221   */
222  public void toString(StringBuilder buffer)
223  {
224    buffer.append("SortValues(");
225
226    SortKey[] sortKeys = sortOrder.getSortKeys();
227    for (int i=0; i < sortKeys.length; i++)
228    {
229      if (i > 0)
230      {
231        buffer.append(",");
232      }
233
234      buffer.append(sortKeys[i].ascending() ? "+" : "-");
235
236      buffer.append(sortKeys[i].getAttributeType().getNameOrOID());
237      buffer.append("=");
238      buffer.append(values[i]);
239    }
240
241    buffer.append(", id=");
242    buffer.append(entryID);
243    buffer.append(")");
244  }
245
246  /**
247   * Retrieve the attribute values in this sort values.
248   *
249   * @return The array of attribute values for this sort values.
250   */
251  public ByteString[] getValues()
252  {
253    return values;
254  }
255
256  /**
257   * Retrieve the type of the attribute values in this sort values.
258   *
259   * @return The array of type of the attribute values for this sort values.
260   */
261  public AttributeType[] getTypes()
262  {
263    return types;
264  }
265
266  /**
267   * Retrieve the entry ID in this sort values.
268   *
269   * @return The entry ID for this sort values.
270   */
271  public long getEntryID()
272  {
273    return entryID.longValue();
274  }
275}