package sk.stuba.fiit.perconik.core.persistence.data;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
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.Resource;
import sk.stuba.fiit.perconik.core.Resources;
import sk.stuba.fiit.perconik.core.persistence.InvalidResourceException;
import sk.stuba.fiit.perconik.core.persistence.MarkableRegistration;
import sk.stuba.fiit.perconik.core.persistence.RegistrationMarker;
import sk.stuba.fiit.perconik.core.persistence.serialization.SerializedResourceData;
import sk.stuba.fiit.perconik.core.services.Services;
import sk.stuba.fiit.perconik.core.services.resources.ResourceProvider;
import static com.google.common.collect.Sets.newHashSet;
/**
* Markable resource registration with lively updated registration status.
*
* <p><b>Note:</b> This implementation is truly serializable if and only
* if the underlying resource is serializable. Otherwise this implementation
* serializes resource's data necessary to obtain the resource from the core
* resource provider after deserialization at runtime.
*
* @author Pavol Zbell
* @since 1.0
*/
public final class ResourcePersistenceData extends AbstractResourceRegistration implements MarkableRegistration, RegistrationMarker<ResourcePersistenceData>, Serializable, SerializedResourceData {
private static final long serialVersionUID = 6677144113746518278L;
private final transient boolean registered;
private final transient Class<? extends Listener> type;
private final transient String name;
private final transient Optional<Resource<?>> resource;
private ResourcePersistenceData(final boolean registered, final Class<? extends Listener> type, final String name, final Optional<Resource<?>> resource) {
this.registered = registered;
this.type = type;
this.name = name;
this.resource = resource;
}
static ResourcePersistenceData construct(final boolean registered, final Class<? extends Listener> type, final String name, @Nullable final Resource<?> resource) {
Utilities.checkListenerType(type);
Utilities.checkResourceName(name);
Utilities.checkResourceImplementation(name, resource);
return copy(registered, type, name, resource);
}
static ResourcePersistenceData copy(final boolean registered, final Class<? extends Listener> type, final String name, @Nullable final Resource<?> resource) {
return new ResourcePersistenceData(registered, type, name, Utilities.<Resource<?>>serializableOrNullAsOptional(resource));
}
public static <L extends Listener> ResourcePersistenceData of(final Class<L> type, final String name) {
return of(type, Unsafe.cast(type, Resources.forName(name)));
}
public static <L extends Listener> ResourcePersistenceData of(final Class<L> type, final Resource<? super L> resource) {
return construct(Resources.isRegistered(type, resource), type, resource.getName(), resource);
}
public static Set<ResourcePersistenceData> defaults() {
ResourceProvider provider = Services.getResourceService().getResourceProvider();
Set<ResourcePersistenceData> data = newHashSet();
for (Class<? extends Listener> type: provider.types()) {
for (Resource<?> resource: provider.forType(type)) {
data.add(construct(Utilities.registeredByDefault(resource.getClass()), type, resource.getName(), resource));
}
}
return data;
}
public static Set<ResourcePersistenceData> snapshot() {
ResourceProvider provider = Services.getResourceService().getResourceProvider();
Set<ResourcePersistenceData> data = newHashSet();
for (Class<? extends Listener> type: provider.types()) {
for (Resource<?> resource: provider.forType(type)) {
data.add(construct(Resources.isRegistered(type, resource), type, resource.getName(), resource));
}
}
return data;
}
private static final class SerializationProxy implements Serializable {
private static final long serialVersionUID = 4583906053454610999L;
private final boolean registered;
private final String type;
private final String name;
@Nullable
private final Optional<Resource<?>> resource;
private SerializationProxy(final ResourcePersistenceData data) {
this.registered = data.hasRegistredMark();
this.type = data.getListenerType().getName();
this.name = data.getResourceName();
this.resource = data.getSerializedResource();
}
static SerializationProxy of(final ResourcePersistenceData data) {
return new SerializationProxy(data);
}
private Object readResolve() throws InvalidObjectException {
try {
return construct(this.registered, Utilities.resolveClassAsSubclass(this.type, Listener.class), this.name, this.resource.orNull());
} catch (Exception e) {
throw new InvalidResourceException("Unknown deserialization error", e);
}
}
}
@SuppressWarnings({"static-method", "unused"})
private void readObject(final ObjectInputStream in) throws InvalidObjectException {
throw new InvalidResourceException("Serialization proxy required");
}
private Object writeReplace() {
return SerializationProxy.of(this);
}
public ResourcePersistenceData applyRegisteredMark() {
Resource<?> resource = this.getResource();
if (resource == null) {
return this;
}
boolean status = Resources.isRegistered(this.type, resource);
if (this.registered == status) {
return this;
}
if (this.registered) {
Unsafe.register(this.type, resource);
} else {
Unsafe.unregister(this.type, resource);
}
return new ResourcePersistenceData(status, this.type, this.name, this.resource);
}
public ResourcePersistenceData updateRegisteredMark() {
return this.markRegistered(this.isRegistered());
}
public ResourcePersistenceData markRegistered(final boolean status) {
if (this.registered == status) {
return this;
}
return new ResourcePersistenceData(status, this.type, this.name, this.resource);
}
public boolean hasRegistredMark() {
return this.registered;
}
public boolean hasSerializedResource() {
return this.resource.isPresent();
}
public Class<? extends Listener> getListenerType() {
return this.type;
}
public Resource<?> getResource() {
if (this.hasSerializedResource()) {
return this.resource.get();
}
return Resources.forName(this.name);
}
public String getResourceName() {
return this.name;
}
public Optional<Resource<?>> getSerializedResource() {
return this.resource;
}
}