/* * Copyright © 2015 Cask Data, Inc. * * 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 co.cask.cdap.data2.dataset2.lib.table; import co.cask.cdap.api.common.Bytes; import com.google.common.primitives.Ints; import com.google.common.primitives.Longs; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; /** * Metadata entry key */ public final class MDSKey { private final byte[] key; /** * @param key bytearray as constructed by {@code MDSKey.Builder} */ public MDSKey(byte[] key) { this.key = key; } /** * @return the underlying key */ public byte[] getKey() { return key; } /** * Splits the keys into a {@link Splitter} which exposes the parts that comprise the key. */ public Splitter split() { return new Splitter(key); } /** * Decodes the keys parts, as specified (int, long, byte[], String) */ public static final class Splitter { private final ByteBuffer byteBuffer; private Splitter(byte[] bytes) { byteBuffer = ByteBuffer.wrap(bytes); } /** * @throws BufferUnderflowException if there is no int as expected * @return the next int part in the splitter */ public int getInt() { return byteBuffer.getInt(); } /** * @throws BufferUnderflowException if there is no long as expected * @return the next long part in the splitter */ public long getLong() { return byteBuffer.getLong(); } /** * @throws BufferUnderflowException if there is no byte[] as expected * @return the next byte[] part in the splitter */ public byte[] getBytes() { int len = byteBuffer.getInt(); if (byteBuffer.remaining() < len) { throw new BufferUnderflowException(); } byte[] bytes = new byte[len]; byteBuffer.get(bytes, 0, len); return bytes; } /** * @throws BufferUnderflowException if there is no String as expected * @return the next String part in the splitter */ public String getString() { return Bytes.toString(getBytes()); } /** * skips the next int part in the splitter * @throws BufferUnderflowException if there is no int as expected */ public void skipInt() { forward(Ints.BYTES); } /** * skips the next long part in the splitter * @throws BufferUnderflowException if there is no long as expected */ public void skipLong() { forward(Longs.BYTES); } /** * skips the next byte[] part in the splitter * @throws BufferUnderflowException if there is no byte[] as expected */ public void skipBytes() { int len = byteBuffer.getInt(); forward(len); } /** * skips the next String part in the splitter * @throws BufferUnderflowException if there is no String as expected */ public void skipString() { skipBytes(); } private void forward(int count) { int position = byteBuffer.position(); byteBuffer.position(position + count); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MDSKey that = (MDSKey) o; return Bytes.equals(this.key, that.key); } @Override public int hashCode() { return Bytes.hashCode(key); } /** * Builds {@link MDSKey}s. */ public static final class Builder { private byte[] key; public Builder() { key = new byte[0]; } public Builder(MDSKey start) { this.key = start.getKey(); } // Encodes parts of the key with segments of <length> <value> public Builder add(byte[] part) { key = Bytes.add(key, Bytes.toBytes(part.length), part); return this; } public Builder add(String part) { add(Bytes.toBytes(part)); return this; } public Builder add(String... parts) { for (String part : parts) { add(part); } return this; } public Builder add(long part) { key = Bytes.add(key, Bytes.toBytes(part)); return this; } public Builder add(int part) { key = Bytes.add(key, Bytes.toBytes(part)); return this; } public Builder append(MDSKey mdsKey) { key = Bytes.add(key, mdsKey.getKey()); return this; } public MDSKey build() { return new MDSKey(key); } } }