
cdc.enums.AbstractMask Maven / Gradle / Ivy
package cdc.enums;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.function.IntFunction;
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) {
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, HashSet::new);
}
/**
* 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 new SupportImpl<>(maskClass, creator, EnumTypes.getEnumType(enumClass), nullable,
nullable.isTrue() ? HashSet::new : s -> EnumSet.noneOf(enumClass));
}
private Set newSet(Collection values) {
final Set result = support.newSet(values.size());
result.addAll(values);
return result;
}
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 = newSet(this.values);
tmp.add(value);
return support.create(tmp);
}
}
@Override
public M clear(V value) {
checkValue(value);
if (isSet(value)) {
final Set tmp = newSet(this.values);
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);
if (this.values.size() <= other.values.size()) {
final Set tmp = newSet(this.values);
tmp.retainAll(other.values);
return support.create(tmp);
} else {
final Set tmp = newSet(other.values);
tmp.retainAll(this.values);
return support.create(tmp);
}
}
@Override
public M and(V value) {
if (contains(value)) {
return support.create(value);
} else {
return empty();
}
}
@Override
@SuppressWarnings("unchecked")
public M and(V... values) {
if (values.length == 0) {
return support.empty();
} else {
final M other = support.create(values);
return and(other);
}
}
@Override
public M or(M other) {
checkOther(other);
if (other.isEmpty()) {
return support.getMaskClass().cast(this);
} else if (isEmpty()) {
return other;
} else {
final Set tmp = newSet(this.values);
tmp.addAll(other.values);
return support.create(tmp);
}
}
@Override
public M or(V value) {
if (contains(value)) {
return support.getMaskClass().cast(this);
} else {
final M other = support.create(value);
return or(other);
}
}
@Override
@SuppressWarnings("unchecked")
public M or(V... values) {
if (values.length == 0) {
return support.getMaskClass().cast(this);
} else {
final M other = support.create(values);
return or(other);
}
}
@Override
public M not() {
final Set tmp = newSet(support.getType().getValues());
if (support.isNullable()) {
tmp.add(null);
}
tmp.removeAll(this.values);
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) {
if (values.length == 0) {
return true;
} else {
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 IntFunction> newSet;
private final M empty;
public SupportImpl(Class cls,
Creator creator,
ListType type,
Nullable nullable,
IntFunction> newSet) {
this.cls = cls;
this.creator = creator;
this.type = type;
this.nullable = nullable;
this.empty = creator.create(this, Collections.emptySet());
this.newSet = newSet;
}
@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.toSet(value));
}
@SuppressWarnings("unchecked")
@Override
public M create(V... values) {
return creator.create(this, CollectionUtils.toSet(values));
}
@Override
public M create(Iterable values) {
return creator.create(this, IterableUtils.toSet(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());
}
@Override
public Set newSet(int size) {
return newSet.apply(size);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy