package sk.stuba.fiit.perconik.core.persistence.data;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.base.Optional;
import sk.stuba.fiit.perconik.core.Listener;
import sk.stuba.fiit.perconik.core.Listeners;
import sk.stuba.fiit.perconik.core.persistence.InvalidListenerException;
import sk.stuba.fiit.perconik.core.persistence.MarkableRegistration;
import sk.stuba.fiit.perconik.core.persistence.RegistrationMarker;
import sk.stuba.fiit.perconik.core.persistence.serialization.SerializedListenerData;
import sk.stuba.fiit.perconik.core.services.Services;
import sk.stuba.fiit.perconik.core.services.listeners.ListenerProvider;
import static com.google.common.collect.Sets.newHashSet;
/**
* Markable listener registration with lively updated registration status.
*
* <p><b>Note:</b> This implementation is truly serializable if and only
* if the underlying listener is serializable. Otherwise this implementation
* serializes listener's data necessary to obtain the listener from the core
* listener provider after deserialization at runtime.
*
* @author Pavol Zbell
* @since 1.0
*/
public final class ListenerPersistenceData extends AbstractListenerRegistration implements MarkableRegistration, RegistrationMarker<ListenerPersistenceData>, Serializable, SerializedListenerData {
private static final long serialVersionUID = -1672202405264953995L;
private final transient boolean registered;
private final transient Class<? extends Listener> implementation;
private final transient Optional<Listener> listener;
private ListenerPersistenceData(final boolean registered, final Class<? extends Listener> implementation, final Optional<Listener> listener) {
this.registered = registered;
this.implementation = implementation;
this.listener = listener;
}
static ListenerPersistenceData construct(final boolean registered, final Class<? extends Listener> implementation, @Nullable final Listener listener) {
Utilities.checkListenerClass(implementation);
Utilities.checkListenerImplementation(implementation, listener);
return copy(registered, implementation, listener);
}
static ListenerPersistenceData copy(final boolean registered, final Class<? extends Listener> implementation, @Nullable final Listener listener) {
return new ListenerPersistenceData(registered, implementation, Utilities.serializableOrNullAsOptional(listener));
}
public static ListenerPersistenceData of(final Listener listener) {
return construct(Listeners.isRegistered(listener), listener.getClass(), listener);
}
public static Set<ListenerPersistenceData> defaults() {
ListenerProvider provider = Services.getListenerService().getListenerProvider();
Set<ListenerPersistenceData> data = newHashSet();
for (Class<? extends Listener> implementation: provider.classes()) {
data.add(construct(Utilities.registeredByDefault(implementation), implementation, null));
}
return data;
}
public static Set<ListenerPersistenceData> snapshot() {
ListenerProvider provider = Services.getListenerService().getListenerProvider();
Set<ListenerPersistenceData> data = newHashSet();
Collection<Listener> listeners = Listeners.registrations().values();
for (Class<? extends Listener> implementation: provider.classes()) {
for (Listener listener: listeners) {
data.add(construct(implementation == listener.getClass(), implementation, listener));
}
}
return data;
}
private static final class SerializationProxy implements Serializable {
private static final long serialVersionUID = -6638506142325802066L;
private final boolean registered;
private final String implementation;
private final Optional<Listener> listener;
private SerializationProxy(final ListenerPersistenceData data) {
this.registered = data.hasRegistredMark();
this.implementation = data.getListenerClass().getName();
this.listener = data.getSerializedListener();
}
static SerializationProxy of(final ListenerPersistenceData data) {
return new SerializationProxy(data);
}
private Object readResolve() throws InvalidObjectException {
try {
return construct(this.registered, Utilities.resolveClassAsSubclass(this.implementation, Listener.class), this.listener.orNull());
} catch (Exception e) {
throw new InvalidListenerException("Unknown deserialization error", e);
}
}
}
@SuppressWarnings({"static-method", "unused"})
private void readObject(final ObjectInputStream in) throws InvalidObjectException {
throw new InvalidListenerException("Serialization proxy required");
}
private Object writeReplace() {
return SerializationProxy.of(this);
}
public ListenerPersistenceData applyRegisteredMark() {
Listener listener = this.getListener();
if (listener == null) {
return this;
}
boolean status = Listeners.isRegistered(listener);
if (this.registered == status) {
return this;
}
if (this.registered) {
Listeners.register(listener);
} else {
Listeners.unregister(listener);
}
return new ListenerPersistenceData(status, this.implementation, this.listener);
}
public ListenerPersistenceData updateRegisteredMark() {
return this.markRegistered(this.isRegistered());
}
public ListenerPersistenceData markRegistered(final boolean status) {
if (this.registered == status) {
return this;
}
return new ListenerPersistenceData(status, this.implementation, this.listener);
}
public boolean hasRegistredMark() {
return this.registered;
}
public boolean hasSerializedListener() {
return this.listener.isPresent();
}
public Listener getListener() {
if (this.hasSerializedListener()) {
return this.listener.get();
}
return Listeners.forClass(this.implementation);
}
public Class<? extends Listener> getListenerClass() {
return this.implementation;
}
public Optional<Listener> getSerializedListener() {
return this.listener;
}
}