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

com.bazaarvoice.emodb.sor.delta.impl.LiteralImpl Maven / Gradle / Ivy

The newest version!
package com.bazaarvoice.emodb.sor.delta.impl;

import com.bazaarvoice.emodb.common.json.JsonValidator;
import com.bazaarvoice.emodb.common.json.OrderedJson;
import com.bazaarvoice.emodb.sor.delta.DeltaVisitor;
import com.bazaarvoice.emodb.sor.delta.Literal;
import com.bazaarvoice.emodb.sor.delta.deser.DeltaJson;
import com.google.common.base.Throwables;
import com.google.common.io.CharStreams;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Longs;

import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class LiteralImpl extends AbstractDelta implements Literal {

    @Nullable
    private final Object _value;

    public LiteralImpl(@Nullable Object value) {
        _value = JsonValidator.checkValid(value);
    }

    @Override
    @Nullable
    public Object getValue() {
        return _value;
    }

    @Override
    @Nullable
    public  V visit(DeltaVisitor visitor, @Nullable T context) {
        return visitor.visit(this, context);
    }

    @Override
    public boolean isConstant() {
        return true;
    }

    @Override
    public void appendTo(Appendable buf) throws IOException {
        // nested content is sorted so toString() is deterministic.  this is valuable in tests and for
        // comparing hashes of deltas for equality.
        DeltaJson.write(CharStreams.asWriter(buf), OrderedJson.ordered(_value));
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(@Nullable Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof Literal)) {
            return false;
        }

        Object that = ((Literal) obj).getValue();
        if (_value == null) {
            return that == null;
        } else if (that == null) {
            return false;
        }

        // Optimize for the common case where values being equated are the same type
        Class thisClass = _value.getClass();
        Class thatClass = that.getClass();

        if (!areClassesEqual(thisClass, thatClass)) {
            // If both values are numeric then use numeric equality regardless of the underlying type (int, double, and so on)
            if (isNumber(thisClass) && isNumber(thatClass)) {
                if (isDouble((Class) thisClass) || isDouble((Class) thatClass)) {
                    return ((Number) _value).doubleValue() == ((Number) that).doubleValue();
                } else {
                    return ((Number) _value).longValue() == ((Number) that).longValue();
                }
            }
        }

        return _value.equals(that);
    }

    @SuppressWarnings("unchecked")
    @Override
    public int compareTo(Literal o) {
        Object that = o.getValue();

        // Sort nulls first
        if (_value == null) {
            return that == null ? 0 : -1;
        } else if (that == null) {
            return 1;
        }

        // Optimize for common case where values being compared are of the same type
        Class thisClass = _value.getClass();
        Class thatClass = that.getClass();

        if (!areClassesEqual(thisClass, thatClass)) {
            // Values are of different types
            // If both values are numeric then use numeric comparison regardless of the underlying type (int, double, and so on)
            if (isNumber(thisClass) && isNumber(thatClass)) {
                if (isDouble((Class) thisClass) || isDouble((Class) thatClass)) {
                    return Doubles.compare(((Number) _value).doubleValue(), ((Number) that).doubleValue());
                } else {
                    return Longs.compare(((Number) _value).longValue(), ((Number) that).longValue());
                }
            }

            // Sort by simple class name
            return getComparisonClassName(thisClass).compareTo(getComparisonClassName(thatClass));
        }

        if (Comparable.class.isAssignableFrom(thisClass)) {
            return ((Comparable) _value).compareTo(that);
        }

        // When not comparable, compare the string representations
        try {
            StringBuilder thisStr = new StringBuilder();
            StringBuilder thatStr = new StringBuilder();
            this.appendTo(thisStr);
            o.appendTo(thatStr);
            return thisStr.toString().compareTo(thatStr.toString());
        } catch (IOException e) {
            // Should never happen
            throw Throwables.propagate(e);
        }

    }

    /**
     * When comparing literals allow subclasses of Maps and Lists to be directly compared even if they have
     * different implementations.
     */
    private boolean areClassesEqual(Class c1, Class c2) {
        if (isMap(c1)) {
            return isMap(c2);
        } else if (isList(c1)) {
            return isList(c2);
        } else {
            return c1.equals(c2);
        }
    }

    /**
     * When comparing literals with different types sort them by their simple class names.  Normalize numbers,
     * maps, and lists so that all values of each type are sorted together regardless of actual implementation
     * (Integer vs Float, HashMap vs TreeMap, and so on).
     */
    private String getComparisonClassName(Class clazz) {
        if (isNumber(clazz)) {
            return Number.class.getSimpleName();
        } else if (isMap(clazz)) {
            return Map.class.getSimpleName();
        } else if (isList(clazz)) {
            return List.class.getSimpleName();
        }
        return clazz.getSimpleName();
    }

    private boolean isNumber(Class clazz) {
        return Number.class.isAssignableFrom(clazz);
    }

    private boolean isDouble(Class clazz) {
        return Double.class.equals(clazz) || Float.class.equals(clazz);
    }

    private boolean isMap(Class clazz) {
        return Map.class.isAssignableFrom(clazz);
    }

    private boolean isList(Class clazz) {
        return List.class.isAssignableFrom(clazz);
    }

    @Override
    public int hashCode() {
        return 8971 ^ (_value != null ? _value.hashCode() : 0);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy