A small addition to the library to address something bugging me

forever.  It's a light interface to system properties. It's not
intended to solve all property issues for all time, only to greatly
improve on the overly ad-hoc ways things are presently done.
Feedback welcome.  Sorry it's coming in this late but it arises
from writing the tools to fix the bugs to allow that release to happen.
That's nature's circle of bugs.  Review by community.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@24437 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
extempore 2011-03-12 18:13:12 +00:00
parent 43a6cd5f59
commit 8b448635f2
5 changed files with 237 additions and 6 deletions

View File

@ -0,0 +1,101 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala.sys
/** A lightweight interface wrapping a property contained in some
* unspecified map. Generally it'll be the system properties but this
* is not a requirement.
*
* See `scala.sys.SystemProperties` for an example usage.
*
* @author Paul Phillips
* @version 2.9
* @since 2.9
*/
trait Prop[T] {
/** The full name of the property, e.g. "java.awt.headless".
*/
def key: String
/** If the key exists in the properties map, converts the value
* to type `T` using valueFn. As yet no validation is performed:
* it will throw an exception on a failed conversion.
* @return the converted value, or `zero` if not in the map
*/
def value: T
/** True if the key exists in the properties map. Note that this
* is not sufficient for a Boolean property to be considered true.
* @return whether the map contains the key
*/
def isSet: Boolean
/** Sets the property.
*
* @param the new string value
* @return the old value, or null if it was unset.
*/
def set(newValue: String): String
/** Gets the current string value if any. Will not return null: use
* `isSet` to test for existence.
* @return the current string value if any, else the empty string
*/
def get: String
/** Removes the property from the underlying map.
*/
def clear(): Unit
/** A value of type `T` for use when the property is unset.
* The default implementation delivers null for reference types
* and 0/0.0/false for non-reference types.
*/
protected def zero: T
}
/** A few additional conveniences for Boolean properties.
*/
trait BooleanProp extends Prop[Boolean] {
/** The default implementation of `value` adheres to java's definition
* of truth, which means it is true only if there is a value in the map and
* that value, once converted to all lower case, is equal to "true".
*
* @return true if the current String is considered true, false otherwise
*/
def value: Boolean
/** Alter this property so that `value` will be true. */
def enable(): Unit
/** Alter this property so that `value` will be false. */
def disable(): Unit
/** Toggle the property between enabled and disabled states. */
def toggle(): Unit
}
object Prop extends PropCompanion {
/** A creator of property instances. For any type `T`, if an implicit
* parameter of type Creator[T] is in scope, a Prop[T] can be created
* via this object's apply method.
*/
trait Creator[T] {
/** Creates a Prop[T] of this type based on the given key. */
def apply(key: String): Prop[T]
}
implicit object BooleanProp extends BooleanCreatorImpl
implicit object StringProp extends CreatorImpl[String](s => s)
implicit object IntProp extends CreatorImpl[Int](_.toInt)
implicit object DoubleProp extends CreatorImpl[Double](_.toDouble)
def bool[T](key: String): BooleanProp = BooleanProp(key)
def apply[T: Creator](key: String): Prop[T] = implicitly[Creator[T]] apply key
}

View File

@ -0,0 +1,58 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala.sys
import scala.collection.mutable
/** The internal implementation of scala.sys.Prop.
*/
private[sys] class PropImpl[T](val key: String, valueFn: String => T) extends Prop[T] {
def value: T = if (isSet) valueFn(get) else zero
def isSet = underlying contains key
def set(newValue: String): String = {
val old = if (isSet) get else null
underlying(key) = newValue
old
}
def get: String =
if (isSet) underlying(key)
else ""
def clear() = underlying -= key
/** The underlying property map, in our case always sys.props */
protected def underlying: mutable.Map[String, String] = scala.sys.props
protected def zero: T = null.asInstanceOf[T]
private def getString = if (isSet) "currently: " + get else "unset"
override def toString = "%s (%s)".format(key, getString)
}
trait PropCompanion {
self: Prop.type =>
private[sys] class BooleanPropImpl(key: String, valueFn: String => Boolean) extends PropImpl[Boolean](key, valueFn) with BooleanProp {
def enable() = this set "true"
def disable() = this.clear()
def toggle() = if (value) disable() else enable()
}
private[sys] abstract class CreatorImpl[T](f: String => T) extends Creator[T] {
def apply(key: String): Prop[T] = new PropImpl[T](key, f)
}
/** Implicit objects for the standard types. Custom ones can also be
* created by implementing Creator[T].
*/
private[sys] trait BooleanCreatorImpl extends Creator[Boolean] {
self: BooleanProp.type =>
/** As implemented in java.lang.Boolean.getBoolean. */
private def javaStyleTruth(s: String) = s.toLowerCase == "true"
def apply(key: String): BooleanProp = new BooleanPropImpl(key, javaStyleTruth)
}
}

View File

@ -20,12 +20,34 @@ import scala.collection.JavaConverters._
* @version 2.9
* @since 2.9
*/
class PropertiesMap extends mutable.Map[String, String] {
override def empty = new PropertiesMap
class SystemProperties extends mutable.Map[String, String] {
override def empty = new SystemProperties
override def default(key: String): String = null
def iterator: Iterator[(String, String)] = System.getProperties().asScala.iterator
def get(key: String) = Option(System.getProperty(key))
def -= (key: String): this.type = { System.clearProperty(key) ; this }
def += (kv: (String, String)): this.type = { System.setProperty(kv._1, kv._2) ; this }
}
}
/** The values in SystemProperties can be used to access and manipulate
* designated system properties. See `scala.sys.Prop` for particulars.
* @example {{{
* if (!headless.isSet) headless.enable()
* }}}
*/
object SystemProperties {
implicit def systemPropertiesToCompanion(p: SystemProperties): SystemProperties.type = this
private lazy val propertyHelp = mutable.Map[String, String]()
private def bool(key: String, helpText: String) = {
try Prop.bool(key)
finally propertyHelp(key) = helpText
}
def help(key: String) = propertyHelp.getOrElse(key, "")
// Todo: bring some sanity to the intersection of system properties aka "mutable
// state shared by everyone and everything" and the reality that there is no other
// mechanism for accomplishing some things on the jvm.
lazy val headless = bool("java.awt.headless", "system should not utilize a display device")
lazy val preferIPv4 = bool("java.net.preferIPv4Stack", "system should prefer IPv4 sockets")
}

View File

@ -49,10 +49,10 @@ package object sys {
/** A bidirectional, mutable Map representing the current system Properties.
*
* @return a PropertiesMap.
* @see `scala.sys.PropertiesMap`
* @return a SystemProperties.
* @see `scala.sys.SystemProperties`
*/
def props: PropertiesMap = new PropertiesMap
def props: SystemProperties = new SystemProperties
/** An immutable Map representing the current system environment.
*

View File

@ -0,0 +1,50 @@
import sys._
/** Basic sys.Prop test. */
object Test {
val key = "ding.dong.doobie"
def bool() = {
val prop = Prop.bool(key)
assert(prop.key == key)
prop.clear()
assert(!prop.value)
assert(!prop.isSet)
assert(prop.get != null)
prop set "dingus"
assert(prop.get == "dingus")
assert(!prop.value)
prop set "true"
assert(prop.value)
prop.toggle()
assert(!prop.value)
prop.enable()
assert(prop.value)
prop.disable()
assert(!prop.value)
}
def int() = {
val prop = Prop[Int](key)
prop.clear()
assert(prop.value == 0)
prop.set("523")
assert(prop.value == 523)
prop.set("DingusInt")
try { println(prop.value) ; assert(false, "should not get here") }
catch { case _: Exception => () }
}
def double() = {
val prop = Prop[Double](key)
prop.set("55.0")
assert(prop.value == 55.0)
}
def main(args: Array[String]): Unit = {
bool()
int()
double()
}
}