package sk.stuba.fiit.perconik.utilities.configuration;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Map;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Maps.newLinkedHashMap;
/**
* TODO
*
* @author Pavol Zbell
* @since 1.0
*/
public abstract class MapOptions extends AbstractMapOptions implements Serializable {
private static final long serialVersionUID = -6372082702258295853L;
final transient Putter putter;
MapOptions(final Map<String, Object> map, final Putter putter) {
super(map);
this.putter = checkNotNull(putter);
}
public static MapOptions empty() {
return EmptyHolder.options;
}
public static MapOptions create() {
return new Regular(StandardPutter.instance);
}
public static MapOptions create(final Putter putter) {
return new Regular(putter);
}
public static MapOptions copyOf(final Options options) {
if (options instanceof MapOptions) {
return copyOf((MapOptions) options);
}
return from(options.toMap());
}
public static MapOptions copyOf(final MapOptions options) {
if (options instanceof Immutable) {
return options;
}
return from(options.map, options.putter);
}
public static MapOptions from(final Map<String, Object> map) {
return from(map, StandardPutter.instance);
}
public static MapOptions from(final Map<String, Object> map, final Putter putter) {
if (map instanceof ImmutableMap) {
return new Immutable(map, putter);
}
return new Regular(map, putter);
}
public static MapOptions view(final Map<String, Object> map) {
return view(map, StandardPutter.instance);
}
public static MapOptions view(final Map<String, Object> map, final Putter putter) {
if (map instanceof ImmutableMap) {
return new Immutable(map, putter);
}
return new View(map, putter);
}
private static final class Regular extends MapOptions {
private static final long serialVersionUID = 1704716560859767601L;
Regular(final Putter putter) {
super(Maps.<String, Object>newLinkedHashMap(), putter);
}
Regular(final Map<String, Object> map, final Putter putter) {
super(newLinkedHashMap(map), putter);
}
@Override
public Object put(final String key, @Nullable final Object value) {
return this.putter.put(this.map, key, value);
}
public Map<String, Object> toMap() {
return newLinkedHashMap(this.map);
}
private static final class SerializationProxy extends AbstractSerializationProxy<Regular> {
private static final long serialVersionUID = -7140194869918852030L;
SerializationProxy(final Regular options) {
super(options);
}
@Override
Regular construct(final Map<String, Object> map, final Putter putter) {
return new Regular(map, putter);
}
}
@Override
Object writeReplace() {
return new SerializationProxy(this);
}
}
private static final class Immutable extends MapOptions {
private static final long serialVersionUID = -3797590867166061284L;
Immutable(final Map<String, Object> map, final Putter putter) {
super(ImmutableMap.copyOf(map), putter);
}
@Override
public Object put(final String key, final Object value) {
throw new UnsupportedOperationException();
}
public Map<String, Object> toMap() {
return this;
}
private static final class SerializationProxy extends AbstractSerializationProxy<Immutable> {
private static final long serialVersionUID = -2172898611082615023L;
SerializationProxy(final Immutable options) {
super(options);
}
@Override
Immutable construct(final Map<String, Object> map, final Putter putter) {
return new Immutable(map, putter);
}
}
@Override
Object writeReplace() {
return new SerializationProxy(this);
}
}
private static final class View extends MapOptions {
private static final long serialVersionUID = 1031140282957202002L;
View(final Map<String, Object> map, final Putter putter) {
super(map, putter);
}
@Override
public Object put(final String key, @Nullable final Object value) {
return this.putter.put(this.map, key, value);
}
public Map<String, Object> toMap() {
return this.map;
}
private static final class SerializationProxy extends AbstractSerializationProxy<View> {
private static final long serialVersionUID = 5730743428732201474L;
SerializationProxy(final View options) {
super(options);
}
@Override
View construct(final Map<String, Object> map, final Putter putter) {
return new View(map, putter);
}
}
@Override
Object writeReplace() {
return new SerializationProxy(this);
}
}
public interface Putter {
public Object put(final Map<String, Object> map, final String key, @Nullable final Object value);
}
private enum StandardPutter implements Putter {
instance;
public Object put(final Map<String, Object> map, final String key, @Nullable final Object value) {
checkNotNull(map);
try {
return map.put(key, value);
} catch (RuntimeException e) {
throw new IllegalOptionException(e);
}
}
}
private static final class EmptyHolder {
static final MapOptions options = new Immutable(ImmutableMap.<String, Object>of(), StandardPutter.instance);
private EmptyHolder() {}
}
static abstract class AbstractSerializationProxy<T extends MapOptions> implements Serializable {
private static final long serialVersionUID = 8169680327325018850L;
private final Map<String, Object> map;
private final Putter putter;
AbstractSerializationProxy(final T options) {
this.map = options.map;
this.putter = options.putter;
}
abstract T construct(Map<String, Object> map, Putter putter);
final Object readResolve() throws InvalidObjectException {
try {
return this.construct(this.map, this.putter);
} catch (Exception e) {
throw new InvalidObjectException("Unknown deserialization error");
}
}
}
@SuppressWarnings({"static-method", "unused"})
final void readObject(final ObjectInputStream in) throws InvalidObjectException {
throw new InvalidObjectException("Serialization proxy required");
}
abstract Object writeReplace();
public Putter putter() {
return this.putter;
}
}