/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.server.backends.jeb; import org.opends.server.api.OrderingMatchingRule; import org.opends.server.types.AttributeValue; import org.opends.server.types.DirectoryException; import org.opends.server.types.ByteString; import java.util.Comparator; import java.io.Serializable; import com.sleepycat.je.DatabaseException; /** * This class is used to compare the keys used in a VLV index. Each key is * made up the sort values and the entry ID of the largest entry in the sorted * set stored in the data for the key. */ public class VLVKeyComparator implements Comparator<byte[]>, Serializable { /** * The serial version identifier required to satisfy the compiler because this * class implements the <CODE>java.io.Serializable</CODE> interface. This * value was generated using the <CODE>serialver</CODE> command-line utility * included with the Java SDK. */ static final long serialVersionUID = 1585167927344130604L; private OrderingMatchingRule[] orderingRules; private boolean[] ascending; /** * Construst a new VLV Key Comparator object. * * @param orderingRules The array of ordering rules to use when comparing * the decoded values in the key. * @param ascending The array of booleans indicating the ordering for * each value. */ public VLVKeyComparator(OrderingMatchingRule[] orderingRules, boolean[] ascending) { this.orderingRules = orderingRules; this.ascending = ascending; } /** * Compares the contents of the provided byte arrays to determine their * relative order. A key in the VLV index contains the sorted attribute values * in order followed by the 8 byte entry ID. A attribute value of length 0 * means that value is null and the attribute type was not part of the entry. * A null value is always considered greater then a non null value. If all * attribute values are the same, the entry ID will be used to determine the * ordering. * * When comparing partial keys (ie. keys with only the first attribute value * encoded for evaluating VLV assertion value offsets or keys with no entry * IDs), only information available in both byte keys will be used to * determine the ordering. If all available information is the same, 0 will * be returned. * * @param b1 The first byte array to use in the comparison. * @param b2 The second byte array to use in the comparison. * * @return A negative integer if <CODE>b1</CODE> should come before * <CODE>b2</CODE> in ascending order, a positive integer if * <CODE>b1</CODE> should come after <CODE>b2</CODE> in ascending * order, or zero if there is no difference between the values with * regard to ordering. */ public int compare(byte[] b1, byte[] b2) { // A 0 length byte array is a special key used for the unbound max // sort values set. It always comes after a non length byte array. if(b1.length == 0) { if(b2.length == 0) { return 0; } else { return 1; } } else if(b2.length == 0) { return -1; } int b1Pos = 0; int b2Pos = 0; for (int j=0; j < orderingRules.length && b1Pos < b1.length && b2Pos < b2.length; j++) { int b1Length = b1[b1Pos] & 0x7F; if (b1[b1Pos++] != b1Length) { int b1NumLengthBytes = b1Length; b1Length = 0; for (int k=0; k < b1NumLengthBytes; k++, b1Pos++) { b1Length = (b1Length << 8) | (b1[b1Pos] & 0xFF); } } int b2Length = b2[b2Pos] & 0x7F; if (b2[b2Pos++] != b2Length) { int b2NumLengthBytes = b2Length; b2Length = 0; for (int k=0; k < b2NumLengthBytes; k++, b2Pos++) { b2Length = (b2Length << 8) | (b2[b2Pos] & 0xFF); } } byte[] b1Bytes; byte[] b2Bytes; if(b1Length > 0) { b1Bytes = new byte[b1Length]; System.arraycopy(b1, b1Pos, b1Bytes, 0, b1Length); b1Pos += b1Length; } else { b1Bytes = null; } if(b2Length > 0) { b2Bytes = new byte[b2Length]; System.arraycopy(b2, b2Pos, b2Bytes, 0, b2Length); b2Pos += b2Length; } else { b2Bytes = null; } // A null value will always come after a non-null value. if (b1Bytes == null) { if (b2Bytes == null) { continue; } else { return 1; } } else if (b2Bytes == null) { return -1; } int result; if(ascending[j]) { result = orderingRules[j].compare(b1Bytes, b2Bytes); } else { result = orderingRules[j].compare(b2Bytes, b1Bytes); } if(result != 0) { return result; } } // If we've gotten here, then we can't tell a difference between the sets // of available values, so sort based on entry ID if its in the key. if(b1Pos + 8 <= b1.length && b2Pos + 8 <= b2.length) { long b1ID = 0; for (int i = b1Pos; i < b1Pos + 8; i++) { b1ID <<= 8; b1ID |= (b1[i] & 0xFF); } long b2ID = 0; for (int i = b2Pos; i < b2Pos + 8; i++) { b2ID <<= 8; b2ID |= (b2[i] & 0xFF); } long idDifference = (b1ID - b2ID); if (idDifference < 0) { return -1; } else if (idDifference > 0) { return 1; } else { return 0; } } // If we've gotten here, then we can't tell the difference between the sets // of available values and entry IDs are not all available, so just return // 0 return 0; } /** * Compares the contents in the provided values set with the given values to * determine their relative order. A null value is always considered greater * then a non null value. If all attribute values are the same, the entry ID * will be used to determine the ordering. * * If the given attribute values array does not contain all the values in the * sort order, any missing values will be considered as a unknown or * wildcard value instead of a nonexistant value. When comparing partial * information, only values available in both the values set and the * given values will be used to determine the ordering. If all available * information is the same, 0 will be returned. * * @param set The sort values set to containing the values. * @param index The index of the values in the set. * @param entryID The entry ID to use in the comparasion. * @param values The values to use in the comparasion. * * @return A negative integer if the values in the set should come before * the given values in ascending order, a positive integer if * the values in the set should come after the given values in * ascending order, or zero if there is no difference between the * values with regard to ordering. * @throws DatabaseException If an error occurs during an operation on a * JE database. * @throws DirectoryException If an error occurs while trying to * normalize the value (e.g., if it is * not acceptable for use with the * associated equality matching rule). */ public int compare(SortValuesSet set, int index, long entryID, AttributeValue[] values) throws DatabaseException, DirectoryException { for (int j=0; j < orderingRules.length; j++) { if(j >= values.length) { break; } ByteString b1Bytes = set.getValue((index * orderingRules.length) + j); ByteString b2Bytes = null; if(values[j] != null) { b2Bytes = values[j].getNormalizedValue(); } // A null value will always come after a non-null value. if (b1Bytes == null) { if (b2Bytes == null) { continue; } else { return 1; } } else if (b2Bytes == null) { return -1; } int result; if(ascending[j]) { result = orderingRules[j].compareValues(b1Bytes, b2Bytes); } else { result = orderingRules[j].compareValues(b2Bytes, b1Bytes); } if(result != 0) { return result; } } if(entryID != -1) { // If we've gotten here, then we can't tell a difference between the sets // of values, so sort based on entry ID. long idDifference = (set.getEntryIDs()[index] - entryID); if (idDifference < 0) { return -1; } else if (idDifference > 0) { return 1; } else { return 0; } } // If we've gotten here, then we can't tell the difference between the sets // of available values and the entry ID is not available. Just return 0. return 0; } }