diff options
author | Martin Odersky <odersky@gmail.com> | 2009-01-28 19:17:42 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2009-01-28 19:17:42 +0000 |
commit | e52898338ef12db769e2b61ffef748faca28e33e (patch) | |
tree | 61eb81bb036ee203970a546d218e3b64ed0e8897 /src/library/scalax/runtime | |
parent | a0c64cf5a8856cb8d905530e91ac052351b92efc (diff) | |
download | scala-e52898338ef12db769e2b61ffef748faca28e33e.tar.gz scala-e52898338ef12db769e2b61ffef748faca28e33e.tar.bz2 scala-e52898338ef12db769e2b61ffef748faca28e33e.zip |
added support for strings as collections in 2.8
Diffstat (limited to 'src/library/scalax/runtime')
-rw-r--r-- | src/library/scalax/runtime/Boxed.scala | 20 | ||||
-rw-r--r-- | src/library/scalax/runtime/BoxedString.scala | 199 | ||||
-rw-r--r-- | src/library/scalax/runtime/StringVector.scala | 54 |
3 files changed, 273 insertions, 0 deletions
diff --git a/src/library/scalax/runtime/Boxed.scala b/src/library/scalax/runtime/Boxed.scala new file mode 100644 index 0000000000..ace7c73753 --- /dev/null +++ b/src/library/scalax/runtime/Boxed.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Buffer.scala 15799 2008-08-15 18:23:54Z odersky $ + + +package scalax.runtime + +trait Boxed[T] { + + def unbox: T + +} + + diff --git a/src/library/scalax/runtime/BoxedString.scala b/src/library/scalax/runtime/BoxedString.scala new file mode 100644 index 0000000000..67ba804576 --- /dev/null +++ b/src/library/scalax/runtime/BoxedString.scala @@ -0,0 +1,199 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RichString.scala 15602 2008-07-24 08:20:38Z washburn $ + + +package scalax.runtime + +import scala.util.matching.Regex +import scalax.collection._ +import scalax.collection.mutable.StringBuilder +import scalax.collection.generic +import scalax.collection.generic._ + +object BoxedString { + // just statics for rich string. + private final val LF: Char = 0x0A + private final val FF: Char = 0x0C + private final val CR: Char = 0x0D + private final val SU: Char = 0x1A +} + +import BoxedString._ + +class BoxedString(val self: String) extends StringVector[Char] with Proxy with PartialFunction[Int, Char] with Ordered[String] { + + /** Return element at index `n` + * @throws IndexOutofBoundsException if the index is not valid + */ + def apply(n: Int): Char = self charAt n + + def length = self.length + + override def unbox = self + override def mkString = self + override def toString = self + + /** return n times the current string + */ + def * (n: Int): String = { + val buf = new StringBuilder + for (i <- 0 until n) buf append self + buf.toString + } + + override def compare(other: String) = self compareTo other + + private def isLineBreak(c: Char) = c == LF || c == FF + + /** <p> + * Strip trailing line end character from this string if it has one. + * A line end character is one of + * </p> + * <ul style="list-style-type: none;"> + * <li>LF - line feed (0x0A hex)</li> + * <li>FF - form feed (0x0C hex)</li> + * </ul> + * <p> + * If a line feed character LF is preceded by a carriage return CR + * (0x0D hex), the CR character is also stripped (Windows convention). + * </p> + */ + def stripLineEnd: String = { + val len = self.length + if (len == 0) self + else { + val last = apply(len - 1) + if (isLineBreak(last)) + self.substring(0, if (last == LF && len >= 2 && apply(len - 2) == CR) len - 2 else len - 1) + else + self + } + } + + /** <p> + * Return all lines in this string in an iterator, including trailing + * line end characters. + * </p> + * <p> + * The number of strings returned is one greater than the number of line + * end characters in this string. For an empty string, a single empty + * line is returned. A line end character is one of + * </p> + * <ul style="list-style-type: none;"> + * <li>LF - line feed (0x0A hex)</li> + * <li>FF - form feed (0x0C hex)</li> + * </ul> + */ + def linesWithSeparators = new Iterator[String] { + val len = self.length + var index = 0 + def hasNext: Boolean = index < len + def next(): String = { + if (index >= len) throw new NoSuchElementException("next on empty iterator") + val start = index + while (index < len && !isLineBreak(apply(index))) index += 1 + index += 1 + self.substring(start, index min len) + } + } + + /** Return all lines in this string in an iterator, excluding trailing line + * end characters, i.e. apply <code>.stripLineEnd</code> to all lines + * returned by <code>linesWithSeparators</code>. + */ + def lines: Iterator[String] = + linesWithSeparators map (line => new BoxedString(line).stripLineEnd) + + /** Returns this string with first character converted to upper case */ + def capitalize: String = + if (self == null) null + else if (self.length == 0) "" + else { + val chars = self.toCharArray + chars(0) = chars(0).toUpperCase + new String(chars) + } + + /** <p> + * For every line in this string: + * </p> + * <blockquote> + * Strip a leading prefix consisting of blanks or control characters + * followed by <code>marginChar</code> from the line. + * </blockquote> + */ + def stripMargin(marginChar: Char): String = { + val buf = new StringBuilder + for (line <- linesWithSeparators) { + val len = line.length + var index = 0 + while (index < len && line.charAt(index) <= ' ') index += 1 + buf append + (if (index < len && line.charAt(index) == marginChar) line.substring(index + 1) else line) + } + buf.toString + } + + /** <p> + * For every line in this string: + * </p> + * <blockquote> + * Strip a leading prefix consisting of blanks or control characters + * followed by <code>|</code> from the line. + * </blockquote> + */ + def stripMargin: String = stripMargin('|') + + // NB. "\\Q" + '\\' + "\\E" works on Java 1.5 and newer, but not on Java 1.4 + private def escape(ch: Char): String = ch match { + case '\\' => "\\\\" + case _ => "\\Q"+ch+"\\E" + } + + @throws(classOf[java.util.regex.PatternSyntaxException]) + def split(separator: Char): Array[String] = self.split(escape(separator)) + + @throws(classOf[java.util.regex.PatternSyntaxException]) + def split(separators: Array[Char]): Array[String] = { + val re = separators.foldLeft("[")(_+escape(_)) + "]" + self.split(re) + } + + /** You can follow a string with `.r', turning + * it into a Regex. E.g. + * + * """A\w*""".r is the regular expression for identifiers starting with `A'. + */ + def r: Regex = new Regex(self) + + def toBoolean: Boolean = parseBoolean(self) + def toByte: Byte = java.lang.Byte.parseByte(self) + def toShort: Short = java.lang.Short.parseShort(self) + def toInt: Int = java.lang.Integer.parseInt(self) + def toLong: Long = java.lang.Long.parseLong(self) + def toFloat: Float = java.lang.Float.parseFloat(self) + def toDouble: Double = java.lang.Double.parseDouble(self) + + private def parseBoolean(s: String): Boolean = + if (s != null) s.toLowerCase match { + case "true" => true + case "false" => false + case _ => throw new NumberFormatException("For input string: \""+s+"\"") + } + else + throw new NumberFormatException("For input string: \"null\"") + + def toArray: Array[Char] = { + val result = new Array[Char](length) + self.getChars(0, length, result, 0) + result + } +} + diff --git a/src/library/scalax/runtime/StringVector.scala b/src/library/scalax/runtime/StringVector.scala new file mode 100644 index 0000000000..ee7a805449 --- /dev/null +++ b/src/library/scalax/runtime/StringVector.scala @@ -0,0 +1,54 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Buffer.scala 15799 2008-08-15 18:23:54Z odersky $ + + +package scalax.runtime + +import collection.immutable.Vector +import collection.mutable.{ArrayBuffer, BuilderProxy} +import collection.generic.covariant.VectorTemplate + +object StringVector { + + implicit def unbox(sv: StringVector[Char]): String = sv.mkString + +} + +@cloneable +abstract class StringVector[+A] extends VectorTemplate[StringVector, A] with Vector[A] with Boxed[String] { + + /** The length of the string */ + def length: Int + + /** The element at given index */ + def apply(idx: Int): A + + /** Creates new builder for this collection */ + def newBuilder[B] = new BuilderProxy[StringVector, B] { + val self = new ArrayBuffer[B] + def result: StringVector[B] = new StringVector[B] { + def length = self.length + def apply(n: Int) = self.apply(n) + override def foreach(f: B => Unit) = self.foreach(f) + } + } + + def unbox: String = { + val sb = new StringBuilder + for (x <- this) + sb append x.asInstanceOf[Char] + sb.toString + } + + override def toString = mkString + +} + + |