/* * Copyright (c) 2013 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.common.core.io; import java.io.Serializable; import java.util.Objects; import org.w3c.dom.Element; import eu.esdihumboldt.hale.common.core.io.extension.ComplexValueDefinition; import eu.esdihumboldt.hale.common.core.io.extension.ComplexValueExtension; import eu.esdihumboldt.hale.common.core.io.impl.ComplexValue; import eu.esdihumboldt.hale.common.core.io.impl.SimpleValue; import eu.esdihumboldt.hale.common.core.io.impl.StringValue; /** * A simple or complex value with either a {@link String} or DOM {@link Element} * representation for serializing it.<br> * <br> * A DOM representation must be linked to a corresponding complex value type * registered in the {@link ComplexValueExtension} to be convertible to an * object. * * @author Simon Templer */ public abstract class Value implements Serializable { private static final long serialVersionUID = 1628156172357895991L; /** * Null value. */ public static final Value NULL = new Value() { private static final long serialVersionUID = -1264492211623753778L; @Override public <T> T as(Class<T> expectedType) { return null; } @Override public <T> T as(Class<T> expectedType, T defValue) { return defValue; } @Override public Object getValue() { return null; } @Override public boolean isEmpty() { return true; } @Override public boolean isRepresentedAsDOM() { return false; } @Override public Element getDOMRepresentation() { return null; } @Override public String getStringRepresentation() { return null; } }; private static final Value TRUE = new SimpleValue(true); private static final Value FALSE = new SimpleValue(false); /** * Create a value from a string. * * @param str the string * @return the value wrapper */ public static Value of(String str) { if (str == null) { return Value.NULL; } return new StringValue(str); } /** * Create a value from a boolean. * * @param bool the boolean value * @return the value wrapper */ public static Value of(Boolean bool) { if (bool == null) { return NULL; } else if (bool) { return TRUE; } else { return FALSE; } } /** * Create a value from a number. * * @param number the number value * @return the value wrapper */ public static Value of(Number number) { if (number == null) { return NULL; } return new SimpleValue(number); } /** * Create a simple value with a string representation for serialization. * * @param value the value * @return the value wrapper */ public static Value simple(Object value) { if (value == null) { return NULL; } return new SimpleValue(value); } /** * Create a complex value with a DOM representation for serialization. * * @param value the value * @return the value wrapper */ public static Value complex(Object value) { if (value == null) { return NULL; } return new ComplexValue(value); } /** * Create a value from an object. If a complex value representation is found * in {@link ComplexValueExtension} a complex value is created, otherwise a * simple value. * * @see #complex(Object) * @see #simple(Object) * @param object the object to wrap * @return the value wrapper */ public static Value of(Object object) { if (object == null) { return NULL; } // check if there is a complex value definition for the object ComplexValueDefinition def = ComplexValueExtension.getInstance() .getDefinition(object.getClass()); if (def != null) { return Value.complex(object); } else { return Value.simple(object); } } /** * Get the value as the expected type if possible. * * @param expectedType the expected value type, this must be either * {@link String}, DOM {@link Element} or a complex value type * defined in the {@link ComplexValueExtension} * @return the value as the expected type or <code>null</code> if it could * not be created/converted */ public abstract <T> T as(Class<T> expectedType); /** * Get the value as the expected type if possible, a default value * otherwise. * * @param expectedType the expected value type, this must be either * {@link String}, DOM {@link Element} or a complex value type * defined in the {@link ComplexValueExtension} * @param defValue the default value to use if the value is * <code>null</code> or cannot be converted to the expected type * @return the value as the expected type or the given default value if it * could not be created/converted */ public abstract <T> T as(Class<T> expectedType, T defValue); /** * Get the value as the given type. * * @param type the type to convert the value to * @return the value converted to the type or <code>null</code> for a null * value * @throws IllegalArgumentException if the value cannot be converted to the * given type */ @SuppressWarnings("unchecked") public <T> T asType(Class<T> type) { // check for null if (getValue() == null) { return null; } // for String target, use toString() if (String.class.equals(type)) { return (T) toString(); } T obj = as(type); if (obj == null) { throw new IllegalArgumentException("Value could not be converted to " + type); } return obj; } /** * Get the internal value.<br> * <br> * In most cases it is more appropriate to use {@link #getValue()} instead. * * @return the internal value */ public abstract Object getValue(); /** * Determines if the value is empty. * * @return <code>true</code> if the value is <code>null</code> or another * kind of empty (e.g. empty string) depending on the value type, * <code>false</code> otherwise */ public abstract boolean isEmpty(); /** * Determines if the value is represented as DOM {@link Element} for * serializing it. * * @return if the value can be represented as {@link Element} * @see #getDOMRepresentation() * @see #getStringRepresentation() */ public abstract boolean isRepresentedAsDOM(); /** * Convenience method to determine if this value has a simple * representation. * * @return if this value has a simple representation */ public boolean isSimple() { return !isRepresentedAsDOM(); } /** * Convenience method to determine if this value has a complex * representation. * * @return if this value has a complex representation */ public boolean isComplex() { return isRepresentedAsDOM(); } /** * Get the value's DOM representation if applicable. * * @return an {@link Element} representing the value or <code>null</code> if * {@link #isRepresentedAsDOM()} yields <code>false</code> * @see #isRepresentedAsDOM() */ public abstract Element getDOMRepresentation(); /** * Get the value's string representation. * * @return a string representing the value or <code>null</code> if * {@link #isRepresentedAsDOM()} yields <code>true</code> * @see #isRepresentedAsDOM() */ public abstract String getStringRepresentation(); @Override public boolean equals(Object obj) { if (!(obj instanceof Value)) return false; Value other = (Value) obj; if (!other.isRepresentedAsDOM() && !isRepresentedAsDOM()) { /* * Both represented as String, use string representation to compare */ return Objects.equals(other.getStringRepresentation(), getStringRepresentation()); } // in any other case, compare the internal values return Objects.equals(other.getValue(), getValue()); } @Override public int hashCode() { if (!isRepresentedAsDOM()) { /* * If represented through a string, use the string hash code. */ return Objects.hashCode(getStringRepresentation()); } // in any other case, use the internal value hash code return Objects.hashCode(getValue()); } @Override public String toString() { String def = "Value<null>"; Object value = getValue(); if (value != null) { def = value.toString(); } return as(String.class, def); } }