package org.codefx.libfx.serialization;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;
/**
* Convenience class to wrap an {@link Optional} for serialization. Instances of this class are immutable.
* <p>
* Note that it does not provide any of the methods {@code Optional} has as its only goal is to enable serialization.
* But it holds a reference to the {@code Optional} which was used to create it (can be accessed with
* {@link #asOptional()}). This {@code Optional} instance is of course reconstructed on deserialization, so it will not
* be the same as the one specified for its creation.
* <p>
* The class can be used as an argument or return type for serialization-based RPC technologies like RMI.
* <p>
* There are three ways to use this class to serialize instances which have an optional field.
* </p>
* <h2>Transform On Serialization</h2>
* <p>
* The field can be declared as {@code transient Optional<T> optionalField}, which will exclude it from serialization.
* <p>
* The class then needs to implement custom (de)serialization methods {@code writeObject} and {@code readObject}. They
* must transform the {@code optionalField} to a {@code SerializableOptional} when writing the object and after reading
* such an instance transform it back to an {@code Optional}.
* </p>
* <h3>Example</h3>
*
* <pre>
* private void writeObject(ObjectOutputStream out) throws IOException {
* out.defaultWriteObject();
* out.writeObject(
* SerializableOptional.fromOptional(optionalField));
* }
*
* private void readObject(ObjectInputStream in)
* throws IOException, ClassNotFoundException {
*
* in.defaultReadObject();
* optionalField =
* ((SerializableOptional<T>) in.readObject()).toOptional();
* }
* </pre>
*
* <h2>Transform On Replace</h2>
* <p>
* If the class is serialized using the Serialization Proxy Pattern (see <i>Effective Java, 2nd Edition</i> by Joshua
* Bloch, Item 78), the proxy can have an instance of {@link SerializableOptional} to clearly denote the field as being
* optional.
* <p>
* In this case, the proxy needs to transform the {@code Optional} to {@code SerializableOptional} in its constructor
* (using {@link SerializableOptional#fromOptional(Optional)}) and the other way in {@code readResolve()} (with
* {@link SerializableOptional#asOptional()}).
* </p>
* <h2>Transform On Access</h2>
* <p>
* The field can be declared as {@code SerializableOptional<T> optionalField}. This will include it in the
* (de)serialization process so it does not need to be customized.
* <p>
* But methods interacting with the field need to get an {@code Optional} instead. This can easily be done by writing
* the accessor methods such that they transform the field on each access.
* <p>
* Note that {@link #asOptional()} simply returns the {@code Optional} which with this instance was created so no
* constructor needs to be invoked.
* </p>
* <h3>Example</h3>
* <p>
* Note that it is rarely useful to expose an optional field via accessor methods. Hence the following are private and
* for use inside the class.
*
* <pre>
* private Optional<T> getOptionalField() {
* return optionalField.asOptional();
* }
*
* private void setOptionalField(Optional<T> optionalField) {
* this.optionalField = SerializableOptional.fromOptional(optionalField);
* }
* </pre>
*
* @param <T>
* the type of the wrapped value
*/
public final class SerializableOptional<T extends Serializable> implements Serializable {
// FIELDS
private static final long serialVersionUID = -652697447004597911L;
/**
* The wrapped {@link Optional}. Note that this field is transient so it will not be (de)serializd automatically.
*/
private final Optional<T> optional;
// CONSTRUCTION AND TRANSFORMATION
/**
* Creates a new instance. Private to enforce use of {@link #fromOptional(Optional)}.
*
* @param optional
* the wrapped {@link Optional}
*/
private SerializableOptional(Optional<T> optional) {
Objects.requireNonNull(optional, "The argument 'optional' must not be null.");
this.optional = optional;
}
/**
* Creates a serializable optional from the specified optional.
*
* @param <T>
* the type of the wrapped value
* @param optional
* the {@link Optional} from which the serializable wrapper will be created
* @return a {@link SerializableOptional} which wraps the specified optional
*/
public static <T extends Serializable> SerializableOptional<T> fromOptional(Optional<T> optional) {
return new SerializableOptional<>(optional);
}
/**
* Creates a serializable optional which wraps an empty optional.
*
* @param <T>
* the type of the non-existent value
* @return a {@link SerializableOptional} which wraps an {@link Optional#empty() empty} {@link Optional}
* @see Optional#of(Object)
*/
public static <T extends Serializable> SerializableOptional<T> empty() {
return new SerializableOptional<>(Optional.empty());
}
/**
* Creates a serializable optional for the specified value by wrapping it in an {@link Optional}.
*
* @param <T>
* the type of the wrapped value
* @param value
* the value which will be contained in the wrapped {@link Optional}; must be non-null
* @return a {@link SerializableOptional} which wraps the an optional for the specified value
* @throws NullPointerException
* if {@code value} is null
* @see Optional#of(Object)
*/
public static <T extends Serializable> SerializableOptional<T> of(T value) throws NullPointerException {
return new SerializableOptional<>(Optional.of(value));
}
/**
* Creates a serializable optional for the specified value by wrapping it in an {@link Optional}.
*
* @param <T>
* the type of the wrapped value
* @param value
* the value which will be contained in the wrapped {@link Optional}; may be null
* @return a {@link SerializableOptional} which wraps the an optional for the specified value
* @see Optional#ofNullable(Object)
*/
public static <T extends Serializable> SerializableOptional<T> ofNullable(T value) {
return new SerializableOptional<>(Optional.ofNullable(value));
}
/**
* Returns the {@code Optional} instance with which this instance was created.
*
* @return this instance as an {@link Optional}
*/
public Optional<T> asOptional() {
return optional;
}
// SERIALIZATION
/**
* Implements the "write part" of the Serialization Proxy Pattern by creating a proxy which will be serialized
* instead of this instance.
*
* @return the {@link SerializationProxy}
*/
private Object writeReplace() {
return new SerializationProxy<>(this);
}
/**
* Since this class should never be deserialized directly, this method should not be called. If it is, someone
* purposely created a serialization of this class to bypass that mechanism, so throw an exception.
*
* @param in
* the {@link ObjectInputStream} from which the instance should be read
* @throws InvalidObjectException
* always throws this exception
*/
@SuppressWarnings({ "static-method", "unused" })
private void readObject(ObjectInputStream in) throws InvalidObjectException {
throw new InvalidObjectException("Serialization proxy expected.");
}
/**
* The proxy which is serialized instead of an instance of {@link SerializableOptional}.
*
* @param <T>
* the type of the wrapped value
*/
private static class SerializationProxy<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -1326520485869949065L;
/**
* This value is (de)serialized. It comes from the {@link Optional} wrapped by the {@code SerializableOptional}.
*/
private final T value;
/**
* Creates a new serialization proxy for the specified serializable optional.
*
* @param serializableOptional
* the {@link SerializableOptional} for which this proxy is created
*/
public SerializationProxy(SerializableOptional<T> serializableOptional) {
value = serializableOptional.asOptional().orElse(null);
}
/**
* Implements the "read part" of the Serialization Proxy Pattern by creating a serializable optional for the
* deserialized value.
*
* @return a {@link SerializableOptional}
*/
private Object readResolve() {
return SerializableOptional.ofNullable(value);
}
}
}