/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id$ package scala import scala.collection.SetLike import scala.collection.mutable.{Builder, AddingBuilder, Map, HashMap} import scala.collection.immutable.{Set, BitSet} import scala.collection.generic.CanBuildFrom private object Enumeration { /* This map is used to cache enumeration instances for resolving enumeration _values_ to equal objects (by-reference) when values are deserialized. */ private val emap: Map[Class[_], Enumeration] = new HashMap } /**
* Defines a finite set of values specific to the enumeration. Typically * these values enumerate all possible forms something can take and provide a * lightweight alternative to case classes. *
*
* Each call to a Value
method adds a new unique value to the
* enumeration. To be accessible, these values are usually defined as
* val
members of the evaluation.
*
* All values in an enumeration share a common, unique type defined as the
* Value
type member of the enumeration (Value
* selected on the stable identifier path of the enumeration instance).
*
* Example use *
* object Main extends Application { * * object WeekDay extends Enumeration { * type WeekDay = Value * val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value * } * import WeekDay._ * * def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) * * WeekDay.iterator filter isWorkingDay foreach println * }* * @param initial The initial value from which to count the integers that * identifies values at run-time. * @param names The sequence of names to give to this enumeration's values. * * @author Matthias Zenger * @version 1.0, 10/02/2004 */ @serializable @SerialVersionUID(8476000850333817230L) abstract class Enumeration(initial: Int, names: String*) { thisenum => def this() = this(0, null) def this(names: String*) = this(0, names: _*) Enumeration.synchronized { Enumeration.emap.get(getClass) match { case None => Enumeration.emap += (getClass -> this) case Some(_) => /* do nothing */ } } /* Note that `readResolve` cannot be private, since otherwise the JVM does not invoke it when deserializing subclasses. */ protected def readResolve(): AnyRef = Enumeration.synchronized { Enumeration.emap.get(getClass) match { case None => Enumeration.emap += (getClass -> this) this case Some(existing) => existing } } /** The name of this enumeration. */ override def toString = { val name = this.getClass.getName var string = if (name endsWith "$") name.substring(0, name.length - 1) else name val idx1 = string.lastIndexOf('.' : Int) if (idx1 != -1) string = string.substring(idx1 + 1) val idx2 = string.indexOf('$') if (idx2 != -1) string = string.substring(idx2 + 1) string } /** The mapping from the integer used to identify values to the actual * values. */ private val vmap: Map[Int, Value] = new HashMap /** The cache listing all values of this enumeration. */ @transient private var vset: ValueSet = null @transient private var vsetDefined = false /** The mapping from the integer used to identify values to their * names. */ private val nmap: Map[Int, String] = new HashMap /** The values of this enumeration as a set. */ def values: ValueSet = { if (!vsetDefined) { vset = new ValueSet(BitSet.empty ++ (vmap.values map (_.id))) vsetDefined = true } vset } /** The integer to use to identify the next created value. */ protected var nextId = initial /** The string to use to name the next created value. */ protected var nextName = names.iterator /** The highest integer amongst those used to identify values in this * enumeration. */ private var topId = initial /** The highest integer amongst those used to identify values in this * enumeration. */ final def maxId = topId /** The value of this enumeration with given id `x` */ final def apply(x: Int): Value = vmap(x) /** Returns a Value from this Enumeration whose name matches * the argument s. * * You can pass a String* set of names to the constructor, or * initialize each Enumeration with Value(String). Otherwise, the * names are determined automatically through reflection. * * Note the change here wrt 2.7 is intentional. You should know whether * a name is in an Enumeration beforehand. If not, just use find on * values. * * @param s an Enumeration name * @return the Value of this Enumeration if its name matches s * @throws java.util.NoSuchElementException if no Value with a matching * name is in this Enumeration */ final def withName(s: String): Value = values.find(_.toString == s).get /** Creates a fresh value, part of this enumeration. */ protected final def Value: Value = Value(nextId) /** Creates a fresh value, part of this enumeration, identified by the integer *
i
.
*
* @param i An integer that identifies this value at run-time. It must be
* unique amongst all values of the enumeration.
* @return ..
*/
protected final def Value(i: Int): Value =
Value(i, if (nextName.hasNext) nextName.next else null)
/** Creates a fresh value, part of this enumeration, called name
.
*
* @param name A human-readable name for that value.
*/
protected final def Value(name: String): Value = Value(nextId, name)
/** Creates a fresh value, part of this enumeration, called name
* and identified by the integer i
.
*
* @param i An integer that identifies this value at run-time. It must be
* unique amongst all values of the enumeration.
* @param name A human-readable name for that value.
* @return ..
*/
protected final def Value(i: Int, name: String): Value = new Val(i, name)
/* Obtains the name for the value with id `i`. If no name is cached
* in `nmap`, it populates `nmap` using reflection.
*/
private def nameOf(i: Int): String = synchronized {
def isValDef(m: java.lang.reflect.Method) =
getClass.getDeclaredFields.exists(fd => fd.getName == m.getName &&
fd.getType == m.getReturnType)
nmap.get(i) match {
case Some(name) => name
case None =>
val methods = getClass.getMethods
for (m <- methods
if (classOf[Value].isAssignableFrom(m.getReturnType) &&
!java.lang.reflect.Modifier.isFinal(m.getModifiers) &&
m.getParameterTypes.isEmpty &&
isValDef(m))) {
val name = m.getName
// invoke method to obtain actual `Value` instance
val value = m.invoke(this)
// invoke `id` method
val idMeth = classOf[Val].getMethod("id")
val id: Int = idMeth.invoke(value).asInstanceOf[java.lang.Integer].intValue()
nmap += (id -> name)
}
nmap(i)
}
}
/** The type of the enumerated values. */
@serializable
@SerialVersionUID(7091335633555234129L)
abstract class Value extends Ordered[Value] {
/** the id and bit location of this enumeration value */
def id: Int
override def compare(that: Value): Int = this.id - that.id
override def equals(other: Any): Boolean =
other match {
case that: thisenum.Value => compare(that) == 0
case _ => false
}
override def hashCode: Int = id.hashCode
/** this enumeration value as an Int
bit mask.
* @throws IllegalArgumentException if id
is greater than 31
*/
@deprecated("mask32 will be removed")
def mask32: Int = {
if (id >= 32) throw new IllegalArgumentException
1 << id
}
/** this enumeration value as a Long
bit mask.
* @throws IllegalArgumentException if id
is greater than 63
*/
@deprecated("mask64 will be removed")
def mask64: Long = {
if (id >= 64) throw new IllegalArgumentException
1L << id
}
}
/** A class implementing the Value
type. This class can be
* overridden to change the enumeration's naming and integer identification
* behaviour.
*/
@serializable
@SerialVersionUID(0 - 3501153230598116017L)
protected class Val(i: Int, name: String) extends Value {
def this(i: Int) =
this(i, if (nextName.hasNext) nextName.next else i.toString())
def this(name: String) = this(nextId, name)
def this() =
this(nextId, if (nextName.hasNext) nextName.next else nextId.toString())
assert(!vmap.isDefinedAt(i))
vmap(i) = this
vsetDefined = false
nextId = i + 1
if (nextId > topId) topId = nextId
def id = i
override def toString() =
if (name eq null) Enumeration.this.nameOf(i)
else name
protected def readResolve(): AnyRef = {
val enum = Enumeration.synchronized {
Enumeration.emap.get(Enumeration.this.getClass) match {
case None => Enumeration.this
case Some(existing) => existing
}
}
if (enum.vmap ne null) enum.vmap(i)
else this
}
}
/** A class for sets of values
* Iterating through this set will yield values in increasing order of their ids.
* @param ids The set of ids of values, organized as a BitSet.
*/
class ValueSet private[Enumeration] (val ids: BitSet) extends Set[Value] with SetLike[Value, ValueSet] {
override def empty = ValueSet.empty
def contains(v: Value) = ids contains (v.id)
def + (value: Value) = new ValueSet(ids + value.id)
def - (value: Value) = new ValueSet(ids - value.id)
def iterator = ids.iterator map Enumeration.this.apply
override def stringPrefix = Enumeration.this + ".ValueSet"
}
/** A factory object for value sets */
object ValueSet {
/** The empty value set */
val empty = new ValueSet(BitSet.empty)
/** A value set consisting of given elements */
def apply(elems: Value*): ValueSet = elems.foldLeft(empty)(_ + _)
/** A builder object for value sets */
def newBuilder: Builder[Value, ValueSet] = new AddingBuilder(empty)
/** The implicit builder for value sets */
implicit def canBuildFrom: CanBuildFrom[ValueSet, Value, ValueSet] =
new CanBuildFrom[ValueSet, Value, ValueSet] {
def apply(from: ValueSet) = newBuilder
def apply() = newBuilder
}
}
/** The name of this enumeration. */
@deprecated("use toString instead") def name = toString
@deprecated("use withName instead")
def valueOf(s: String) = values.find(_.toString == s)
/** A new iterator over all values of this enumeration. */
@deprecated("use values.iterator instead")
final def iterator: Iterator[Value] = values.iterator
/** Apply a function f to all values of this enumeration. */
@deprecated("use values.foreach instead")
def foreach(f: Value => Unit): Unit = this.iterator foreach f
/** Apply a predicate p to all values of this enumeration and return
* true, iff the predicate yields true for all values.
*/
@deprecated("use values.forall instead")
def forall(p: Value => Boolean): Boolean = this.iterator forall p
/** Apply a predicate p to all values of this enumeration and return
* true, iff there is at least one value for which p yields true.
*/
@deprecated("use values.exists instead")
def exists(p: Value => Boolean): Boolean = this.iterator exists p
/** Returns an iterator resulting from applying the given function f to each
* value of this enumeration.
*/
@deprecated("use values.map instead")
def map[B](f: Value => B): Iterator[B] = this.iterator map f
/** Applies the given function f to each value of this enumeration, then
* concatenates the results.
*/
@deprecated("use values.flatMap instead")
def flatMap[B](f: Value => Iterator[B]): Iterator[B] = this.iterator flatMap f
/** Returns all values of this enumeration that satisfy the predicate p.
* The order of values is preserved.
*/
@deprecated("use values.filter instead")
def filter(p: Value => Boolean): Iterator[Value] = this.iterator filter p
}