!!!!! 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<<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<<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<<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