org.xlrnet.tibaija.memory.Value.java Source code

Java tutorial

Introduction

Here is the source code for org.xlrnet.tibaija.memory.Value.java

Source

/*
 * Copyright (c) 2015 Jakob Hende
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE
 */

package org.xlrnet.tibaija.memory;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import org.apache.commons.math3.complex.Complex;
import org.jetbrains.annotations.NotNull;
import org.xlrnet.tibaija.exception.IllegalTypeException;
import org.xlrnet.tibaija.exception.TIArgumentException;
import org.xlrnet.tibaija.exception.TIRuntimeException;
import org.xlrnet.tibaija.util.ComplexComparator;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Holds any value Make sure to run a type-check using the built-in
 * methods of this class before querying its value.
 */
public class Value implements Comparable<Value> {

    public static final Value ZERO = Value.of(Complex.ZERO);

    public static final Value ONE = Value.of(Complex.ONE);

    public static final Value NEGATIVE_ONE = Value.of(Complex.valueOf(-1));

    public static final Value EMPTY_LIST = Value.of(new ArrayList<>());

    public static final Value EMPTY_STRING = Value.of("");

    private static final Comparator<Complex> complexComparator = new ComplexComparator();

    private final Object value;

    private final Variables.VariableType type;

    /**
     * Create a new Value object from a complex number and set the according type. Note: Numbers are always represented
     * as complex values!
     *
     * @param number
     *         The complex number.
     */
    private Value(@NotNull Complex number) {
        value = number;
        type = Variables.VariableType.NUMBER;
    }

    /**
     * Create a new Value object from a string and set the according type.
     *
     * @param string
     *         The string value.
     */
    private Value(@NotNull String string) {
        value = string;
        type = Variables.VariableType.STRING;
    }

    /**
     * Create a new Value object from an immutable list of complex numbers and set the according type. Note: Numbers
     * are  always represented as complex values!
     *
     * @param complexImmutableList
     *         The immutable list of complex numbers.
     */
    private Value(@NotNull ImmutableList<Complex> complexImmutableList) {
        value = complexImmutableList;
        type = Variables.VariableType.LIST;
    }

    /**
     * Create a new Value object from an immutable list of complex numbers.
     *
     * @param complexImmutableList
     *         The immutable list of complex numbers.
     */
    @NotNull
    public static Value of(@NotNull ImmutableList<Complex> complexImmutableList) {
        return new Value(complexImmutableList);
    }

    /**
     * Create a new string Value object from a {@link String}.
     *
     * @param string
     *         The string to store inside the new value. May not be null.
     */
    @NotNull
    public static Value of(@NotNull String string) {
        return new Value(string);
    }

    /**
     * Create a new Value object from a mutable list of complex numbers. The given objects will be transferred to a
     * separate immutable list.
     *
     * @param complexMutableList
     *         The immutable list of complex numbers.
     */
    @NotNull
    public static Value of(@NotNull List<Complex> complexMutableList) {
        return of(ImmutableList.copyOf(complexMutableList));
    }

    /**
     * Create a new Value object from a mutable array of complex numbers. The given objects will be transferred to a
     * separate immutable list.
     *
     * @param complexArray
     *         The immutable list of complex numbers.
     */
    @NotNull
    public static Value of(@NotNull Complex... complexArray) {
        return of(ImmutableList.copyOf(complexArray));
    }

    /**
     * Create a new Value object from a complex number. Note: Numbers are always represented as complex values!
     *
     * @param c
     *         The complex number.
     * @return A new Value object with the given complex number.
     */
    @NotNull
    public static Value of(@NotNull Complex c) {
        return new Value(c);
    }

    /**
     * Create a new Value object from a BigDecimal. This is recommended, since future versions might work with
     * BigDecimal implementations.
     *
     * @param bigDecimal
     *         A big decimal.
     * @return A new Value object with a real number.
     */
    @NotNull
    public static Value of(@NotNull BigDecimal bigDecimal) {
        return of(bigDecimal.doubleValue()); // We don't support BigDecimal yet :( -> casting to double
    }

    /**
     * Create a new Value object from a boolean. True is represented as 1, false will become 0.
     *
     * @param bool
     *         True or false.
     * @return A new Value object with the given boolean parameter represented as 1 or 0.
     */
    @NotNull
    public static Value of(boolean bool) {
        return of(bool ? 1 : 0);
    }

    /**
     * Create a new Value object from a real number.
     *
     * @param real
     *         A real number.
     * @return A new Value object with a real number.
     */
    @NotNull
    public static Value of(Number real) {
        return of(real, 0);
    }

    /**
     * Create a new Value object from a real number and an imaginary part.
     *
     * @param real
     *         A real number.
     * @param imaginary
     *         The imaginary part.
     * @return A new Value object with a full complex number.
     */
    @NotNull
    public static Value of(@NotNull Number real, @NotNull Number imaginary) {
        return of(Complex.valueOf(real.doubleValue(), imaginary.doubleValue()));
    }

    /**
     * Retrieves the internal value as a boolean value. If the internal is not a number, this method will throw an
     * {IllegalTypeException}. Use this method only if you know the underlying object type or want to let this method fail explicitly when being invoked with a wrong value type.
     *
     * @return The internal value as a boolean. If the numerical value is not zero, true will be returned. Otherwise
     * false.
     * @throws TIRuntimeException
     */
    public boolean bool() throws IllegalTypeException {
        Complex c = complex();
        return c.getReal() != 0 || c.getImaginary() != 0;
    }

    @Override
    public int compareTo(@NotNull Value o) {
        checkNotNull(o);

        if (Objects.equals(this, o))
            return 0;

        try {
            if (!(this.isNumber() || o.isNumber()))
                throw new IllegalTypeException("Comparison not supported for right type",
                        Variables.VariableType.NUMBER, type);
            return complexComparator.compare(this.complex(), o.complex());
        } catch (UnsupportedOperationException u) {
            throw new TIArgumentException("Illegal operation: " + u.getMessage(), this, o);
        }
    }

    /**
     * Retrieves the internal value as a Complex object. If the internal is not a Complex, this method will throw an
     * {@link IllegalTypeException}. Use this method only if you know the underlying object type or want to let this method fail explicitly when being invoked with a wrong value type.
     *
     * @return The internal value as a Complex object.
     * @throws TIRuntimeException
     */
    @NotNull
    public Complex complex() throws IllegalTypeException {
        internalTypeCheck(Variables.VariableType.NUMBER);
        return (Complex) value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof Value))
            return false;
        Value value1 = (Value) o;
        return com.google.common.base.Objects.equal(value, value1.value)
                && com.google.common.base.Objects.equal(type, value1.type);
    }

    @NotNull

    public Variables.VariableType getType() {
        return type;
    }

    /**
     * Returns the internal value without casting as a pure object.
     */
    @NotNull
    public Object getValue() {
        return value;
    }

    /**
     * Checks if the value is complex and has an imaginary value.
     *
     * @return True if the value is complex and has an imaginary value. False otherwise.
     */
    public boolean hasImaginaryValue() {
        return isType(Variables.VariableType.NUMBER) && complex().getImaginary() != 0;
    }

    @Override
    public int hashCode() {
        return com.google.common.base.Objects.hashCode(value, type);
    }

    /**
     * Check if this object contains a list.
     *
     * @return True if this object contains a list; false otherwise.
     */
    public boolean isList() {
        return isType(Variables.VariableType.LIST);
    }

    /**
     * Check if this object contains a complex or numerical value.
     *
     * @return True if this object contains a complex or numerical value; false otherwise.
     */
    public boolean isNumber() {
        return isType(Variables.VariableType.NUMBER);
    }

    /**
     * Check if this object contains a string value.
     *
     * @return True if this object contains a string value; false otherwise.
     */
    public boolean isString() {
        return isType(Variables.VariableType.STRING);
    }

    /**
     * Retrieves the internal value as a list of Complex objects. If the internal is not a Complex, this method will
     * throw an {@link IllegalTypeException}. Use this method only if you know the underlying object type or want to let this method fail explicitly when being invoked with a wrong value type.
     *
     * @return The internal value as a Complex object.
     * @throws TIRuntimeException
     */
    @NotNull
    @SuppressWarnings("unchecked")
    public ImmutableList<Complex> list() throws IllegalTypeException {
        internalTypeCheck(Variables.VariableType.LIST);
        return (ImmutableList<Complex>) value;
    }

    /**
     * Retrieves the real part of the internal complex value. If the internal is not a Complex, this method will throw an
     * {@link IllegalTypeException}. Use this method only if you know the underlying object type or want to let this method fail explicitly when being invoked with a wrong value type.
     *
     * @return The internal value as a Complex object.
     * @throws TIRuntimeException
     */
    public double realPart() throws IllegalTypeException {
        return complex().getReal();
    }

    /**
     * Retrieves the internal value as a boolean value. If the internal is not a number, this method will throw an
     * {@link IllegalTypeException}. Use this method only if you know the underlying object type or want to let this method fail explicitly when being invoked with a wrong value type.
     *
     * @return The internal value as a boolean. If the numerical value is not zero, true will be returned. Otherwise
     * false.
     * @throws TIRuntimeException
     */
    public String string() throws IllegalTypeException {
        internalTypeCheck(Variables.VariableType.STRING);
        return (String) value;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this).add("value", value).add("type", type).toString();
    }

    /**
     * Internal type check. Will throw an exception if the internal type is not equal to the given parameter.
     *
     * @param checkType
     *         The type to match.
     */
    private void internalTypeCheck(Variables.VariableType checkType) throws IllegalTypeException {
        if (!isType(checkType))
            throw new IllegalTypeException(-1, -1, "Illegal type cast", checkType, type);
    }

    /**
     * Internal method for type checking.
     *
     * @param checkedType
     *         The expected type.
     * @return True if the internal type is equal to the expected type. False otherwise.
     */
    private boolean isType(Variables.VariableType checkedType) {
        return Objects.equals(this.getType(), checkedType);
    }
}