/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ package scala import scala.collection.SetLike import scala.collection.{ mutable, immutable, generic } import java.lang.reflect.{ Modifier, Method => JMethod, Field => JField } /** * 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 {{{ * 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.values filter isWorkingDay foreach println * } * // output: * // Mon * // Tue * // Wed * // Thu * // Fri * }}} * * @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 */ @SerialVersionUID(8476000850333817230L) abstract class Enumeration(initial: Int, names: String*) extends Serializable { thisenum => def this() = this(0) def this(names: String*) = this(0, names: _*) /* Note that `readResolve` cannot be private, since otherwise the JVM does not invoke it when deserializing subclasses. */ protected def readResolve(): AnyRef = thisenum.getClass.getField("MODULE$").get() /** The name of this enumeration. */ override def toString = (getClass.getName stripSuffix "$" split '.' last) split '$' last /** The mapping from the integer used to identify values to the actual * values. */ private val vmap: mutable.Map[Int, Value] = new mutable.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: mutable.Map[Int, String] = new mutable.HashMap /** The values of this enumeration as a set. */ def values: ValueSet = { if (!vsetDefined) { vset = new ValueSet(immutable.SortedSet.empty[Int] ++ (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 private def nextNameOrNull = if (nextName.hasNext) nextName.next else null /** 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 <var>s</var>. * * 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 <var>s</var> * @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 Fresh value identified by `i`. */ protected final def Value(i: Int): Value = Value(i, nextNameOrNull) /** Creates a fresh value, part of this enumeration, called `name`. * * @param name A human-readable name for that value. * @return Fresh value called `name`. */ 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 Fresh value with the provided identifier `i` and name `name`. */ protected final def Value(i: Int, name: String): Value = new Val(i, name) private def populateNameMap() { val fields = getClass.getDeclaredFields def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType) // The list of possible Value methods: 0-args which return a conforming type val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && classOf[Value].isAssignableFrom(m.getReturnType) && m.getDeclaringClass != classOf[Enumeration] && isValDef(m)) methods foreach { m => val name = m.getName // invoke method to obtain actual `Value` instance val value = m.invoke(this).asInstanceOf[Value] // verify that outer points to the correct Enumeration: ticket #3616. if (value.outerEnum eq thisenum) { val id = Int.unbox(classOf[Val] getMethod "id" invoke value) nmap += ((id, 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 { nmap.getOrElse(i, { populateNameMap() ; nmap(i) }) } /** The type of the enumerated values. */ @SerialVersionUID(7091335633555234129L) abstract class Value extends Ordered[Value] with Serializable { /** the id and bit location of this enumeration value */ def id: Int /** a marker so we can tell whose values belong to whom come reflective-naming time */ private[Enumeration] val outerEnum = thisenum override def compare(that: Value): Int = this.id - that.id override def equals(other: Any) = other match { case that: Enumeration#Value => (outerEnum eq that.outerEnum) && (id == that.id) case _ => false } override def hashCode: Int = id.## } /** A class implementing the <a href="Enumeration.Value.html" * target="contentFrame">`Value`</a> type. This class can be * overridden to change the enumeration's naming and integer identification * behaviour. */ @SerialVersionUID(0 - 3501153230598116017L) protected class Val(i: Int, name: String) extends Value with Serializable { def this(i: Int) = this(i, nextNameOrNull) def this(name: String) = this(nextId, name) def this() = this(nextId) assert(!vmap.isDefinedAt(i), "Duplicate id: " + i) vmap(i) = this vsetDefined = false nextId = i + 1 if (nextId > topId) topId = nextId def id = i override def toString() = if (name != null) name else try thisenum.nameOf(i) catch { case _: NoSuchElementException => "<Invalid enum: no field for #" + i + ">" } protected def readResolve(): AnyRef = { val enum = thisenum.readResolve().asInstanceOf[Enumeration] if (enum.vmap == null) this else enum.vmap(i) } } /** 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 SortedSet. */ class ValueSet private[Enumeration] (val ids: immutable.SortedSet[Int]) 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 thisenum.apply override def stringPrefix = thisenum + ".ValueSet" } /** A factory object for value sets */ object ValueSet { import mutable.{ Builder, SetBuilder } import generic.CanBuildFrom /** The empty value set */ val empty = new ValueSet(immutable.SortedSet.empty) /** A value set consisting of given elements */ def apply(elems: Value*): ValueSet = empty ++ elems /** A builder object for value sets */ def newBuilder: Builder[Value, ValueSet] = new SetBuilder(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 } } }
<iframe src="https://xuwei-k.github.io/scala-library-sxr/scala-library-2.9.1/scala/Enumeration.scala.html" width="1280" height="720" frameborder="0"> </iframe>