/** * 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.avro.mapred; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.WeakHashMap; import java.nio.ByteBuffer; import org.apache.avro.Schema; import org.apache.avro.AvroRuntimeException; import org.apache.avro.Schema.Type; import org.apache.avro.Schema.Field; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericContainer; import org.apache.avro.generic.IndexedRecord; import org.apache.avro.specific.SpecificData.SchemaConstructable; import org.apache.avro.reflect.ReflectData; /** A key/value pair. */ public class Pair<K,V> implements IndexedRecord, Comparable<Pair>, SchemaConstructable { private static final String PAIR = Pair.class.getName(); private static final String KEY = "key"; private static final String VALUE = "value"; private Schema schema; private K key; private V value; public Pair(Schema schema) { checkIsPairSchema(schema); this.schema = schema; } public Pair(K key, Schema keySchema, V value, Schema valueSchema) { this.schema = getPairSchema(keySchema, valueSchema); this.key = key; this.value = value; } private static void checkIsPairSchema(Schema schema) { if (!PAIR.equals(schema.getFullName())) throw new IllegalArgumentException("Not a Pair schema: "+schema); } /** Return a pair's key schema. */ public static Schema getKeySchema(Schema pair) { checkIsPairSchema(pair); return pair.getField(KEY).schema(); } /** Return a pair's value schema. */ public static Schema getValueSchema(Schema pair) { checkIsPairSchema(pair); return pair.getField(VALUE).schema(); } private static final Map<Schema,Map<Schema,Schema>> SCHEMA_CACHE = new WeakHashMap<Schema,Map<Schema,Schema>>(); /** Get a pair schema. */ public static Schema getPairSchema(Schema key, Schema value) { Map<Schema,Schema> valueSchemas; synchronized (SCHEMA_CACHE) { valueSchemas = SCHEMA_CACHE.get(key); if (valueSchemas == null) { valueSchemas = new WeakHashMap<Schema,Schema>(); SCHEMA_CACHE.put(key, valueSchemas); } Schema result; result = valueSchemas.get(value); if (result == null) { result = makePairSchema(key, value); valueSchemas.put(value, result); } return result; } } private static Schema makePairSchema(Schema key, Schema value) { Schema pair = Schema.createRecord(PAIR, null, null, false); List<Field> fields = new ArrayList<Field>(); fields.add(new Field(KEY, key, "", null)); fields.add(new Field(VALUE, value, "", null, Field.Order.IGNORE)); pair.setFields(fields); return pair; } @Override public Schema getSchema() { return schema; } /** Get the key. */ public K key() { return key; } /** Set the key. */ public void key(K key) { this.key = key; } /** Get the value. */ public V value() { return value; } /** Set the value. */ public void value(V value) { this.value = value; } /** Set both the key and value. */ public void set(K key, V value) { this.key = key; this.value = value; } @Override public boolean equals(Object o) { if (o == this) return true; // identical object if (!(o instanceof Pair)) return false; // not a pair Pair that = (Pair)o; if (!this.schema.equals(that.schema)) return false; // not the same schema return this.compareTo(that) == 0; } @Override public int hashCode() { return GenericData.get().hashCode(this, schema); } @Override public int compareTo(Pair that) { return GenericData.get().compare(this, that, schema); } @Override public String toString() { return GenericData.get().toString(this); } @Override public Object get(int i) { switch (i) { case 0: return key; case 1: return value; default: throw new org.apache.avro.AvroRuntimeException("Bad index: "+i); } } @Override @SuppressWarnings("unchecked") public void put(int i, Object o) { switch (i) { case 0: this.key = (K)o; break; case 1: this.value = (V)o; break; default: throw new org.apache.avro.AvroRuntimeException("Bad index: "+i); } } private static final Schema STRING_SCHEMA = Schema.create(Type.STRING); private static final Schema BYTES_SCHEMA = Schema.create(Type.BYTES); private static final Schema INT_SCHEMA = Schema.create(Type.INT); private static final Schema LONG_SCHEMA = Schema.create(Type.LONG); private static final Schema FLOAT_SCHEMA = Schema.create(Type.FLOAT); private static final Schema DOUBLE_SCHEMA = Schema.create(Type.DOUBLE); private static final Schema NULL_SCHEMA = Schema.create(Type.NULL); @SuppressWarnings("unchecked") public Pair(Object key, Object value) { this((K)key, getSchema(key), (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Object key, GenericContainer value) { this((K)key, getSchema(key), (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Object key, CharSequence value) { this((K)key, getSchema(key), (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, ByteBuffer value) { this((K)key, getSchema(key), (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, Integer value) { this((K)key, getSchema(key), (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, Long value) { this((K)key, getSchema(key), (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, Float value) { this((K)key, getSchema(key), (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, Double value) { this((K)key, getSchema(key), (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Object key, Void value) { this((K)key, getSchema(key), (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Object value) { this((K)key, key.getSchema(), (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, GenericContainer value) { this((K)key, key.getSchema(), (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, CharSequence value) { this((K)key, key.getSchema(), (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, ByteBuffer value) { this((K)key, key.getSchema(), (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Integer value) { this((K)key, key.getSchema(), (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Long value) { this((K)key, key.getSchema(), (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Float value) { this((K)key, key.getSchema(), (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Double value) { this((K)key, key.getSchema(), (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(GenericContainer key, Void value) { this((K)key, key.getSchema(), (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Object value) { this((K)key, STRING_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(CharSequence key, GenericContainer value) { this((K)key, STRING_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(CharSequence key, CharSequence value) { this((K)key, STRING_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, ByteBuffer value) { this((K)key, STRING_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Integer value) { this((K)key, STRING_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Long value) { this((K)key, STRING_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Float value) { this((K)key, STRING_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Double value) { this((K)key, STRING_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(CharSequence key, Void value) { this((K)key, STRING_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Object value) { this((K)key, BYTES_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, GenericContainer value) { this((K)key, BYTES_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, CharSequence value) { this((K)key, BYTES_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, ByteBuffer value) { this((K)key, BYTES_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Integer value) { this((K)key, BYTES_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Long value) { this((K)key, BYTES_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Float value) { this((K)key, BYTES_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Double value) { this((K)key, BYTES_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(ByteBuffer key, Void value) { this((K)key, BYTES_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Object value) { this((K)key, INT_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Integer key, GenericContainer value) { this((K)key, INT_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Integer key, CharSequence value) { this((K)key, INT_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, ByteBuffer value) { this((K)key, INT_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Integer value) { this((K)key, INT_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Long value) { this((K)key, INT_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Float value) { this((K)key, INT_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Double value) { this((K)key, INT_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Integer key, Void value) { this((K)key, INT_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Object value) { this((K)key, LONG_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Long key, GenericContainer value) { this((K)key, LONG_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Long key, CharSequence value) { this((K)key, LONG_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, ByteBuffer value) { this((K)key, LONG_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Integer value) { this((K)key, LONG_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Long value) { this((K)key, LONG_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Float value) { this((K)key, LONG_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Double value) { this((K)key, LONG_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Long key, Void value) { this((K)key, LONG_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Object value) { this((K)key, FLOAT_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Float key, GenericContainer value) { this((K)key, FLOAT_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Float key, CharSequence value) { this((K)key, FLOAT_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, ByteBuffer value) { this((K)key, FLOAT_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Integer value) { this((K)key, FLOAT_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Long value) { this((K)key, FLOAT_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Float value) { this((K)key, FLOAT_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Double value) { this((K)key, FLOAT_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Float key, Void value) { this((K)key, FLOAT_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Object value) { this((K)key, DOUBLE_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Double key, GenericContainer value) { this((K)key, DOUBLE_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Double key, CharSequence value) { this((K)key, DOUBLE_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, ByteBuffer value) { this((K)key, DOUBLE_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Integer value) { this((K)key, DOUBLE_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Long value) { this((K)key, DOUBLE_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Float value) { this((K)key, DOUBLE_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Double value) { this((K)key, DOUBLE_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Double key, Void value) { this((K)key, DOUBLE_SCHEMA, (V)value, NULL_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Object value) { this((K)key, NULL_SCHEMA, (V)value, getSchema(value)); } @SuppressWarnings("unchecked") public Pair(Void key, GenericContainer value) { this((K)key, NULL_SCHEMA, (V)value, value.getSchema()); } @SuppressWarnings("unchecked") public Pair(Void key, CharSequence value) { this((K)key, NULL_SCHEMA, (V)value, STRING_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, ByteBuffer value) { this((K)key, NULL_SCHEMA, (V)value, BYTES_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Integer value) { this((K)key, NULL_SCHEMA, (V)value, INT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Long value) { this((K)key, NULL_SCHEMA, (V)value, LONG_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Float value) { this((K)key, NULL_SCHEMA, (V)value, FLOAT_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Double value) { this((K)key, NULL_SCHEMA, (V)value, DOUBLE_SCHEMA); } @SuppressWarnings("unchecked") public Pair(Void key, Void value) { this((K)key, NULL_SCHEMA, (V)value, NULL_SCHEMA); } private static Schema getSchema(Object o) { try { return ReflectData.get().getSchema(o.getClass()); } catch (AvroRuntimeException e) { throw new AvroRuntimeException ("Cannot infer schema for : " + o.getClass() + ". Must create Pair with explicit key and value schemas.", e); } } // private static final String[][] TABLE = new String[][] { // {"Object", "getSchema({0})"}, // {"GenericContainer", "{0}.getSchema()"}, // {"CharSequence", "STRING_SCHEMA"}, // {"ByteBuffer", "BYTES_SCHEMA"}, // {"Integer", "INT_SCHEMA"}, // {"Long", "LONG_SCHEMA"}, // {"Float", "FLOAT_SCHEMA"}, // {"Double", "DOUBLE_SCHEMA"}, // {"Void", "NULL_SCHEMA"}, // }; // private static String f(String pattern, String value) { // return java.text.MessageFormat.format(pattern, value); // } // public static void main(String... args) throws Exception { // StringBuffer b = new StringBuffer(); // for (String[] k : TABLE) { // for (String[] v : TABLE) { // b.append("@SuppressWarnings(\"unchecked\")\n"); // b.append("public Pair("+k[0]+" key, "+v[0]+" value) {\n"); // b.append(" this((K)key, "+f(k[1],"key") // +", (V)value, "+f(v[1],"value")+");\n"); // b.append("}\n"); // } // } // System.out.println(b); // } }