All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.astyanax.model.AbstractComposite Maven / Gradle / Ivy

There is a newer version: 3.10.2
Show newest version
package com.netflix.astyanax.model;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.apache.cassandra.utils.ByteBufferUtil;

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableClassToInstanceMap;
import com.netflix.astyanax.Serializer;
import com.netflix.astyanax.serializers.AsciiSerializer;
import com.netflix.astyanax.serializers.BigIntegerSerializer;
import com.netflix.astyanax.serializers.BooleanSerializer;
import com.netflix.astyanax.serializers.ByteBufferOutputStream;
import com.netflix.astyanax.serializers.ByteBufferSerializer;
import com.netflix.astyanax.serializers.ComparatorType;
import com.netflix.astyanax.serializers.IntegerSerializer;
import com.netflix.astyanax.serializers.LongSerializer;
import com.netflix.astyanax.serializers.SerializerTypeInferer;
import com.netflix.astyanax.serializers.StringSerializer;
import com.netflix.astyanax.serializers.UUIDSerializer;

/**
 * Parent class of Composite and DynamicComposite. Acts as a list of objects
 * that get serialized into a composite column name. Unless
 * setAutoDeserialize(true) is called, it's going to try to match serializers to
 * Cassandra comparator types.
 * 
 * @author edanuff
 */
@SuppressWarnings("rawtypes")
public abstract class AbstractComposite extends AbstractList implements Comparable {

    public enum ComponentEquality {
        LESS_THAN_EQUAL((byte) -1), EQUAL((byte) 0), GREATER_THAN_EQUAL((byte) 1);

        private final byte equality;

        ComponentEquality(byte equality) {
            this.equality = equality;
        }

        public byte toByte() {
            return equality;
        }

        public static ComponentEquality fromByte(byte equality) {
            if (equality > 0) {
                return GREATER_THAN_EQUAL;
            }
            if (equality < 0) {
                return LESS_THAN_EQUAL;
            }
            return EQUAL;
        }
    }

    static final Logger logger = Logger.getLogger(AbstractComposite.class.getName());

    public static final BiMap, String> DEFAULT_SERIALIZER_TO_COMPARATOR_MAPPING = new ImmutableBiMap.Builder, String>()
            .put(AsciiSerializer.class,      AsciiSerializer.get().getComparatorType().getTypeName())
            .put(BigIntegerSerializer.class, BigIntegerSerializer.get().getComparatorType().getTypeName())
            .put(LongSerializer.class,       LongSerializer.get().getComparatorType().getTypeName())
            .put(IntegerSerializer.class,    IntegerSerializer.get().getComparatorType().getTypeName())
            .put(BooleanSerializer.class,    BooleanSerializer.get().getComparatorType().getTypeName())
            .put(StringSerializer.class,     StringSerializer.get().getComparatorType().getTypeName())
            .put(UUIDSerializer.class,       UUIDSerializer.get().getComparatorType().getTypeName()).build();

    static final ImmutableClassToInstanceMap SERIALIZERS = new ImmutableClassToInstanceMap.Builder()
            .put(IntegerSerializer.class,    IntegerSerializer.get())
            .put(BooleanSerializer.class,    BooleanSerializer.get())
            .put(AsciiSerializer.class,      AsciiSerializer.get())
            .put(BigIntegerSerializer.class, BigIntegerSerializer.get())
            .put(ByteBufferSerializer.class, ByteBufferSerializer.get())
            .put(LongSerializer.class,       LongSerializer.get())
            .put(StringSerializer.class,     StringSerializer.get())
            .put(UUIDSerializer.class,       UUIDSerializer.get()).build();

    public static final BiMap DEFAULT_ALIAS_TO_COMPARATOR_MAPPING = new ImmutableBiMap.Builder()
            .put((byte) 'a', ComparatorType.ASCIITYPE.getTypeName())
            .put((byte) 'b', ComparatorType.BYTESTYPE.getTypeName())
            .put((byte) 'i', ComparatorType.INTEGERTYPE.getTypeName())
            .put((byte) 'x', ComparatorType.LEXICALUUIDTYPE.getTypeName())
            .put((byte) 'l', ComparatorType.LONGTYPE.getTypeName())
            .put((byte) 't', ComparatorType.TIMEUUIDTYPE.getTypeName())
            .put((byte) 's', ComparatorType.UTF8TYPE.getTypeName())
            .put((byte) 'u', ComparatorType.UUIDTYPE.getTypeName()).build();

    BiMap, String> serializerToComparatorMapping = DEFAULT_SERIALIZER_TO_COMPARATOR_MAPPING;

    BiMap aliasToComparatorMapping = DEFAULT_ALIAS_TO_COMPARATOR_MAPPING;

    final boolean dynamic;

    List> serializersByPosition = null;
    List comparatorsByPosition = null;

    public class Component {
        final Serializer serializer;
        final T value;
        final ByteBuffer bytes;
        final String comparator;
        final ComponentEquality equality;

        public Component(T value, ByteBuffer bytes, Serializer serializer, String comparator,
                ComponentEquality equality) {
            this.serializer = serializer;
            this.value = value;
            this.bytes = bytes;
            this.comparator = comparator;
            this.equality = equality;
        }

        public Serializer getSerializer() {
            return serializer;
        }

        @SuppressWarnings("unchecked")
        public  A getValue(Serializer s) {
            if (s == null) {
                s = (Serializer) serializer;
            }
            if ((value == null) && (bytes != null) && (s != null)) {
                ByteBuffer cb = bytes.duplicate();
                if (cb.hasRemaining()) {
                    return s.fromByteBuffer(cb);
                }
            }
            if (value instanceof ByteBuffer) {
                return (A) ((ByteBuffer) value).duplicate();
            }
            return (A) value;
        }

        public T getValue() {
            return getValue(serializer);
        }

        @SuppressWarnings("unchecked")
        public  ByteBuffer getBytes(Serializer s) {
            if (bytes == null) {
                if (value instanceof ByteBuffer) {
                    return ((ByteBuffer) value).duplicate();
                }

                if (value == null) {
                    return null;
                }

                if (s == null) {
                    s = (Serializer) serializer;
                }
                if (s != null) {
                    return s.toByteBuffer((A) value).duplicate();
                }

            }

            return bytes.duplicate();
        }

        public ByteBuffer getBytes() {
            return getBytes(serializer);
        }

        public String getComparator() {
            return comparator;
        }

        public ComponentEquality getEquality() {
            return equality;
        }

        @Override
        public String toString() {
            return "Component [" + getValue() + "]";
        }
    }

    List> components = new ArrayList>();

    ByteBuffer serialized = null;

    public AbstractComposite(boolean dynamic) {
        this.dynamic = dynamic;
    }

    public AbstractComposite(boolean dynamic, Object... o) {
        this.dynamic = dynamic;
        this.addAll(Arrays.asList(o));
    }

    public AbstractComposite(boolean dynamic, List l) {
        this.dynamic = dynamic;
        this.addAll(l);
    }

    public List> getComponents() {
        return components;
    }

    public void setComponents(List> components) {
        serialized = null;
        this.components = components;
    }

    public Map, String> getSerializerToComparatorMapping() {
        return serializerToComparatorMapping;
    }

    public void setSerializerToComparatorMapping(Map, String> serializerToComparatorMapping) {
        serialized = null;
        this.serializerToComparatorMapping = new ImmutableBiMap.Builder, String>().putAll(
                serializerToComparatorMapping).build();
    }

    public Map getAliasesToComparatorMapping() {
        return aliasToComparatorMapping;
    }

    public void setAliasesToComparatorMapping(Map aliasesToComparatorMapping) {
        serialized = null;
        aliasToComparatorMapping = new ImmutableBiMap.Builder().putAll(aliasesToComparatorMapping)
                .build();
    }

    public boolean isDynamic() {
        return dynamic;
    }

    public List> getSerializersByPosition() {
        return serializersByPosition;
    }

    public void setSerializersByPosition(List> serializersByPosition) {
        this.serializersByPosition = serializersByPosition;
    }

    public void setSerializersByPosition(Serializer... serializers) {
        serializersByPosition = Arrays.asList(serializers);
    }

    public void setSerializerByPosition(int index, Serializer s) {
        if (serializersByPosition == null) {
            serializersByPosition = new ArrayList>();
        }
        while (serializersByPosition.size() <= index) {
            serializersByPosition.add(null);
        }
        serializersByPosition.set(index, s);
    }

    public List getComparatorsByPosition() {
        return comparatorsByPosition;
    }

    public void setComparatorsByPosition(List comparatorsByPosition) {
        this.comparatorsByPosition = comparatorsByPosition;
    }

    public void setComparatorsByPosition(String... comparators) {
        comparatorsByPosition = Arrays.asList(comparators);
    }

    public void setComparatorByPosition(int index, String c) {
        if (comparatorsByPosition == null) {
            comparatorsByPosition = new ArrayList();
        }
        while (comparatorsByPosition.size() <= index) {
            comparatorsByPosition.add(null);
        }
        comparatorsByPosition.set(index, c);
    }

    @Override
    public int compareTo(AbstractComposite o) {
        return serialize().compareTo(o.serialize());
    }

    private String comparatorForSerializer(Serializer s) {
        String comparator = serializerToComparatorMapping.get(s.getClass());
        if (comparator != null) {
            return comparator;
        }
        return ComparatorType.BYTESTYPE.getTypeName();
    }

    private Serializer serializerForComparator(String c) {
        int p = c.indexOf('(');
        if (p >= 0) {
            c = c.substring(0, p);
        }
        if (ComparatorType.LEXICALUUIDTYPE.getTypeName().equals(c)
                || ComparatorType.TIMEUUIDTYPE.getTypeName().equals(c)) {
            return UUIDSerializer.get();
        }

        Serializer s = SERIALIZERS.getInstance(serializerToComparatorMapping.inverse().get(c));
        if (s != null) {
            return s;
        }
        return ByteBufferSerializer.get();
    }

    private Serializer serializerForPosition(int i) {
        if (serializersByPosition == null) {
            return null;
        }
        if (i >= serializersByPosition.size()) {
            return null;
        }
        return serializersByPosition.get(i);
    }

    private Serializer getSerializer(int i, String c) {
        Serializer s = serializerForPosition(i);
        if (s != null) {
            return s;
        }
        return serializerForComparator(c);
    }

    private String comparatorForPosition(int i) {
        if (comparatorsByPosition == null) {
            return null;
        }
        if (i >= comparatorsByPosition.size()) {
            return null;
        }
        return comparatorsByPosition.get(i);
    }

    private String getComparator(int i, ByteBuffer bb) {
        String name = comparatorForPosition(i);
        if (name != null) {
            return name;
        }
        if (!dynamic) {
            if (bb.hasRemaining()) {
                return ComparatorType.BYTESTYPE.getTypeName();
            }
            else {
                return null;
            }
        }
        if (bb.hasRemaining()) {
            try {
                int header = getShortLength(bb);
                if ((header & 0x8000) == 0) {
                    name = ByteBufferUtil.string(getBytes(bb, header));
                }
                else {
                    byte a = (byte) (header & 0xFF);
                    name = aliasToComparatorMapping.get(a);
                    if (name == null) {
                        a = (byte) Character.toUpperCase((char) a);
                        name = aliasToComparatorMapping.get(a);
                        if (name != null) {
                            name += "(reversed=true)";
                        }
                    }
                }
            }
            catch (CharacterCodingException e) {
                throw new RuntimeException(e);
            }
        }
        if ((name != null) && (name.length() == 0)) {
            name = null;
        }
        return name;
    }

    @Override
    public void clear() {
        serialized = null;
        components = new ArrayList>();
    }

    @Override
    public int size() {
        return components.size();
    }

    public  AbstractComposite addComponent(T value, Serializer s) {

        addComponent(value, s, comparatorForSerializer(s));

        return this;

    }

    public  AbstractComposite addComponent(T value, Serializer s, ComponentEquality equality) {

        addComponent(value, s, comparatorForSerializer(s), equality);

        return this;

    }

    public  AbstractComposite addComponent(T value, Serializer s, String comparator) {

        addComponent(value, s, comparator, ComponentEquality.EQUAL);

        return this;

    }

    public  AbstractComposite addComponent(T value, Serializer s, String comparator, ComponentEquality equality) {

        addComponent(-1, value, s, comparator, equality);

        return this;

    }

    @SuppressWarnings("unchecked")
    public  AbstractComposite addComponent(int index, T value, Serializer s, String comparator,
            ComponentEquality equality) {
        serialized = null;

        if (index < 0) {
            index = components.size();
        }

        while (components.size() < index) {
            components.add(null);
        }
        components.add(index, new Component(value, null, s, comparator, equality));

        return this;

    }

    private static Object mapIfNumber(Object o) {
        if ((o instanceof Byte) || (o instanceof Integer) || (o instanceof Short)) {
            return BigInteger.valueOf(((Number) o).longValue());
        }
        return o;
    }

    @SuppressWarnings({ "unchecked" })
    private static Collection flatten(Collection c) {
        if (c instanceof AbstractComposite) {
            return ((AbstractComposite) c).getComponents();
        }
        boolean hasCollection = false;
        for (Object o : c) {
            if (o instanceof Collection) {
                hasCollection = true;
                break;
            }
        }
        if (!hasCollection) {
            return c;
        }
        List newList = new ArrayList();
        for (Object o : c) {
            if (o instanceof Collection) {
                newList.addAll(flatten((Collection) o));
            }
            else {
                newList.add(o);
            }
        }
        return newList;
    }

    @Override
    public boolean addAll(Collection c) {
        return super.addAll(flatten(c));
    }

    @Override
    public boolean containsAll(Collection c) {
        return super.containsAll(flatten(c));
    }

    @Override
    public boolean removeAll(Collection c) {
        return super.removeAll(flatten(c));
    }

    @Override
    public boolean retainAll(Collection c) {
        return super.retainAll(flatten(c));
    }

    @Override
    public boolean addAll(int i, Collection c) {
        return super.addAll(i, flatten(c));
    }

    @SuppressWarnings("unchecked")
    @Override
    public void add(int index, Object element) {
        serialized = null;

        if (element instanceof Component) {
            components.add(index, (Component) element);
            return;
        }

        element = mapIfNumber(element);
        Serializer s = serializerForPosition(index);
        if (s == null) {
            s = SerializerTypeInferer.getSerializer(element);
        }
        String c = comparatorForPosition(index);
        if (c == null) {
            c = comparatorForSerializer(s);
        }
        components.add(index, new Component(element, null, s, c, ComponentEquality.EQUAL));
    }

    @Override
    public Object remove(int index) {
        serialized = null;
        Component prev = components.remove(index);
        if (prev != null) {
            return prev.getValue();
        }
        return null;
    }

    public  AbstractComposite setComponent(int index, T value, Serializer s) {

        setComponent(index, value, s, comparatorForSerializer(s));

        return this;

    }

    public  AbstractComposite setComponent(int index, T value, Serializer s, String comparator) {

        setComponent(index, value, s, comparator, ComponentEquality.EQUAL);

        return this;

    }

    @SuppressWarnings("unchecked")
    public  AbstractComposite setComponent(int index, T value, Serializer s, String comparator,
            ComponentEquality equality) {
        serialized = null;

        while (components.size() <= index) {
            components.add(null);
        }
        components.set(index, new Component(value, null, s, comparator, equality));

        return this;

    }

    @SuppressWarnings("unchecked")
    @Override
    public Object set(int index, Object element) {
        serialized = null;

        if (element instanceof Component) {
            Component prev = components.set(index, (Component) element);
            if (prev != null) {
                return prev.getValue();
            }
            return null;
        }

        element = mapIfNumber(element);
        Serializer s = serializerForPosition(index);
        if (s == null) {
            s = SerializerTypeInferer.getSerializer(element);
        }
        String c = comparatorForPosition(index);
        if (c == null) {
            c = comparatorForSerializer(s);
        }
        Component prev = components.set(index, new Component(element, null, s, c, ComponentEquality.EQUAL));
        if (prev != null) {
            return prev.getValue();
        }
        return null;
    }

    @Override
    public Object get(int i) {
        Component c = components.get(i);
        if (c != null) {
            return c.getValue();
        }
        return null;
    }

    public  T get(int i, Serializer s) throws ClassCastException {
        T value = null;
        Component c = components.get(i);
        if (c != null) {
            value = c.getValue(s);
        }
        return value;
    }

    public Component getComponent(int i) {
        if (i >= components.size()) {
            return null;
        }
        Component c = components.get(i);
        return c;
    }

    public Iterator> componentsIterator() {
        return components.iterator();
    }

    @SuppressWarnings("unchecked")
    public ByteBuffer serialize() {
        if (serialized != null) {
            return serialized.duplicate();
        }

        ByteBufferOutputStream out = new ByteBufferOutputStream();

        int i = 0;
        for (Component c : components) {
            Serializer s = serializerForPosition(i);
            ByteBuffer cb = c.getBytes(s);
            if (cb == null) {
                cb = ByteBuffer.allocate(0);
            }

            if (dynamic) {
                String comparator = comparatorForPosition(i);
                if (comparator == null) {
                    comparator = c.getComparator();
                }
                if (comparator == null) {
                    comparator = ComparatorType.BYTESTYPE.getTypeName();
                }
                int p = comparator.indexOf("(reversed=true)");
                boolean desc = false;
                if (p >= 0) {
                    comparator = comparator.substring(0, p);
                    desc = true;
                }
                if (aliasToComparatorMapping.inverse().containsKey(comparator)) {
                    byte a = aliasToComparatorMapping.inverse().get(comparator);
                    if (desc) {
                        a = (byte) Character.toUpperCase((char) a);
                    }
                    out.writeShort((short) (0x8000 | a));
                }
                else {
                    out.writeShort((short) comparator.length());
                    out.write(ByteBufferUtil.bytes(comparator));
                }
            }
            out.writeShort((short) cb.remaining());
            out.write(cb.slice());
            out.write(c.getEquality().toByte());
            i++;
        }

        serialized = out.getByteBuffer();
        return serialized.duplicate();
    }

    @SuppressWarnings("unchecked")
    public void deserialize(ByteBuffer b) {
        serialized = b.duplicate();
        components = new ArrayList>();

        String comparator = null;
        int i = 0;
        while ((comparator = getComparator(i, b)) != null) {
            ByteBuffer data = getWithShortLength(b);
            if (data != null) {
                Serializer s = getSerializer(i, comparator);
                ComponentEquality equality = ComponentEquality.fromByte(b.get());
                components.add(new Component(null, data.slice(), s, comparator, equality));
            }
            else {
                throw new RuntimeException("Missing component data in composite type");
            }
            i++;
        }

    }

    protected static int getShortLength(ByteBuffer bb) {
        int length = (bb.get() & 0xFF) << 8;
        return length | (bb.get() & 0xFF);
    }

    protected static ByteBuffer getBytes(ByteBuffer bb, int length) {
        ByteBuffer copy = bb.duplicate();
        copy.limit(copy.position() + length);
        bb.position(bb.position() + length);
        return copy;
    }

    protected static ByteBuffer getWithShortLength(ByteBuffer bb) {
        int length = getShortLength(bb);
        return getBytes(bb, length);
    }

}