/* * Copyright 2012 NGDATA nv * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lilyproject.repository.impl.hbase; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import org.apache.hadoop.hbase.filter.WritableByteArrayComparable; import org.apache.hadoop.hbase.util.Bytes; public class ContainsValueComparator extends WritableByteArrayComparable { private byte[] nestingLevelAndValue; private int offset; /** * Nullary constructor, for Writable */ public ContainsValueComparator() { super(); } /** * Constructor. * */ public ContainsValueComparator(byte[] nestingLevelAndValue) { this.nestingLevelAndValue = nestingLevelAndValue; } @Override public byte[] getValue() { return nestingLevelAndValue; } @Override public void readFields(DataInput in) throws IOException { nestingLevelAndValue = Bytes.readByteArray(in); } @Override public void write(DataOutput out) throws IOException { Bytes.writeByteArray(out, nestingLevelAndValue); } @Override /** * Checks if a blob key (ourStoreKey) is contained in the blob field (theirValue). * The blob field can be a multivalue and / or hierarchical field. * * <p>IMPORTANT: This implementation depends on the byte encodings from ValueTypeImpl, BlobValueType and DataOutputImpl. * Any changes there have an impact on this implementation. */ public int compareTo(byte[] theirValue, int fromOffset, int length) { byte[] ourStoreKey = Bytes.tail(nestingLevelAndValue, nestingLevelAndValue.length-Bytes.SIZEOF_INT); if (theirValue == null && ourStoreKey == null) { return 0; } if (length == 0 && ourStoreKey.length == 0) { return 0; } if (length < ourStoreKey.length) { return -1; } if (theirValue[fromOffset] == (byte)(1)) { // First byte indicates if it was deleted or not return -1; } int nestingLevel = Bytes.toInt(nestingLevelAndValue); offset = fromOffset + 1; return compareBlob(nestingLevel, ourStoreKey, theirValue); } private int compareBlob(int nestingLevel, byte[] ourStoreKey, byte[] theirValue) { int compareTo = -1; if (0 == nestingLevel) { compareTo = compareBlob(ourStoreKey, theirValue); if (0 == compareTo) { return 0; } skipRestOfBlob(theirValue); } else { int count = readInt(theirValue); // Number of elements in the list or path for (int i = 0; i < count; i++) { compareTo = compareBlob(nestingLevel-1, ourStoreKey, theirValue); if (0 == compareTo) { return 0; } } } return compareTo; } /** * Compares the value of the blob with ourStoreKey */ private int compareBlob(byte[] ourStoreKey, byte[] theirValue) { offset++; // Skip the encoding byte. Currently there is only one encoding version so we can ignore it. int blobValueLength = readVInt(theirValue); // Length of the blob value int compareTo = Bytes.compareTo(ourStoreKey, 0, ourStoreKey.length, theirValue, offset , blobValueLength); offset += blobValueLength; return compareTo; } /** * Skips the rest of the blob (media type, blob size, name) and puts the offset to the next value to be read */ private void skipRestOfBlob(byte[] theirValue) { int mediaTypeLength = readInt(theirValue); // Length of the blob media type (offset = offset + blobvaluelength_size + blobvalue_size) offset += mediaTypeLength; offset += 8; // Blob size (long) int nameLength = readInt(theirValue); // Length of the blob name (offset = offset + blobvaluelength_size + blobvalue_size + mediaTypeLength_size + mediaType_size + blobsize_long) offset += nameLength; } private int readInt(byte[] bytes) { return ((bytes[offset++] & 0xFF) << 24) | ((bytes[offset++] & 0xFF) << 16) | ((bytes[offset++] & 0xFF) << 8) | (bytes[offset++] & 0xFF); } /** * Reads an int stored in variable-length format. Reads between one and * five bytes. Smaller values take fewer bytes. Negative numbers are not * supported. */ public int readVInt(byte[] bytes) { byte b = bytes[offset++]; int i = b & 0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { b = bytes[offset++]; i |= (b & 0x7F) << shift; } return i; } }