DulcetAirman icon

EnumBitOperations

DulcetAirman | PRO | 12/31/13 12:57:25 PM UTC | 0 ⭐ | 656 👁️ | Never ⏰ | []
Java |

13.15 KB

|

None

|

0 👍

/

0 👎

!!!!! There is now a project with much better code than this !!!!!
http://claude-martin.ch/enumbitset/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
I made this because enums can not easily be stored to bit fields. Set and bitwise operations are equally difficult.
I put this on pastebin because it's just one single interface. I will edit this paste if I change anything. Maybe I'll put in on a repository later. 
Note that I didn't really test this!
You can use it for whatever you want. There is no copyright. But it would be nice if you would keep my name in the file.
-----------------
package ch.claude_martin.java8;
 
import java.math.BigInteger;
import java.util.Arrays;
import java.util.BitSet;
import java.util.EnumSet;
import java.util.Set;
 
/**
 * This adds support to use bit fields and bit masks for the enym type. The set
 * of enum values is interpreted as a bit set, which can be stored to a bit
 * field.
 * 
 * <p>
 * Since this is an interface it does not change the state of the element it is
 * used on or any parameters. Instead it always returns a newly created object.
 * I.e. all methods that take a Set return a new Set. If you wish to alter the
 * set you can simply use the methods of that set.
 * 
 * <p>
 * Bit fields are by default of type {@link BigInteger}, but {@link BitSet} and
 * {@link Long long} can be used as well. Bit masks should not be stored into a
 * database unless you can guarantee that no changes are made in the future. The
 * only change that would not break your code is to add new enums at the end of
 * the list of existing enums. Note that {@link EnumSet} and {@link BigInteger}
 * already has many methods to help using sets of enums and large integers.
 * 
 * <p>
 * A bit field is always used to store a set of bits. Each bit can represent one
 * enum value. each enum has a distinct {@link #bitmask() bit mask}.
 * 
 * <p>
 * Java 8 Beta has still a bug that does not allow assert statements in
 * interfaces: <a
 * href="https://bugs.openjdk.java.net/browse/JDK-8025141">https:/
 * /bugs.openjdk.java.net/browse/JDK-8025141</a>
 * 
 * <p>
 * Nomenclature:
 * <dl>
 * <dt>enum type</dt>
 * <dd>Any type that is an enum. I.e. it uses "enum" insteaf of "class" in its
 * declaration. <br/>
 * Example: <code>Planet.class</code></dd>
 * <dt>enum</dt>
 * <dd>A field of an enumy type. This is one single value of any enum type.<br/>
 * Example: Planet.VENUS</dd>
 * </dl>
 * Example:
 * 
 * <pre><blockquote>
 * public enum Planet {
 *   MERCURY, VENUS, EARTH, ...
 * }
 * </blockquote></pre>
 * <p>
 * The goal of this interface is to use only existing types. Since all primitive
 * types are limited this uses {@link BigInteger} instead.
 * 
 * @author Claude Martin
 * 
 * @param <E>
 *          The Enum-Type.
 */
public interface EnumBitOperations<E extends Enum<E>> {
    /**
     * Returns whether this enum can be found in the list of enums of the same
     * type.
     * 
     * @returns <code>true</code>, if <code>this</code> can be found.
     */
    public default boolean elementOf(final Enum<?>... set) {
        for (final Enum<?> e : set)
            if (e == this)
                return true;
        return false;
    }
 
    /**
     * Returns whether this enum can be found in the set of enums of the same
     * type.
     * <p>
     * This is equivalent to: <code>
     * set.contains(this);
     * </code>
     * 
     * @returns <code>true</code>, if <code>this</code> can be found.
     */
    public default boolean elementOf(final Set<Enum<?>> set) {
        return set.contains(this);
    }
 
    /**
     * Creates a new EnumSet with <code>this</code> added.
     * 
     * @returns A new {@link EnumSet} including all given elements and also
     *          <code>this</code>.
     */
    @SuppressWarnings("unchecked")
    public default EnumSet<E> addTo(final E... set) {
        final EnumSet<E> result = EnumSet.copyOf(Arrays.asList(set));
        result.add((E) this);
        return result;
    }
 
    /**
     * Creates a new EnumSet with <code>this</code> added.
     * 
     * @returns A new {@link EnumSet} including all elements of the set and also
     *          <code>this</code>.
     */
    @SuppressWarnings("unchecked")
    public default EnumSet<E> addedTo(final EnumSet<E> set) {
        final EnumSet<E> result = EnumSet.copyOf(set);
        result.add((E) this);
        return result;
    }
 
    /**
     * Creates a new EnumSet with <code>this</code> removed.
     * 
     * @returns A new {@link EnumSet} with all given enums, except
     *          <code>this</code>. A copy of the set is returned if
     *          <code>this</code> is not present.
     */
    @SuppressWarnings("unchecked")
    public default EnumSet<E> removedFrom(final E... set) {
        final EnumSet<E> result = EnumSet.copyOf(Arrays.asList(set));
        result.remove(this);
        return result;
    }
 
    /**
     * Creates a new EnumSet with <code>this</code> removed.
     * 
     * @returns A new {@link EnumSet} with all elements of the set, except
     *          <code>this</code>. A copy of the set is returned if
     *          <code>this</code> is not present.
     */
    public default EnumSet<E> removedFrom(final EnumSet<E> set) {
        final EnumSet<E> result = EnumSet.copyOf(set);
        result.remove(this);
        return result;
    }
 
    /**
     * Returns a set of all enums except <code>this</code>.
     * 
     * <p>
     * This is equivalent to: <code>EnumSet.complementOf(this.asSet())</code>
     * 
     * @returns A new {@link EnumSet} with all elements except <code>this</code>.
     */
    @SuppressWarnings("unchecked")
    public default EnumSet<E> others() {
        final E e = (E) this;
        final EnumSet<E> result = EnumSet.allOf(e.getClass());
        result.remove(e);
        return result;
    }
 
    /**
     * Returns a set containing nothing but <code>this</code>.
     * 
     * @returns A new {@link EnumSet} containing <code>this</code>.
     */
    @SuppressWarnings("unchecked")
    public default EnumSet<E> asEnumSet() {
        final E e = (E) this;
        final EnumSet<E> result = EnumSet.noneOf(e.getClass());
        result.add(e);
        return result;
    }
 
    /**
     * Returns a BitSet with all bits set to 0, except the bit representing
     * <code>this</code>.
     * 
     * @returns A new {@link EnumSet} containing <code>this</code>.
     */
    @SuppressWarnings("unchecked")
    public default BitSet asBitSet() {
        final E e = (E) this;
        final BitSet result = new BitSet();
        result.set(e.ordinal());
        return result;
    }
 
    /**
     * Bitmask for <code>this</code>. The value is based on the ordinal.
     * 
     * @return <code>1&lt;&lt;this.ordinal()</code>
     * @deprecated This is actually the same as {@link #bitmask()}. This is here
     *             so that programmers will find and use {@link #bitmask()}.
     */
    @Deprecated
    public default BigInteger asBigInteger() {
        return this.bitmask();
    }
 
    /**
     * 64 bit bitmask for <code>this</code>. The value is based on the ordinal.
     * 
     * @return <code>1&lt;&lt;this.ordinal()</code>
     */
    @SuppressWarnings("unchecked")
    public default long bitmask64() {
        final E e = (E) this;
        // assert EnumSet.allOf(e.getClass()).size() <= 64;
        return 1 << e.ordinal();
    }
 
    /**
     * Bitmask for <code>this</code>. The value is based on the ordinal.
     * 
     * @return <code>1&lt;&lt;this.ordinal()</code>
     */
    @SuppressWarnings("unchecked")
    public default BigInteger bitmask() {
        final E e = (E) this;
        return BigInteger.ONE.shiftLeft(e.ordinal());
    }
 
    /**
     * Returns whether this value is set in the given bitmask.
     * 
     * @param bitmask64
     *          A bitmask.
     * @return (this.bitmask64() & bitmask8) != 0;
     */
    public default boolean elementOf(final long bitmask64) {
        return (this.bitmask64() & bitmask64) != 0;
    }
 
    /**
     * Returns whether this value is set in the given bitmask.
     * 
     * @param bitmask
     *          A bitmask.
     * @return (this.bitmask() & bitmask) != 0
     */
    public default boolean elementOf(final BigInteger bitmask) {
        return !this.bitmask().and(bitmask).equals(BigInteger.ZERO);
    }
 
    /**
     * Returns whether this value is set in the given bitset.
     * 
     * @param bitset
     * @return bitset.get(this.ordinal());
     */
    @SuppressWarnings("unchecked")
    public default boolean elementOf(final BitSet bitset) {
        return bitset.get(((E) this).ordinal());
    }
 
    /** Creates a 64 bit bitmask of a given set of enums. */
    public static <X extends Enum<X> & EnumBitOperations<X>> long toBitmask64(final EnumSet<X> set) {
        long result = 0;
        for (final X e : set)
            result |= e.bitmask64();
        return result;
    }
 
    /** Creates a 64 bit bitmask of a given set of enums. */
    @SafeVarargs
    public static <X extends Enum<X> & EnumBitOperations<X>> long toBitmask64(final X... set) {
        long result = 0;
        for (final X e : set)
            result |= e.bitmask64();
        return result;
    }
 
    /** Creates a bitmask of a given set of enums. */
    public static <X extends Enum<X> & EnumBitOperations<X>> BigInteger toBitmask(final EnumSet<X> set) {
        BigInteger result = BigInteger.ZERO;
        for (final X e : set)
            result = result.or(e.bitmask());
        return result;
    }
 
    /** Creates a bitmask of a given set of enums. */
    @SafeVarargs
    public static <X extends Enum<X> & EnumBitOperations<X>> BigInteger toBitmask(final X... set) {
        BigInteger result = BigInteger.ZERO;
        for (final X e : set)
            result = result.or(e.bitmask());
        return result;
    }
 
    /**
     * Creates a BitSet of a given set of enums.
     * */
    @SafeVarargs
    public static <X extends Enum<X> & EnumBitOperations<X>> BitSet toBitSet(final X... set) {
        final BitSet result = new BitSet();
        for (final X e : set)
            result.set(e.ordinal());
        return result;
    }
 
    /**
     * Creates a BitSet of a given set of enums.
     */
    public static <X extends Enum<X> & EnumBitOperations<X>> BitSet toBitSet(final EnumSet<X> set) {
        final BitSet result = new BitSet();
        for (final X e : set)
            result.set(e.ordinal());
        return result;
    }
 
    /** Creates a BitSet of a given mask. */
    public static BitSet toBitSet(final BigInteger mask) {
        return BitSet.valueOf(mask.toByteArray());
    }
 
    /** Creates a BigInteger of a given mask. */
    public static BigInteger toBigInteger(final BitSet bitset) {
        return new BigInteger(bitset.toByteArray());
    }
 
    /** Creates set of enums from a 64 bit bitset. */
    public static <X extends Enum<X> & EnumBitOperations<X>> EnumSet<X> toEnumSet(final long mask, final Class<X> type) {
        final EnumSet<X> result = EnumSet.noneOf(type);
        for (final X e : EnumSet.allOf(type))
            if ((e.bitmask64() & mask) != 0)
                result.add(e);
        return result;
    }
 
    /** Creates set of enums from a BitSet. */
    public static <X extends Enum<X> & EnumBitOperations<X>> EnumSet<X> toEnumSet(final BitSet bitset, final Class<X> type) {
        long mask = 0L;
        for (int i = 0; i < bitset.length(); ++i)
            mask += bitset.get(i) ? 1L << i : 0L;
        return EnumBitOperations.toEnumSet(mask, type);
    }
 
    /**
     * Takes the bitmasks of <code>this</code> and all arguments, then applies
     * logical OR to all. This results in a bitmask for all given enums (including
     * <code>this</code>).
     * 
     * @see #or(Enum...)
     * @see EnumSet#of(Enum)
     * @return <code>this.bitmask64() | arg0.bitmask64() | ... | argN.bitmask64()</code>
     */
    @SuppressWarnings("unchecked")
    public default <X extends Enum<X> & EnumBitOperations<X>> long or64(final X... set) {
        long result = this.bitmask64();
        for (final X e : set)
            result |= 1 << e.ordinal();
        return result;
    }
 
    /**
     * 
     * Note that there is a much simpler way using {@link EnumSet#of(Enum)
     * of(...)}. The following expressions define the same set: <br>
     * <code>Planet.<b>EARTH</b>.or(Planet.<b>MARS</b>, Planet.<b>JUPITER</b>)<br/>
     * Planet.of(Planet.<b>EARTH</b>, Planet.<b>MARS</b>, Planet.<b>JUPITER</b>)</code>
     * <br/>
     * However, this method returns a bitmask of type BigInteger.
     * 
     * @see EnumSet#of(Enum)
     * @see #or(BigInteger)
     * @return <code>this.bitmask() | arg<sub>0</sub>.bitmask() | ... | arg<sub>N</sub>.bitmask()</code>
     */
    @SuppressWarnings("unchecked")
    public default <X extends Enum<X> & EnumBitOperations<X>> BigInteger addedTo(final X... set) {
        BigInteger result = this.bitmask();
        for (final X e : set)
            result = result.or(e.bitmask());
        return result;
    }
 
    /**
     * Takes the bitmasks of <code>this</code> and <code>mask</code>, then applies
     * logical OR. This results in a new bitmask that also includes
     * <code>this</code>.
     * 
     * @see #addedTo(BigInteger)
     * @see #addedTo(EnumSet)
     * @see #addedTo(Enum...)
     * @return <code>mask.or(this.bitmask())</code>
     */
    public default <X extends Enum<X> & EnumBitOperations<X>> BigInteger or(final BigInteger mask) {
        return mask.or(this.bitmask());
    }
 
    /**
     * @return <code>mask.and(this.bitmask())</code>
     */
    public default <X extends Enum<X> & EnumBitOperations<X>> BigInteger and(final BigInteger mask) {
        return mask.and(this.bitmask());
    }
 
    /**
     * @return <code>mask.xor(this.bitmask())</code>
     */
    public default <X extends Enum<X> & EnumBitOperations<X>> BigInteger xor(final BigInteger mask) {
        return mask.xor(this.bitmask());
    }
 
    /**
     * Removes this from mask, but only if its bit is set in the mask.
     * 
     * @return <code>mask.andNot(this.bitmask())</code>
     */
    public default <X extends Enum<X> & EnumBitOperations<X>> BigInteger removedFrom(final BigInteger mask) {
        return mask.andNot(this.bitmask());
    }
 
}

Comments