cdc.util.enums.AbstractMask Maven / Gradle / Ivy
package cdc.util.enums;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import cdc.util.function.IterableUtils;
import cdc.util.function.Predicates;
import cdc.util.lang.Checks;
import cdc.util.lang.CollectionUtils;
/**
* Mask (set) of values belonging to a list type.
*
* Objects of this class are immutable.
*
* A typical implementation should look like this:
*
{@code
* public final class FooMask extends AbstractEnumMask {
* public static final Support SUPPORT = support(FooMask.class, FooMask::new, FooType, Nullable.FALSE);
*
* private FooMask(Support support,
* Set values) {
* super(support, values);
* }
* }
* }
* For a standard enum, one can declare:
* {@code
* public final class FooMask extends AbstractEnumMask {
* public static final Support SUPPORT = support(FooMask.class, FooMask::new, Foo.class, Nullable.FALSE);
*
* private FooMask(Support support,
* Set values) {
* super(support, values);
* }
* }
* }
*
* @author Damien Carbonne
* @param The mask type.
* @param The enum type.
*/
public class AbstractMask, V> implements Mask {
/**
* The associated support class.
*/
protected final MaskSupport support;
/**
* The mask values.
*/
protected final Set values;
protected AbstractMask(MaskSupport support,
Set values) {
Checks.isNotNull(support, "support");
this.support = support;
this.values = Collections.unmodifiableSet(values);
checkValues();
}
@FunctionalInterface
protected static interface Creator, V> {
public M create(MaskSupport support,
Set values);
}
/**
* Creates a Support implementation.
*
* @param The mask type.
* @param The enum type.
* @param maskClass The mask class.
* @param creator The mask factory.
* @param type The type.
* @param nullable {@code Nullable.TRUE} if {@code null} is a valid value.
* @return A Support implementation.
*/
protected static , V> MaskSupport support(Class maskClass,
Creator creator,
ListType type,
Nullable nullable) {
Checks.isNotNull(maskClass, "maskClass");
Checks.isNotNull(creator, "creator");
Checks.isNotNull(type, "type");
Checks.isNotNull(nullable, "nullable");
return new SupportImpl<>(maskClass, creator, type, nullable);
}
/**
* Creates a Support implementation for a standard enum.
*
* @param The mask type.
* @param The enum type.
* @param maskClass The mask class.
* @param creator The mask factory.
* @param enumClass The enum class.
* @param nullable {@code Nullable.TRUE} if {@code null} is a valid value.
* @return A Support implementation.
*/
protected static , V extends Enum> MaskSupport support(Class maskClass,
Creator creator,
Class enumClass,
Nullable nullable) {
Checks.isNotNull(maskClass, "maskClass");
Checks.isNotNull(creator, "creator");
Checks.isNotNull(enumClass, "enumClass");
Checks.isNotNull(nullable, "nullable");
return support(maskClass, creator, EnumTypes.getEnumType(enumClass), nullable);
}
private void checkValue(V value) {
Checks.isTrue(value != null || isNullable(), "null is not supported");
}
private void checkOther(M other) {
Checks.isNotNull(other, "other");
Checks.isTrue(support.isNullable() == other.support.isNullable(), "Cannot mix masks with different nullable");
}
private void checkValues() {
Checks.isTrue(isNullable() || !values.contains((V) null), "null is not supported");
}
@Override
public MaskSupport getSupport() {
return support;
}
@Override
public boolean isNullable() {
return support.isNullable();
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public boolean isFull() {
if (values.size() != support.getType().getValues().size() + (isNullable() ? 1 : 0)) {
return false;
} else {
return this.equals(full());
}
}
@Override
public Set getValues() {
return values;
}
@Override
public boolean isSet(V value) {
checkValue(value);
return values.contains(value);
}
@Override
public boolean isClear(V value) {
checkValue(value);
return !values.contains(value);
}
@Override
public M set(V value,
boolean enabled) {
checkValue(value);
return enabled ? set(value) : clear(value);
}
@Override
public M set(V value) {
checkValue(value);
if (isSet(value)) {
return support.getMaskClass().cast(this);
} else {
final Set tmp = new HashSet<>();
tmp.addAll(getValues());
tmp.add(value);
return support.create(tmp);
}
}
@Override
public M clear(V value) {
checkValue(value);
if (isSet(value)) {
final Set tmp = new HashSet<>();
tmp.addAll(getValues());
tmp.remove(value);
return support.create(tmp);
} else {
return support.getMaskClass().cast(this);
}
}
@Override
public M setAll(boolean enabled) {
return support.create(enabled);
}
@Override
public M empty() {
return setAll(false);
}
@Override
public M full() {
return setAll(true);
}
@Override
public M and(M other) {
checkOther(other);
final Set tmp = new HashSet<>();
tmp.addAll(getValues());
tmp.retainAll(other.getValues());
return support.create(tmp);
}
@Override
@SuppressWarnings("unchecked")
public M and(V... values) {
final M other = support.create(values);
return and(other);
}
@Override
public M or(M other) {
checkOther(other);
final Set tmp = new HashSet<>();
tmp.addAll(getValues());
tmp.addAll(other.getValues());
return support.create(tmp);
}
@Override
@SuppressWarnings("unchecked")
public M or(V... values) {
final M other = support.create(values);
return or(other);
}
@Override
public M not() {
final Set tmp = new HashSet<>();
tmp.addAll(support.getType().getValues());
if (support.isNullable()) {
tmp.add(null);
}
tmp.removeAll(getValues());
return support.create(tmp);
}
@Override
public final boolean contains(M other) {
checkOther(other);
return and(other).equals(other);
}
@Override
@SafeVarargs
public final boolean contains(V... values) {
final M other = support.create(values);
return contains(other);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(support.getMaskClass().isInstance(other))) {
return false;
}
final M o = support.getMaskClass().cast(other);
return support.getType().equals(o.support.getType())
&& support.isNullable() == o.support.isNullable()
&& this.values.equals(o.values);
}
@Override
public int hashCode() {
return support.getType().hashCode()
+ (support.isNullable() ? 1 : 0)
+ values.hashCode();
}
@Override
public String toString(Function super V, String> valueToString,
String separator) {
final StringBuilder builder = new StringBuilder();
boolean first = true;
for (final V value : getSupport().getType().getValues()) {
if (isSet(value)) {
if (first) {
first = false;
} else {
builder.append(separator);
}
builder.append(valueToString.apply(value));
}
}
if (isNullable() && isSet(null)) {
if (!first) {
builder.append(separator);
}
builder.append(valueToString.apply(null));
}
return builder.toString();
}
@Override
public String toString() {
return toString(o -> o == null ? "null" : o.toString(), "|");
}
private static final class SupportImpl, V> implements MaskSupport {
public final Class cls;
private final Creator creator;
private final ListType type;
private final Nullable nullable;
private final M empty;
public SupportImpl(Class cls,
Creator creator,
ListType type,
Nullable nullable) {
this.cls = cls;
this.creator = creator;
this.type = type;
this.nullable = nullable;
this.empty = creator.create(this, Collections.emptySet());
}
@Override
public Class getMaskClass() {
return cls;
}
@Override
public ListType getType() {
return type;
}
@Override
public boolean isNullable() {
return nullable == Nullable.TRUE;
}
@Override
public M empty() {
return empty;
}
@Override
public M full() {
return create(Predicates.alwaysTrue());
}
@Override
public M create() {
return empty;
}
@Override
public M create(V value) {
return creator.create(this, CollectionUtils.toUnmodifiableSet(value));
}
@SuppressWarnings("unchecked")
@Override
public M create(V... values) {
return creator.create(this, CollectionUtils.toUnmodifiableSet(values));
}
@Override
public M create(Iterable values) {
return creator.create(this, IterableUtils.toUnmodifiableSet(values));
}
@Override
public M create(Predicate predicate) {
final Set values;
if (nullable.isTrue()) {
values = IterableUtils.toUnmodifiableSet(IterableUtils.join(type.getValues(), (V) null), predicate);
} else {
values = IterableUtils.toUnmodifiableSet(type.getValues(), predicate);
}
return creator.create(this, values);
}
@Override
public M create(boolean enabled) {
return create(enabled ? Predicates.alwaysTrue() : Predicates.alwaysFalse());
}
}
}