/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma; import java.io.Serializable; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; public class Value implements Serializable, Comparable<Value> { private static final long serialVersionUID = 779426587031645153L; private static final Serializable NULL = "org.obiba.magma.Value.NULL".intern(); @NotNull private final ValueType valueType; @NotNull private final ValueLoader valueLoader; private transient int hashCode; Value(@NotNull ValueType valueType, @Nullable Serializable value) { this(valueType, new StaticValueLoader(value)); } @SuppressWarnings({ "NullableProblems", "ConstantConditions" }) Value(@NotNull ValueType valueType, @Nullable ValueLoader valueLoader) { if(valueType == null) throw new IllegalArgumentException("valueType cannot be null"); this.valueType = valueType; this.valueLoader = valueLoader == null ? new StaticValueLoader(null) : valueLoader; } @NotNull public Value copy() { return valueType.valueOf(valueLoader.getValue()); } @NotNull public ValueType getValueType() { return valueType; } @NotNull public Object getValue() { if(isNull()) { throw new NullPointerException("Value is null"); } return valueLoader.getValue(); } public boolean isNull() { return valueLoader.isNull(); } public long getLength() { if(isNull()) return 0; try { return valueLoader.getLength(); } catch(UnsupportedOperationException e) { // fallback to the length of the string representation of the value String str = toString(); return str == null ? 0 : str.length(); } } /** * Returns true when this {@code Value} instance holds a sequence of other {@code Value} instances. In this situation, * the {@code ValueType} of this {@code Value} is the same as the {@code ValueType} of the items in the sequence. That * is if the sequence holds {@code Value} instances of type {@code TextType}, then this {@code Value} also has * {@code TextType} as its {@code ValueType}. * * @return true when this {@code Value} holds a sequence of other {@code Value} instances */ public boolean isSequence() { return false; } /** * Returns a {@code ValueSequence} view of this {@code Value} when {@code #isSequence()} returns true. * * @return */ @SuppressWarnings("ClassReferencesSubclass") @NotNull public ValueSequence asSequence() { throw new IllegalStateException("value is not a sequence"); } @Nullable @Override public String toString() { return getValueType().toString(this); } @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj == null) { return false; } if(getClass() != obj.getClass()) { return false; } Value other = (Value) obj; // Shortcut Object val = valueLoader.getValue(); Object otherVal = other.valueLoader.getValue(); //noinspection SimplifiableIfStatement if(val == otherVal) { return true; } return val.equals(otherVal) && valueType.equals(other.valueType); } @Override public int hashCode() { if(hashCode == 0) { int prime = 31; int result = 1; result = prime * result + valueLoader.getValue().hashCode(); result = prime * result + valueType.hashCode(); hashCode = result; } return hashCode; } @Override public int compareTo(Value o) { return valueType.compare(this, o); } public static class StaticValueLoader implements ValueLoader { private static final long serialVersionUID = 8195664792459648506L; @NotNull private final Serializable value; public StaticValueLoader(@Nullable Serializable value) { this.value = value == null ? NULL : value; } @Override public boolean isNull() { return value == null || NULL.equals(value); } @Override @NotNull public Object getValue() { return value; } @Override public long getLength() { if(isNull()) return 0; throw new UnsupportedOperationException(); } } }