summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-03-12 18:13:12 +0000
committerPaul Phillips <paulp@improving.org>2011-03-12 18:13:12 +0000
commitee4cc17eb7f75361c2395e8f38716de14e223b87 (patch)
tree9842ea8854fa46113e3798ea2869bd3f5a536790
parente86934018bf3078a1a9751c16b95621a04575707 (diff)
downloadscala-ee4cc17eb7f75361c2395e8f38716de14e223b87.tar.gz
scala-ee4cc17eb7f75361c2395e8f38716de14e223b87.tar.bz2
scala-ee4cc17eb7f75361c2395e8f38716de14e223b87.zip
A small addition to the library to address some...
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.
-rw-r--r--src/library/scala/sys/Prop.scala101
-rw-r--r--src/library/scala/sys/PropImpl.scala58
-rw-r--r--src/library/scala/sys/SystemProperties.scala (renamed from src/library/scala/sys/PropertiesMap.scala)28
-rw-r--r--src/library/scala/sys/package.scala6
-rw-r--r--test/files/run/sysprops.scala50
5 files changed, 237 insertions, 6 deletions
diff --git a/src/library/scala/sys/Prop.scala b/src/library/scala/sys/Prop.scala
new file mode 100644
index 0000000000..8444976826
--- /dev/null
+++ b/src/library/scala/sys/Prop.scala
@@ -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
+}
diff --git a/src/library/scala/sys/PropImpl.scala b/src/library/scala/sys/PropImpl.scala
new file mode 100644
index 0000000000..02f00f682b
--- /dev/null
+++ b/src/library/scala/sys/PropImpl.scala
@@ -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)
+ }
+}
diff --git a/src/library/scala/sys/PropertiesMap.scala b/src/library/scala/sys/SystemProperties.scala
index 68f8d10418..cc8131220f 100644
--- a/src/library/scala/sys/PropertiesMap.scala
+++ b/src/library/scala/sys/SystemProperties.scala
@@ -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 }
-} \ No newline at end of file
+}
+
+/** 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")
+}
diff --git a/src/library/scala/sys/package.scala b/src/library/scala/sys/package.scala
index e8c17596ff..16faded419 100644
--- a/src/library/scala/sys/package.scala
+++ b/src/library/scala/sys/package.scala
@@ -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.
*
diff --git a/test/files/run/sysprops.scala b/test/files/run/sysprops.scala
new file mode 100644
index 0000000000..d3df22d634
--- /dev/null
+++ b/test/files/run/sysprops.scala
@@ -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()
+ }
+}