
com.threerings.util.StreamableEnumSet Maven / Gradle / Ivy
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.util;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.EnumSet;
import java.util.Iterator;
import java.io.IOException;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
/**
* An {@link EnumSet} equivalent (not a subclass, because EnumSet's implementation is private)
* that can be streamed.
*
* @see Streamable
* @param the type of enum being stored in this set.
*/
public class StreamableEnumSet> extends AbstractSet
implements Cloneable, Streamable
{
/**
* Creates an empty set of the specified type.
*/
public static > StreamableEnumSet noneOf (Class elementType)
{
return new StreamableEnumSet(elementType);
}
/**
* Creates a set containing all elements of the specified type.
*/
public static > StreamableEnumSet allOf (Class elementType)
{
StreamableEnumSet set = new StreamableEnumSet(elementType);
for (E constant : elementType.getEnumConstants()) {
set.add(constant);
}
return set;
}
/**
* Creates a set containing all elements in the collection provided (which must have at least
* one element, unless it is a StreamableEnumSet
).
*/
public static > StreamableEnumSet copyOf (Collection s)
{
if (s instanceof StreamableEnumSet>) {
StreamableEnumSet set = (StreamableEnumSet)s;
return copyOf(set);
}
if (s.isEmpty()) {
throw new IllegalArgumentException("Collection must have at least one element.");
}
StreamableEnumSet set = new StreamableEnumSet(
s.iterator().next().getDeclaringClass());
set.addAll(s);
return set;
}
/**
* Creates a set containing all elements in the set provided.
*/
public static > StreamableEnumSet copyOf (StreamableEnumSet s)
{
return s.clone();
}
/**
* Creates a set containing all elements not in the set provided.
*/
public static > StreamableEnumSet complementOf (StreamableEnumSet s)
{
Class elementType = s._elementType;
StreamableEnumSet set = new StreamableEnumSet(elementType);
for (E constant : elementType.getEnumConstants()) {
if (!s.contains(constant)) {
set.add(constant);
}
}
return set;
}
/**
* Creates a set consisting of the specified elements.
*/
public static > StreamableEnumSet of (E first, E... rest)
{
StreamableEnumSet set = new StreamableEnumSet(first.getDeclaringClass());
set.add(first);
for (E e : rest) {
set.add(e);
}
return set;
}
/**
* Creates a set that includes all enum constants in the specified (inclusive) range.
*/
public static > StreamableEnumSet range (E from, E to)
{
Class elementType = from.getDeclaringClass();
StreamableEnumSet set = new StreamableEnumSet(elementType);
E[] constants = elementType.getEnumConstants();
for (int ii = from.ordinal(), last = to.ordinal(); ii <= last; ii++) {
set.add(constants[ii]);
}
return set;
}
/**
* Creates a new, empty enum set for storing elements of the specified class.
*/
public StreamableEnumSet (Class elementType)
{
_elementType = elementType;
initContents();
}
@Override
public Iterator iterator ()
{
return new Iterator() {
public boolean hasNext () {
checkConcurrentModification();
return _count < _size;
}
public E next () {
checkConcurrentModification();
do {
_idx += (++_bit >> 3);
_bit &= 0x07;
} while ((_contents[_idx] & (1 << _bit)) == 0);
_count++;
return _elementType.getEnumConstants()[(_idx << 3) | _bit];
}
public void remove () {
checkConcurrentModification();
_contents[_idx] &= ~(1 << _bit);
_size--;
_count--;
_omodcount = ++_modcount;
}
protected void checkConcurrentModification () {
if (_modcount != _omodcount) {
throw new ConcurrentModificationException();
}
}
protected int _idx, _bit = -1;
protected int _count;
protected int _omodcount = _modcount;
};
}
@Override
public int size ()
{
return _size;
}
@Override
public boolean contains (Object o)
{
if (!_elementType.isInstance(o)) {
return false;
}
int ordinal = ((Enum>)o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
return (_contents[idx] & mask) != 0;
}
@Override
public boolean add (E o)
{
int ordinal = _elementType.cast(o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
if ((_contents[idx] & mask) == 0) {
_contents[idx] |= mask;
_size++;
_modcount++;
return true;
}
return false;
}
@Override
public boolean remove (Object o)
{
if (!_elementType.isInstance(o)) {
return false;
}
int ordinal = ((Enum>)o).ordinal();
int idx = ordinal >> 3, mask = 1 << (ordinal & 0x07);
if ((_contents[idx] & mask) != 0) {
_contents[idx] &= ~mask;
_size--;
_modcount++;
return true;
}
return false;
}
@Override
public void clear ()
{
Arrays.fill(_contents, (byte)0);
_size = 0;
_modcount++;
}
@Override
public StreamableEnumSet clone ()
{
try {
// make a deep clone of the contents
@SuppressWarnings("unchecked")
StreamableEnumSet cset = (StreamableEnumSet)super.clone();
cset._contents = _contents.clone();
return cset;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e); // won't happen
}
}
/**
* Writes our custom streamable fields.
*/
public void writeObject (ObjectOutputStream out)
throws IOException
{
if (!classDefinesElementType()) {
out.writeUTF(_elementType.getName());
}
out.write(_contents);
}
/**
* Reads our custom streamable fields.
*/
public void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
if (!classDefinesElementType()) {
@SuppressWarnings("unchecked") Class elementType =
(Class)Class.forName(in.readUTF());
_elementType = elementType;
} else if (_elementType == null) {
throw new RuntimeException("No element type defined in constructor.");
}
initContents();
in.read(_contents);
// count set bits to initialize size
for (byte b : _contents) {
_size += Integer.bitCount(b & 0xFF);
}
}
/**
* Subclasses that only store elements of a single enum type (initialized in their no-arg
* constructors) can return true
here to avoid the overhead of streaming the
* enum type for each instance.
*/
protected boolean classDefinesElementType ()
{
return false;
}
/**
* Creates the contents array.
*/
protected void initContents ()
{
int constants = _elementType.getEnumConstants().length;
_contents = new byte[(constants >> 3) + ((constants & 0x07) == 0 ? 0 : 1)];
}
/** The element type. */
protected Class _elementType;
/** A byte array with bits set for each element in the set. */
protected byte[] _contents;
/** The number of elements in the set. */
protected int _size;
/** The modification count (used to detect concurrent modifications). */
protected int _modcount;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy