/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.hbase.io; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicReference; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.io.Writable; import org.apache.hadoop.util.ReflectionUtils; /** * A Writable Map. * Like {@link org.apache.hadoop.io.MapWritable} but dumb. It will fail * if passed a value type that it has not already been told about. Its been * primed with hbase Writables and byte []. Keys are always byte arrays. * * @param <K> <byte []> key TODO: Parameter K is never used, could be removed. * @param <V> value Expects a Writable or byte []. */ @InterfaceAudience.Private public class HbaseMapWritable <K,V> implements SortedMap<byte[],V>, Configurable, Writable, CodeToClassAndBack{ private AtomicReference<Configuration> conf = null; protected SortedMap<byte [], V> instance = null; /** * The default contructor where a TreeMap is used **/ public HbaseMapWritable(){ this (new TreeMap<byte [], V>(Bytes.BYTES_COMPARATOR)); } /** * Contructor where another SortedMap can be used * * @param map the SortedMap to be used */ public HbaseMapWritable(SortedMap<byte[], V> map){ conf = new AtomicReference<Configuration>(); instance = map; } /** @return the conf */ public Configuration getConf() { return conf.get(); } /** @param conf the conf to set */ public void setConf(Configuration conf) { this.conf.set(conf); } public void clear() { instance.clear(); } public boolean containsKey(Object key) { return instance.containsKey(key); } public boolean containsValue(Object value) { return instance.containsValue(value); } public Set<Entry<byte [], V>> entrySet() { return instance.entrySet(); } public V get(Object key) { return instance.get(key); } public boolean isEmpty() { return instance.isEmpty(); } public Set<byte []> keySet() { return instance.keySet(); } public int size() { return instance.size(); } public Collection<V> values() { return instance.values(); } public void putAll(Map<? extends byte [], ? extends V> m) { this.instance.putAll(m); } public V remove(Object key) { return this.instance.remove(key); } public V put(byte [] key, V value) { return this.instance.put(key, value); } public Comparator<? super byte[]> comparator() { return this.instance.comparator(); } public byte[] firstKey() { return this.instance.firstKey(); } public SortedMap<byte[], V> headMap(byte[] toKey) { return this.instance.headMap(toKey); } public byte[] lastKey() { return this.instance.lastKey(); } public SortedMap<byte[], V> subMap(byte[] fromKey, byte[] toKey) { return this.instance.subMap(fromKey, toKey); } public SortedMap<byte[], V> tailMap(byte[] fromKey) { return this.instance.tailMap(fromKey); } // Writable /** @return the Class class for the specified id */ @SuppressWarnings("boxing") protected Class<?> getClass(byte id) { return CODE_TO_CLASS.get(id); } /** @return the id for the specified Class */ @SuppressWarnings("boxing") protected byte getId(Class<?> clazz) { Byte b = CLASS_TO_CODE.get(clazz); if (b == null) { throw new NullPointerException("Nothing for : " + clazz); } return b; } /** * @see java.lang.Object#toString() */ @Override public String toString() { return this.instance.toString(); } public void write(DataOutput out) throws IOException { // Write out the number of entries in the map out.writeInt(this.instance.size()); // Then write out each key/value pair for (Map.Entry<byte [], V> e: instance.entrySet()) { Bytes.writeByteArray(out, e.getKey()); Byte id = getId(e.getValue().getClass()); out.writeByte(id); Object value = e.getValue(); if (value instanceof byte []) { Bytes.writeByteArray(out, (byte [])value); } else { ((Writable)value).write(out); } } } @SuppressWarnings("unchecked") public void readFields(DataInput in) throws IOException { // First clear the map. Otherwise we will just accumulate // entries every time this method is called. this.instance.clear(); // Read the number of entries in the map int entries = in.readInt(); // Then read each key/value pair for (int i = 0; i < entries; i++) { byte [] key = Bytes.readByteArray(in); byte id = in.readByte(); Class clazz = getClass(id); V value = null; if (clazz.equals(byte [].class)) { byte [] bytes = Bytes.readByteArray(in); value = (V)bytes; } else { Writable w = (Writable)ReflectionUtils. newInstance(clazz, getConf()); w.readFields(in); value = (V)w; } this.instance.put(key, value); } } }