org.wildfly.common.flags.Flags Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.common.flags;
import static java.lang.Integer.bitCount;
import static java.lang.Integer.highestOneBit;
import static java.lang.Integer.lowestOneBit;
import static java.lang.Integer.numberOfTrailingZeros;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.Consumer;
import org.wildfly.common.Assert;
/**
* A base class for implementing value-like flags and flag sets, where flags and flag sets may be used without allocation
* overhead.
*
*/
public abstract class Flags, This extends Flags> extends AbstractSet implements SortedSet {
final int bits;
/**
* Construct a new instance. This constructor should only be called during initial array construction.
*
* @param bits the bits of this set
*/
protected Flags(final int bits) {
this.bits = bits;
}
/**
* Get the set value of the given bit combination. The bit combination may contain extraneous one-bits so
* any bits beyond the bit of the last flag should be masked off if an array is used for lookup.
*
* @param bits the bit combination (possibly with extra bits)
* @return the set instance
*/
protected abstract This value(int bits);
/**
* Return {@code this}.
*
* @return {@code this}
*/
protected abstract This this_();
/**
* Get the flag item with the given index.
*
* @param index the index
* @return the flag
*/
protected abstract E itemOf(int index);
/**
* Get the item (cast to the correct {@code enum} type), or {@code null} if it is not of the correct type.
*
* @param obj the object to cast
* @return the cast object, or {@code null}
*/
protected abstract E castItemOrNull(Object obj);
/**
* Cast the given object to this class, throwing an exception if the cast fails.
*
* @param obj the object to cast
* @return the cast object
*/
protected abstract This castThis(Object obj);
/**
* Get the size of the flag set.
*
* @return the flag set size
*/
public final int size() {
return bitCount(bits);
}
/**
* Get the first flag in the set.
*
* @return the first flag
*/
public final E first() {
final int bits = this.bits;
if (bits == 0) throw new NoSuchElementException();
return itemOf(numberOfTrailingZeros(lowestOneBit(bits)));
}
/**
* Get the last flag in the set.
*
* @return the last flag
*/
public final E last() {
final int bits = this.bits;
if (bits == 0) throw new NoSuchElementException();
return itemOf(numberOfTrailingZeros(highestOneBit(bits)));
}
/**
* Get the {@code null} comparator, indicating that this set is always sorted in natural order.
*
* @return {@code null}
*/
public final Comparator super E> comparator() {
return null;
}
/**
* Determine if this flag set is empty.
*
* @return {@code true} if the flag set is empty, {@code false} otherwise
*/
public boolean isEmpty() {
return bits == 0;
}
/**
* Get the subset of flags from this set, up to (but not including) the given element.
*
* @param toElement the "to" element (must not be {@code null})
* @return the subset
*/
public final This headSet(final E toElement) {
Assert.checkNotNullParam("toElement", toElement);
return value(bits & bitOf(toElement) - 1);
}
/**
* Get the subset of flags from this set, starting from the given element.
*
* @param fromElement the "from" element (must not be {@code null})
* @return the subset
*/
public final This tailSet(final E fromElement) {
Assert.checkNotNullParam("fromElement", fromElement);
return value(bits & ~(bitOf(fromElement) - 1));
}
/**
* Get the subset of flags, starting from {@code fromElement} up to (but not including) {@code toElement}.
*
* @param fromElement the "from" element (must not be {@code null})
* @param toElement the "to" element (must not be {@code null})
* @return the subset
*/
public final This subSet(final E fromElement, final E toElement) {
Assert.checkNotNullParam("fromElement", fromElement);
Assert.checkNotNullParam("toElement", toElement);
return value(bits & (bitOf(toElement) - 1) & ~(bitOf(fromElement) - 1));
}
/**
* Get an {@code Object} array containing all the flag values of this set.
*
* @return the {@code Object} array
*/
public final Object[] toArray() {
int b = bits;
final Object[] array = new Object[bitCount(b)];
int idx = 0;
while (bitCount(b) > 0) {
final int lob = lowestOneBit(b);
array[idx + 1] = itemOf(numberOfTrailingZeros(lob));
b ^= lob;
}
return array;
}
/**
* Get a typed array containing all the flag values of this set.
*
* @param array the array to populate or clone
* @param the element type
* @return the populated array
*/
@SuppressWarnings("unchecked")
public final T[] toArray(T[] array) {
int b = bits;
final int size = bitCount(b);
if (size > array.length) {
array = Arrays.copyOf(array, size);
}
int idx = 0;
while (bitCount(b) > 0) {
final int lob = lowestOneBit(b);
array[idx + 1] = (T) itemOf(numberOfTrailingZeros(lob));
b ^= lob;
}
return array;
}
/**
* Determine if this flag set contains the given flag.
*
* @param flag the flag
* @return {@code true} if the flag is contained by this set
*/
public final boolean contains(E flag) {
return flag != null && (bits & bitOf(flag)) != 0;
}
/**
* Determine if this flag set contains the given object.
*
* @param o the object
* @return {@code true} if the object is contained by this set
*/
public final boolean contains(final Object o) {
return contains(castItemOrNull(o));
}
/**
* Determine if this flag set contains all of the objects in the given collection.
*
* @param c the collection
* @return {@code true} if all of the collection's objects are contained by this set
*/
public final boolean containsAll(final Collection> c) {
if (c.getClass() == getClass()) {
return containsAll(castThis(c));
} else {
for (Object o : c) {
if (! contains(o)) return false;
}
return true;
}
}
/**
* Determine if this flag set contains all of the flags in the given flag set.
*
* @param other the flag set
* @return {@code true} if all of the given set's flags are contained by this set
*/
public final boolean containsAll(This other) {
final int otherBits = other.bits;
return (this.bits & otherBits) == otherBits;
}
/**
* Determine if this flag set contains all of the given flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @return {@code true} if all of the given flags are contained by this set
*/
public final boolean containsAll(E flag1, E flag2) {
return contains(flag1) && contains(flag2);
}
/**
* Determine if this flag set contains all of the given flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @param flag3 the third flag
* @return {@code true} if all of the given flags are contained by this set
*/
public final boolean containsAll(E flag1, E flag2, E flag3) {
return containsAll(flag1, flag2) && contains(flag3);
}
/**
* Determine if this flag set contains any of the flags in the given flag set.
*
* @param other the flag set
* @return {@code true} if all of the given set's flags are contained by this set
*/
public final boolean containsAny(This other) {
return other != null && (bits & other.bits) != 0;
}
/**
* Determine if this flag set contains any of the given flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @return {@code true} if any of the given flags are contained by this set
*/
public final boolean containsAny(E flag1, E flag2) {
return contains(flag1) || contains(flag2);
}
/**
* Determine if this flag set contains any of the given flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @param flag3 the third flag
* @return {@code true} if any of the given flags are contained by this set
*/
public final boolean containsAny(E flag1, E flag2, E flag3) {
return containsAny(flag1, flag2) || contains(flag3);
}
/**
* Get the complement of this set.
*
* @return the complement of this set
*/
public final This complement() {
return value(~bits);
}
/**
* Return a set which includes all of the flags in this set and the given additional flag.
*
* @param flag the additional flag
* @return the combined set
*/
public final This with(E flag) {
return flag == null ? this_() : value(bits | bitOf(flag));
}
/**
* Return a set which includes all of the flags in this set and the given additional flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @return the combined set
*/
public final This with(E flag1, E flag2) {
return with(flag1).with(flag2);
}
/**
* Return a set which includes all of the flags in this set and the given additional flags.
*
* @param flag1 the first flag
* @param flag2 the second flag
* @param flag3 the third flag
* @return the combined set
*/
public final This with(E flag1, E flag2, E flag3) {
return with(flag1, flag2).with(flag3);
}
/**
* Return a set which includes all of the flags in this set and the given additional flags.
*
* @param flags the additional flags
* @return the combined set
*/
@SafeVarargs
public final This with(E... flags) {
if (flags == null) return this_();
int b = bits;
for (E flag : flags) {
if (flag != null) b |= bitOf(flag);
}
return value(b);
}
/**
* Return a set which includes all of the flags in this set and the given additional flags.
*
* @param other the additional flags
* @return the combined set
*/
public final This with(This other) {
return other == null ? this_() : value(bits | other.bits);
}
/**
* Return a set which includes all of the flags except for the given flag.
*
* @param flag the flag
* @return the reduced set
*/
public final This without(E flag) {
return flag == null ? this_() : value(bits & ~bitOf(flag));
}
/**
* Return a set which includes all of the flags except for the given flags.
*
* @param other the flags
* @return the reduced set
*/
public final This without(This other) {
return other == null ? this_() : value(bits & ~other.bits);
}
/**
* Determine if this flag set is equal to the given object.
*
* @param o the other object
* @return {@code true} if the object is equal to this set, {@code false} otherwise
*/
public final boolean equals(final Object o) {
return o == this || o instanceof Set && equals((Set>) o);
}
/**
* Determine if this flag set is equal to the given set.
*
* @param o the other set
* @return {@code true} if the set is equal to this set, {@code false} otherwise
*/
public final boolean equals(final Set> o) {
return o == this || o.containsAll(this) && containsAll(o);
}
/**
* Determine if this flag set is equal to the given flag set.
*
* @param o the other flag set
* @return {@code true} if the flag set is equal to this set, {@code false} otherwise
*/
public final boolean equals(final This o) {
return o == this;
}
/**
* Get the hash code of this flag set.
*
* @return the flag set hash code
*/
public final int hashCode() {
int hc = 0;
int b = this.bits;
while (b != 0) {
int lob = lowestOneBit(b);
hc += itemOf(numberOfTrailingZeros(lob)).hashCode();
b ^= lob;
}
return hc;
}
/**
* Iterate this set in order from first to last flag.
*
* @return the iterator
*/
public final Iterator iterator() {
return new Iterator() {
int b = bits;
public boolean hasNext() {
return b != 0;
}
public E next() {
int b = this.b;
if (b == 0) throw new NoSuchElementException();
final int lob = lowestOneBit(b);
final E item = itemOf(numberOfTrailingZeros(lob));
this.b = b ^ lob;
return item;
}
};
}
/**
* Iterate this set in order from last to first flag.
*
* @return the iterator
*/
public final Iterator descendingIterator() {
return new Iterator() {
int b = bits;
public boolean hasNext() {
return b != 0;
}
public E next() {
int b = this.b;
if (b == 0) throw new NoSuchElementException();
final int hob = highestOneBit(b);
final E item = itemOf(numberOfTrailingZeros(hob));
this.b = b ^ hob;
return item;
}
};
}
/**
* Apply the given action for every flag in this set.
*
* @param action the action to apply
*/
public void forEach(final Consumer super E> action) {
Assert.checkNotNullParam("action", action);
int b = this.bits;
while (b != 0) {
int lob = lowestOneBit(b);
action.accept(itemOf(numberOfTrailingZeros(lob)));
b = b ^ lob;
}
}
/**
* Get a string representation of this flag set.
*
* @return the string representation
*/
public final String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName()).append('[');
int lob;
int bits = this.bits;
if (bits != 0) {
lob = lowestOneBit(bits);
buf.append(itemOf(numberOfTrailingZeros(lob)));
bits ^= lob;
while (bits != 0) {
buf.append(' ');
lob = lowestOneBit(bits);
buf.append(itemOf(numberOfTrailingZeros(lob)));
bits ^= lob;
}
}
buf.append(']');
return buf.toString();
}
private static int bitOf(Enum> item) {
return 1 << item.ordinal();
}
}