|
@@ -0,0 +1,178 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * @author Ranides Atterwim {@literal <ranides@gmail.com>}
|
|
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
|
|
+ * @license WTFPL
|
|
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
|
|
+ */
|
|
|
|
|
+package net.ranides.assira.collection.sets;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+import java.util.function.ToIntFunction;
|
|
|
|
|
+import net.ranides.assira.collection.arrays.ArrayUtils;
|
|
|
|
|
+import net.ranides.assira.collection.sets.MaskSet.ImmutableSet;
|
|
|
|
|
+import net.ranides.assira.collection.sets.MaskSet.MutableSet;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * This class can be used as static mapper of int values into set of predefined values.
|
|
|
|
|
+ *
|
|
|
|
|
+ * At construction time, you specify predefined type and mapping functions which
|
|
|
|
|
+ * associates int value with every predefined value. Later you can use this instance
|
|
|
|
|
+ * as factory which creates MaskSet from integer or vice versa.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Preferably, int values assigned to T constants should be stored in different bits,
|
|
|
|
|
+ * if you want predictable mapping. Whats more, preferred type for MaskFactory is some enum type,
|
|
|
|
|
+ * which offers closed list of possible values.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Every set returned by factory stores its content inside just one int.
|
|
|
|
|
+ * Please note, that because of that, maximal number of distinct values is 32.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author Ranides Atterwim {@literal <ranides@gmail.com>}
|
|
|
|
|
+ */
|
|
|
|
|
+public class MaskFactory<T> {
|
|
|
|
|
+
|
|
|
|
|
+ static final String NO_ELEMENT = "Stored value can't be mapped to any constant: ";
|
|
|
|
|
+
|
|
|
|
|
+ final T[] universe;
|
|
|
|
|
+ final ToIntFunction<T> function;
|
|
|
|
|
+ final int usedbits;
|
|
|
|
|
+ final Class<T> type;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates new factory for specified enum type.
|
|
|
|
|
+ * Mapping function should returns distinct value for every enum value, preferably distinct bit.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param universe list of enum values
|
|
|
|
|
+ * @param function mapping
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressWarnings({"PMD.ArrayIsStoredDirectly", "unchecked"})
|
|
|
|
|
+ public MaskFactory(T[] universe, ToIntFunction<T> function) {
|
|
|
|
|
+ this.universe = ArrayUtils.copy(universe);
|
|
|
|
|
+ this.function = function;
|
|
|
|
|
+ this.type = (Class<T>)universe.getClass().getComponentType();
|
|
|
|
|
+ int sum = 0;
|
|
|
|
|
+ for(T item : universe) {
|
|
|
|
|
+ sum |= function.applyAsInt(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.usedbits = sum;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates immutable set with specified enum values.
|
|
|
|
|
+ * Returned set uses bitwise operations and stores content in just one int.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param values values
|
|
|
|
|
+ * @return immutable mask
|
|
|
|
|
+ */
|
|
|
|
|
+ @SafeVarargs
|
|
|
|
|
+ public final MaskSet<T> constant(T... values) {
|
|
|
|
|
+ return new ImmutableSet<>(this, mask(values));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts integer into immutable set with specified enum values.
|
|
|
|
|
+ * Extracts information about content using mapping function associated with this factory.
|
|
|
|
|
+ * Returned set uses bitwise operations and stores content in just one int.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param value value
|
|
|
|
|
+ * @return immutable mask
|
|
|
|
|
+ */
|
|
|
|
|
+ public final MaskSet<T> constant(int value) {
|
|
|
|
|
+ if(value != (value & usedbits)) {
|
|
|
|
|
+ throw new IllegalArgumentException(unknownValueMessage(value));
|
|
|
|
|
+ }
|
|
|
|
|
+ return new ImmutableSet<>(this, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates mutable set with specified enum values.
|
|
|
|
|
+ * Returned set uses bitwise operations and stores content in just one int.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param values values
|
|
|
|
|
+ * @return mutable mask
|
|
|
|
|
+ */
|
|
|
|
|
+ @SafeVarargs
|
|
|
|
|
+ public final MaskSet<T> collect(T... values) {
|
|
|
|
|
+ return new MutableSet<>(this, mask(values));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts integer into mutable set with specified enum values.
|
|
|
|
|
+ * Extracts information about content using mapping function associated with this factory.
|
|
|
|
|
+ * Returned set uses bitwise operations and stores content in just one int.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param value value
|
|
|
|
|
+ * @return mutable mask
|
|
|
|
|
+ */
|
|
|
|
|
+ public MaskSet<T> collect(int value) {
|
|
|
|
|
+ if(value != (value & usedbits)) {
|
|
|
|
|
+ throw new IllegalArgumentException(unknownValueMessage(value));
|
|
|
|
|
+ }
|
|
|
|
|
+ return new MutableSet<>(this, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts set of enum values into integer representation.
|
|
|
|
|
+ * Converts values into int using mapping function associated with this factory.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param values values
|
|
|
|
|
+ * @return int
|
|
|
|
|
+ */
|
|
|
|
|
+ public int valueOf(Set<T> values) {
|
|
|
|
|
+ int out = 0;
|
|
|
|
|
+ for(T value : values) {
|
|
|
|
|
+ out |= function.applyAsInt(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ return out;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Converts list of enum values into integer representation.
|
|
|
|
|
+ * Converts values into int using mapping function associated with this factory.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param values values
|
|
|
|
|
+ * @return int
|
|
|
|
|
+ */
|
|
|
|
|
+ @SafeVarargs
|
|
|
|
|
+ public final int valueOf(T... values) {
|
|
|
|
|
+ int out = 0;
|
|
|
|
|
+ for(T value : values) {
|
|
|
|
|
+ out |= function.applyAsInt(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ return out;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates new mutable set with the same values as in provided set.
|
|
|
|
|
+ * Returned set uses bitwise operations and stores content in just one int.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param values values
|
|
|
|
|
+ * @return mutable mask
|
|
|
|
|
+ */
|
|
|
|
|
+ public MaskSet<T> clone(Set<T> values) {
|
|
|
|
|
+ if(values instanceof MaskSet<?>) {
|
|
|
|
|
+ return ((MaskSet<T>)values).toMutable();
|
|
|
|
|
+ }
|
|
|
|
|
+ return new MutableSet<>(this, valueOf(values));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int mask(T[] values) {
|
|
|
|
|
+ int mask = 0;
|
|
|
|
|
+ for(T item : values) {
|
|
|
|
|
+ mask |= function.applyAsInt(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ return mask;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int objectToInt(Object object) {
|
|
|
|
|
+ return function.applyAsInt(type.cast(object));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int genericToInt(T object) {
|
|
|
|
|
+ return function.applyAsInt(object);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String unknownValueMessage(int value) {
|
|
|
|
|
+ return NO_ELEMENT + Integer.toHexString(value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|