diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2005-12-19 13:49:03 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2005-12-19 13:49:03 +0000 |
commit | ac849228490d5a0e2d3f048d649297d5c59b6ade (patch) | |
tree | 6314f2c06f37e67dec5827c3f94e25cf844a085c /src/library | |
parent | d6c0efe5b4b89a0337f1cdcdabf8c607d81f4ae1 (diff) | |
download | scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.tar.gz scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.tar.bz2 scala-ac849228490d5a0e2d3f048d649297d5c59b6ade.zip |
Switching to the new build system and to the ne...
Switching to the new build system and to the new build system. This is a
MAJOR commit, so be careful when updating.
Diffstat (limited to 'src/library')
349 files changed, 25956 insertions, 0 deletions
diff --git a/src/library/scala/All$.java b/src/library/scala/All$.java new file mode 100644 index 0000000000..e220b26ac7 --- /dev/null +++ b/src/library/scala/All$.java @@ -0,0 +1,10 @@ +package scala; + +/** + * Dummy class which exist only to satisfy the JVM. It corresponds + * to scala.AllRef. If such type appears in method + * signatures, it is erased to this one. + */ + +public class All$ {} + diff --git a/src/library/scala/AllRef$.java b/src/library/scala/AllRef$.java new file mode 100644 index 0000000000..e8c7e77aa9 --- /dev/null +++ b/src/library/scala/AllRef$.java @@ -0,0 +1,9 @@ +package scala; + +/** + * Dummy class which exist only to satisfy the JVM. It corresponds + * to scala.AllRef. If such type appears in method + * signatures, it is erased to this one. + */ + +public class AllRef$ {} diff --git a/src/library/scala/AnyVal.cs b/src/library/scala/AnyVal.cs new file mode 100644 index 0000000000..13cf2f3e2c --- /dev/null +++ b/src/library/scala/AnyVal.cs @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:AnyVal.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.Any;")] + public abstract class AnyVal {} +} diff --git a/src/library/scala/AnyVal.java b/src/library/scala/AnyVal.java new file mode 100644 index 0000000000..a7a96d0a6d --- /dev/null +++ b/src/library/scala/AnyVal.java @@ -0,0 +1,14 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.Any; */ +public abstract class AnyVal {} diff --git a/src/library/scala/Application.scala b/src/library/scala/Application.scala new file mode 100644 index 0000000000..b536c8a31d --- /dev/null +++ b/src/library/scala/Application.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Application.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** The <code>Application</code> class can be used to quickly turn objects + * into executable programs. Here is an example: + * <pre> + * object Main with Application { + * Console.println("Hello World!"); + * } + * </pre> + * Here, object <code>Main</code> inherits the <code>main</code> method + * of <code>Application</code>. The body of the <code>Main</code> object + * defines the main program. This technique does not work if the main + * program depends on command-line arguments (which are not accessible + * with the technique presented here). + * + * It is possible to time the execution of objects that inherit from + * class <code>Application</code> by setting the global scala.time property. + * Here is an example for benchmarking object <code>Main</code>: + * <pre> + * java -Dscala.time Main + * </pre> + * + * @author Matthias Zenger + * @version 1.0, 10/09/03 + */ + +[_trait_] class Application { + + /** The time when execution of this program started. + */ + val executionStart: Long = java.lang.System.currentTimeMillis(); + + /** The default main method. + */ + def main(args: Array[String]) = { + if (java.lang.System.getProperty("scala.time") != null) + java.lang.System.out.println("[total " + + (java.lang.System.currentTimeMillis() + - executionStart) + "ms]"); + } +} diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala new file mode 100644 index 0000000000..b1f15e1b6d --- /dev/null +++ b/src/library/scala/Array.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +final class Array[A](_length: Int) extends Cloneable with java.io.Serializable with Seq[A] { + def length: Int = throw new Error(); + def apply(i: Int): A = throw new Error(); + def update(i: Int, x: A): Unit = throw new Error(); + def elements: Iterator[A] = throw new Error(); +} diff --git a/src/library/scala/Attribute.scala b/src/library/scala/Attribute.scala new file mode 100644 index 0000000000..ad0df6b2f3 --- /dev/null +++ b/src/library/scala/Attribute.scala @@ -0,0 +1,14 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Attribute.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +/** A base class for attributes +*/ +class Attribute {} diff --git a/src/library/scala/Boolean.cs b/src/library/scala/Boolean.cs new file mode 100644 index 0000000000..73e72935ed --- /dev/null +++ b/src/library/scala/Boolean.cs @@ -0,0 +1,61 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Boolean.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Boolean : AnyVal + { + + public readonly bool value; + + public Boolean(bool value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return other is Boolean && value == ((Boolean)other).value; + } + public override int GetHashCode() + { + return value ? 1231 : 1237; + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Boolean;")] + public bool __bang ( ) { return !value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (bool that) { return value == that; } + public bool __bang__eq (bool that) { return value != that; } + public bool __bar__bar (bool that) { return value || that; } + public bool __amp__amp (bool that) { return value && that; } + public bool __bar (bool that) { return value | that; } + public bool __amp (bool that) { return value & that; } + public bool __up (bool that) { return value ^ that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Boolean.java b/src/library/scala/Boolean.java new file mode 100644 index 0000000000..960595f5a5 --- /dev/null +++ b/src/library/scala/Boolean.java @@ -0,0 +1,53 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Boolean extends AnyVal implements java.io.Serializable { + + public final boolean value; + + public Boolean(boolean value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Boolean && value == ((Boolean)other).value; + } + public int hashCode() { + int bits = value ? 1231 : 1237; + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Boolean; */ + public boolean $bang ( ) { return !value ; } + + + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (boolean that) { return value == that; } + public boolean $bang$eq (boolean that) { return value != that; } + public boolean $bar$bar (boolean that) { return value || that; } + public boolean $amp$amp (boolean that) { return value && that; } + public boolean $bar (boolean that) { return value | that; } + public boolean $amp (boolean that) { return value & that; } + public boolean $up (boolean that) { return value ^ that; } + +} diff --git a/src/library/scala/BufferedIterator.scala b/src/library/scala/BufferedIterator.scala new file mode 100644 index 0000000000..6ee72ee785 --- /dev/null +++ b/src/library/scala/BufferedIterator.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:BufferedIterator.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** Buffered iterators are iterators which allow to inspect the next + * element without discarding it. + * + * @author Martin Odersky + * @version 1.0, 16/07/2003 + */ +trait BufferedIterator[+A] extends Iterator[A] { + + /** Checks what the next available element is. + * + * @return the current element + */ + def head: A; +} diff --git a/src/library/scala/Byte.cs b/src/library/scala/Byte.cs new file mode 100644 index 0000000000..a8faa7080e --- /dev/null +++ b/src/library/scala/Byte.cs @@ -0,0 +1,132 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Byte.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Byte : AnyVal { + + public readonly sbyte value; + + public Byte(sbyte value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return (other is Byte) && (value == ((Byte)other).value); + } + public override int GetHashCode() + { + return value; + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Int;")] + public int __plus ( ) { return +value ; } + [Meta("method []scala.Int;")] + public int __minus ( ) { return -value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce ( ) { return value ; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + [Meta("method []scala.Float;")] + public float coerce (float dummy) { return value ; } + [Meta("method []scala.Int;")] + public int __tilde ( ) { return ~value ; } + + public int __less__less (int that) { return value << that; } + public int __less__less (long that) { return value << (int)that; } + public int __greater__greater(int that) { return value >> that; } + public int __greater__greater(long that) { return value >> (int)that; } + public int __greater__greater__greater(int that) { return (int)((uint)value >>that); } + public int __greater__greater__greater(long that) { return (int)((uint)value >>(int)that); } + + public bool __eq__eq (long that) { return value == that; } + public bool __bang__eq (long that) { return value != that; } + public bool __less (long that) { return value < that; } + public bool __greater (long that) { return value > that; } + public bool __less__eq (long that) { return value <= that; } + public bool __greater__eq(long that) { return value >= that; } + public long __plus (long that) { return value + that; } + public long __minus (long that) { return value - that; } + public long __times (long that) { return value * that; } + public long __div (long that) { return value / that; } + public long __percent (long that) { return value % that; } + public long __bar (long that) { return value | that; } + public long __amp (long that) { return value & that; } + public long __up (long that) { return value ^ that; } + + [Meta("method []scala.Long;")] + public long coerce (long dummy) { return value ; } + + public bool __eq__eq (int that) { return value == that; } + public bool __bang__eq (int that) { return value != that; } + public bool __less (int that) { return value < that; } + public bool __greater (int that) { return value > that; } + public bool __less__eq (int that) { return value <= that; } + public bool __greater__eq(int that) { return value >= that; } + public int __plus (int that) { return value + that; } + public int __minus (int that) { return value - that; } + public int __times (int that) { return value * that; } + public int __div (int that) { return value / that; } + public int __percent (int that) { return value % that; } + public int __bar (int that) { return value | that; } + public int __amp (int that) { return value & that; } + public int __up (int that) { return value ^ that; } + + [Meta("method []scala.Int;")] + public int coerce (int dummy) { return value ; } + + [Meta("method []scala.Short;")] + public short coerce (short dummy) { return value ; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Byte.java b/src/library/scala/Byte.java new file mode 100644 index 0000000000..2a841225dc --- /dev/null +++ b/src/library/scala/Byte.java @@ -0,0 +1,189 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Byte extends AnyVal implements java.io.Serializable { + + public final byte value; + + public Byte (byte value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Byte && value == ((Byte )other).value; + } + public int hashCode() { + int bits = value; + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Int ; */ + public int $plus ( ) { return +value ; } + /** @meta method []scala.Int ; */ + public int $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + + /** @meta method []scala.Float ; */ + public float coerce ( ) { return value ; } + /** @meta method []scala.Int ; */ + public int $tilde ( ) { return ~value ; } + + public int $less$less (int that) { return value << that; } + public int $less$less (long that) { return value << that; } + public int $greater$greater(int that) { return value >> that; } + public int $greater$greater(long that) { return value >> that; } + public int $greater$greater$greater(int that) { return value >>>that; } + public int $greater$greater$greater(long that) { return value >>>that; } + + public boolean $eq$eq (long that) { return value == that; } + public boolean $bang$eq (long that) { return value != that; } + public boolean $less (long that) { return value < that; } + public boolean $greater (long that) { return value > that; } + public boolean $less$eq (long that) { return value <= that; } + public boolean $greater$eq(long that) { return value >= that; } + public long $plus (long that) { return value + that; } + public long $minus (long that) { return value - that; } + public long $times (long that) { return value * that; } + public long $div (long that) { return value / that; } + public long $percent (long that) { return value % that; } + public long $bar (long that) { return value | that; } + public long $amp (long that) { return value & that; } + public long $up (long that) { return value ^ that; } + + /** @meta method []scala.Long ; */ + public long coerce ( ) { return value ; } + + public boolean $eq$eq (int that) { return value == that; } + public boolean $bang$eq (int that) { return value != that; } + public boolean $less (int that) { return value < that; } + public boolean $greater (int that) { return value > that; } + public boolean $less$eq (int that) { return value <= that; } + public boolean $greater$eq(int that) { return value >= that; } + public int $plus (int that) { return value + that; } + public int $minus (int that) { return value - that; } + public int $times (int that) { return value * that; } + public int $div (int that) { return value / that; } + public int $percent (int that) { return value % that; } + public int $bar (int that) { return value | that; } + public int $amp (int that) { return value & that; } + public int $up (int that) { return value ^ that; } + + /** @meta method []scala.Int ; */ + public int coerce ( ) { return value ; } + + /** @meta method []scala.Short ; */ + public short coerce ( ) { return value ; } + + + public boolean $eq$eq (char that) { return value == that; } + public boolean $bang$eq (char that) { return value != that; } + public boolean $less (char that) { return value < that; } + public boolean $greater (char that) { return value > that; } + public boolean $less$eq (char that) { return value <= that; } + public boolean $greater$eq(char that) { return value >= that; } + public int $plus (char that) { return value + that; } + public int $minus (char that) { return value - that; } + public int $times (char that) { return value * that; } + public int $div (char that) { return value / that; } + public int $percent (char that) { return value % that; } + public int $bar (char that) { return value | that; } + public int $amp (char that) { return value & that; } + public int $up (char that) { return value ^ that; } + + public boolean $eq$eq (short that) { return value == that; } + public boolean $bang$eq (short that) { return value != that; } + public boolean $less (short that) { return value < that; } + public boolean $greater (short that) { return value > that; } + public boolean $less$eq (short that) { return value <= that; } + public boolean $greater$eq(short that) { return value >= that; } + public int $plus (short that) { return value + that; } + public int $minus (short that) { return value - that; } + public int $times (short that) { return value * that; } + public int $div (short that) { return value / that; } + public int $percent (short that) { return value % that; } + public int $bar (short that) { return value | that; } + public int $amp (short that) { return value & that; } + public int $up (short that) { return value ^ that; } + + public boolean $eq$eq (byte that) { return value == that; } + public boolean $bang$eq (byte that) { return value != that; } + public boolean $less (byte that) { return value < that; } + public boolean $greater (byte that) { return value > that; } + public boolean $less$eq (byte that) { return value <= that; } + public boolean $greater$eq(byte that) { return value >= that; } + public int $plus (byte that) { return value + that; } + public int $minus (byte that) { return value - that; } + public int $times (byte that) { return value * that; } + public int $div (byte that) { return value / that; } + public int $percent (byte that) { return value % that; } + public int $bar (byte that) { return value | that; } + public int $amp (byte that) { return value & that; } + public int $up (byte that) { return value ^ that; } +} diff --git a/src/library/scala/CaseClass.scala b/src/library/scala/CaseClass.scala new file mode 100644 index 0000000000..07b766ede9 --- /dev/null +++ b/src/library/scala/CaseClass.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:CaseClass.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +/** defines an access function for instances of case classes + * + * @author Burak Emir + */ +trait CaseClass extends AnyRef { + + /** for a case class A(x_0,...,x_(k-1)), returns x_i for 0 <= i < k, + ** null otherwise + */ + def caseElement(n: Int): Any ; + + /** need also, for reflection + def setCaseElement(n: Int, v: Any): unit + */ + + /** for a case class A(x_0,...,x_(k-1)), returns k + */ + def caseArity: Int ; + + def caseName: String = ""; // for now + +} diff --git a/src/library/scala/Cell.scala b/src/library/scala/Cell.scala new file mode 100644 index 0000000000..77ee7015c2 --- /dev/null +++ b/src/library/scala/Cell.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Cell.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** A <code>Cell</code> is a generic wrapper which completely + * hides the functionality of the wrapped object. The wrapped + * object is accessible via the <code>elem</code> accessor method. + * + * @author Martin Odersky + * @version 1.0, 08/08/2003 + */ +case class Cell[+T](elem: T); diff --git a/src/library/scala/Char.cs b/src/library/scala/Char.cs new file mode 100644 index 0000000000..3afde58dfd --- /dev/null +++ b/src/library/scala/Char.cs @@ -0,0 +1,128 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Char.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + public abstract class Char : AnyVal { + + public readonly char value; + + public Char(char value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return other is Char && value == ((Char)other).value; + } + public override int GetHashCode() + { + return value; + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Int;")] + public int __plus ( ) { return +value ; } + [Meta("method []scala.Int;")] + public int __minus ( ) { return -value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce ( ) { return value ; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + [Meta("method []scala.Float;")] + public float coerce (float dummy) { return value; } + [Meta("method []scala.Int;")] + public int __tilde ( ) { return ~value; } + + public int __less__less (int that) { return value << that; } + public int __less__less (long that) { return value << (int)that; } + public int __greater__greater(int that) { return value >> that; } + public int __greater__greater(long that) { return value >> (int)that; } + public int __greater__greater__greater(int that) { return (int)((uint)value >>that); } + public int __greater__greater__greater(long that) { return (int)((uint)value >>(int)that); } + + public bool __eq__eq (long that) { return value == that; } + public bool __bang__eq (long that) { return value != that; } + public bool __less (long that) { return value < that; } + public bool __greater (long that) { return value > that; } + public bool __less__eq (long that) { return value <= that; } + public bool __greater__eq(long that) { return value >= that; } + public long __plus (long that) { return value + that; } + public long __minus (long that) { return value - that; } + public long __times (long that) { return value * that; } + public long __div (long that) { return value / that; } + public long __percent (long that) { return value % that; } + public long __bar (long that) { return value | that; } + public long __amp (long that) { return value & that; } + public long __up (long that) { return value ^ that; } + + [Meta("method []scala.Long;")] + public long coerce (long dummy) { return value ; } + + public bool __eq__eq (int that) { return value == that; } + public bool __bang__eq (int that) { return value != that; } + public bool __less (int that) { return value < that; } + public bool __greater (int that) { return value > that; } + public bool __less__eq (int that) { return value <= that; } + public bool __greater__eq(int that) { return value >= that; } + public int __plus (int that) { return value + that; } + public int __minus (int that) { return value - that; } + public int __times (int that) { return value * that; } + public int __div (int that) { return value / that; } + public int __percent (int that) { return value % that; } + public int __bar (int that) { return value | that; } + public int __amp (int that) { return value & that; } + public int __up (int that) { return value ^ that; } + + [Meta("method []scala.Int;")] + public int coerce (int dummy) { return value ; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Char.java b/src/library/scala/Char.java new file mode 100644 index 0000000000..a875e717df --- /dev/null +++ b/src/library/scala/Char.java @@ -0,0 +1,187 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Char extends AnyVal implements java.io.Serializable { + + public final char value; + + public Char (char value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Char && value == ((Char )other).value; + } + public int hashCode() { + int bits = value; + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Int ; */ + public int $plus ( ) { return +value ; } + /** @meta method []scala.Int ; */ + public int $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + + /** @meta method []scala.Float ; */ + public float coerce ( ) { return value ; } + /** @meta method []scala.Int ; */ + public int $tilde ( ) { return ~value ; } + + public int $less$less (int that) { return value << that; } + public int $less$less (long that) { return value << that; } + public int $greater$greater(int that) { return value >> that; } + public int $greater$greater(long that) { return value >> that; } + public int $greater$greater$greater(int that) { return value >>>that; } + public int $greater$greater$greater(long that) { return value >>>that; } + + public boolean $eq$eq (long that) { return value == that; } + public boolean $bang$eq (long that) { return value != that; } + public boolean $less (long that) { return value < that; } + public boolean $greater (long that) { return value > that; } + public boolean $less$eq (long that) { return value <= that; } + public boolean $greater$eq(long that) { return value >= that; } + public long $plus (long that) { return value + that; } + public long $minus (long that) { return value - that; } + public long $times (long that) { return value * that; } + public long $div (long that) { return value / that; } + public long $percent (long that) { return value % that; } + public long $bar (long that) { return value | that; } + public long $amp (long that) { return value & that; } + public long $up (long that) { return value ^ that; } + + /** @meta method []scala.Long ; */ + public long coerce ( ) { return value ; } + + public boolean $eq$eq (int that) { return value == that; } + public boolean $bang$eq (int that) { return value != that; } + public boolean $less (int that) { return value < that; } + public boolean $greater (int that) { return value > that; } + public boolean $less$eq (int that) { return value <= that; } + public boolean $greater$eq(int that) { return value >= that; } + public int $plus (int that) { return value + that; } + public int $minus (int that) { return value - that; } + public int $times (int that) { return value * that; } + public int $div (int that) { return value / that; } + public int $percent (int that) { return value % that; } + public int $bar (int that) { return value | that; } + public int $amp (int that) { return value & that; } + public int $up (int that) { return value ^ that; } + + public boolean $eq$eq (char that) { return value == that; } + public boolean $bang$eq (char that) { return value != that; } + public boolean $less (char that) { return value < that; } + public boolean $greater (char that) { return value > that; } + public boolean $less$eq (char that) { return value <= that; } + public boolean $greater$eq(char that) { return value >= that; } + public int $plus (char that) { return value + that; } + public int $minus (char that) { return value - that; } + public int $times (char that) { return value * that; } + public int $div (char that) { return value / that; } + public int $percent (char that) { return value % that; } + public int $bar (char that) { return value | that; } + public int $amp (char that) { return value & that; } + public int $up (char that) { return value ^ that; } + + public boolean $eq$eq (short that) { return value == that; } + public boolean $bang$eq (short that) { return value != that; } + public boolean $less (short that) { return value < that; } + public boolean $greater (short that) { return value > that; } + public boolean $less$eq (short that) { return value <= that; } + public boolean $greater$eq(short that) { return value >= that; } + public int $plus (short that) { return value + that; } + public int $minus (short that) { return value - that; } + public int $times (short that) { return value * that; } + public int $div (short that) { return value / that; } + public int $percent (short that) { return value % that; } + public int $bar (short that) { return value | that; } + public int $amp (short that) { return value & that; } + public int $up (short that) { return value ^ that; } + + public boolean $eq$eq (byte that) { return value == that; } + public boolean $bang$eq (byte that) { return value != that; } + public boolean $less (byte that) { return value < that; } + public boolean $greater (byte that) { return value > that; } + public boolean $less$eq (byte that) { return value <= that; } + public boolean $greater$eq(byte that) { return value >= that; } + public int $plus (byte that) { return value + that; } + public int $minus (byte that) { return value - that; } + public int $times (byte that) { return value * that; } + public int $div (byte that) { return value / that; } + public int $percent (byte that) { return value % that; } + public int $bar (byte that) { return value | that; } + public int $amp (byte that) { return value & that; } + public int $up (byte that) { return value ^ that; } + + + /** @meta method []scala.Int ; */ + public int coerce ( ) { return value ; } + +} diff --git a/src/library/scala/Console.scala b/src/library/scala/Console.scala new file mode 100644 index 0000000000..f7175d1e19 --- /dev/null +++ b/src/library/scala/Console.scala @@ -0,0 +1,253 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Console.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import Predef._; + +/** The <code>Console</code> object implements functionality for + * printing Scala values on the terminal. There are also functions + * for reading specific values. <code>Console</code> also defines + * constants for marking up text on ANSI terminals. + * + * @author Matthias Zenger + * @version 1.0, 03/09/2003 + */ +object Console { + import java.io._; + import java.text._; + + // ANSI colors foreground + final val BLACK = "\033[30m"; + final val RED = "\033[31m"; + final val GREEN = "\033[32m"; + final val YELLOW = "\033[33m"; + final val BLUE = "\033[34m"; + final val MAGENTA = "\033[35m"; + final val CYAN = "\033[36m"; + final val WHITE = "\033[37m"; + + // ANSI colors background + final val BLACK_B = "\033[40m"; + final val RED_B = "\033[41m"; + final val GREEN_B = "\033[42m"; + final val YELLOW_B = "\033[43m"; + final val BLUE_B = "\033[44m"; + final val MAGENTA_B = "\033[45m"; + final val CYAN_B = "\033[46m"; + final val WHITE_B = "\033[47m"; + + // ANSI styles + final val RESET = "\033[0m"; + final val BOLD = "\033[1m"; + final val UNDERLINED = "\033[4m"; + final val BLINK = "\033[5m"; + final val REVERSED = "\033[7m"; + final val INVISIBLE = "\033[8m"; + + private var out: PrintStream = java.lang.System.out; + private var in: BufferedReader = + new BufferedReader(new InputStreamReader(java.lang.System.in)); + + /** Set the default output stream. + * + * @param out the new output stream. + */ + def setOut(out: PrintStream): Unit = { + this.out = out; + } + + /** Set the default input stream. + * + * @param in the new input stream. + */ + def setIn(in: InputStream): Unit = { + this.in = new BufferedReader(new InputStreamReader(in)); + } + + /** Set the default input stream. + * + * @param reader specifies the new input stream. + */ + def setIn(reader: Reader): Unit = { + this.in = new BufferedReader(reader); + } + + /** Print an object on the terminal. + * + * @param obj the object to print. + */ + def print(obj: Any): Unit = { + if (obj == null) + out.print("null"); + else + out.print(obj.toString()); + } + + /** Flush the output stream. This function is required when partial + * output (i.e. output not terminated by a new line character) has + * to be made visible on the terminal. + */ + def flush: Unit = out.flush(); + + /** Print a new line character on the terminal. + */ + def println: Unit = { + out.println(); + } + + /** Print out an object followed by a new line character. + * + * @param x the object to print. + */ + def println(x: Any): Unit = { + out.println(x); + } + + /** Format and print out some text (in a fashion similar to printf in C). + * The format of the text to print is specified by the parameter + * <code>text</code>. The arguments that are inserted into specific + * locations in <code>text</code> are provided with parameter + * <code>args</code>. See class <code>java.text.MessageFormat</code> + * for a full specification of the format syntax. + * + * @param text the format of the text to print out. + * @param args the parameters used to instantiate the format. + */ + def printf(text: String)(args: Any*): Unit = { + // todo: Uncurry + if (text == null) + out.print("null"); + else + out.print(MessageFormat.format(text, textParams(args))); + } + + /** Read a full line from the terminal. + * + * @return the string read from the terminal. + */ + def readLine: String = in.readLine(); + + /** Read a boolean value from the terminal. + * + * @return the boolean value read from the terminal. + */ + def readBoolean: Boolean = in.readLine().toLowerCase() match { + case "true" => true + case "t" => true + case "yes" => true + case "y" => true + case _ => false + } + + /** Read a byte value from the terminal. + */ + def readByte: Byte = java.lang.Byte.decode(in.readLine()).byteValue(); + + /** Read a short value from the terminal. + */ + def readShort: Short = java.lang.Short.decode(in.readLine()).shortValue(); + + /** Read a char value from the terminal. + */ + def readChar: Char = in.readLine().charAt(0); + + /** Read an int value from the terminal. + */ + def readInt: Int = java.lang.Integer.decode(in.readLine()).intValue(); + + /** Read a float value from the terminal. + */ + def readFloat: Float = + scala.runtime.compat.Platform.parseFloat(in.readLine()); + + /** Read a double value from the terminal. + */ + def readDouble: Double = + scala.runtime.compat.Platform.parseDouble(in.readLine()); + + /** Read in some structured input, specified by a format specifier. + * See class <code>java.text.MessageFormat</code> for details of + * the format specification. + * + * @param format the format of the input. + * @return a list of all extracted values. + */ + def readf(format: String): List[Any] = + textComponents(new MessageFormat(format).parse(in.readLine())); + + /** Read in some structured input, specified by a format specifier. + * Opposed to <code>readf</code>, this function only returns the + * first value extracted from the input according to the format + * specification. + */ + def readf1(format: String): Any = readf(format).head; + + /** Read in some structured input, specified by a format specifier. + * Opposed to <code>readf</code>, this function only returns the + * first two values extracted from the input according to the format + * specification. + */ + def readf2(format: String): Pair[Any, Any] = { + val res = readf(format); + Pair(res.head, res.tail.head) + } + + /** Read in some structured input, specified by a format specifier. + * Opposed to <code>readf</code>, this function only returns the + * first three values extracted from the input according to the format + * specification. + */ + def readf3(format: String): Triple[Any, Any, Any] = { + val res = readf(format); + Triple(res.head, res.tail.head, res.tail.tail.head) + } + + private def textComponents(a: Array[AnyRef]): List[Any] = { + var i: Int = a.length - 1; + var res: List[Any] = Nil; + while (i >= 0) { + res = (a(i) match { + case x: java.lang.Boolean => x.booleanValue() + case x: java.lang.Byte => x.byteValue() + case x: java.lang.Short => x.shortValue() + case x: java.lang.Character => x.charValue() + case x: java.lang.Integer => x.intValue() + case x: java.lang.Long => x.longValue() + case x: java.lang.Float => x.floatValue() + case x: java.lang.Double => x.doubleValue() + case x => x + }) :: res; + i = i - 1; + } + res + } + + private def textParams(s: Seq[Any]): Array[AnyRef] = { + val res = new Array[AnyRef](s.length); + var i: Int = 0; + val iter = s.elements; + while (iter.hasNext) { + res(i) = iter.next match { + case x: Boolean => new java.lang.Boolean(x); + case x: Byte => new java.lang.Byte(x); + case x: Short => new java.lang.Short(x); + case x: Char => new java.lang.Character(x); + case x: Int => new java.lang.Integer(x); + case x: Long => new java.lang.Long(x); + case x: Float => new java.lang.Float(x); + case x: Double => new java.lang.Double(x); + case x: Unit => "()"; + case x: AnyRef => x; + } + i = i + 1; + } + res + } +} diff --git a/src/library/scala/Double.cs b/src/library/scala/Double.cs new file mode 100644 index 0000000000..30b9293c47 --- /dev/null +++ b/src/library/scala/Double.cs @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Double.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Double : AnyVal { + + public readonly double value; + + public Double (double value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return other is Double && value == ((Double )other).value; + } + public override int GetHashCode() + { + return value.GetHashCode(); + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Double;")] + public double __plus (object dummy) { return +value; } + [Meta("method []scala.Double;")] + public double __minus ( ) { return -value; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Double.java b/src/library/scala/Double.java new file mode 100644 index 0000000000..77f44fd10e --- /dev/null +++ b/src/library/scala/Double.java @@ -0,0 +1,78 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Double extends AnyVal implements java.io.Serializable { + + public final double value; + + public Double (double value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Double && value == ((Double )other).value; + } + public int hashCode() { + long bits = java.lang.Double.doubleToLongBits(value); + return (int)(bits ^ (bits >>> 32)); + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Double ; */ + public double $plus ( ) { return +value ; } + /** @meta method []scala.Double ; */ + public double $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + +} diff --git a/src/library/scala/Enumeration.scala b/src/library/scala/Enumeration.scala new file mode 100644 index 0000000000..885d51a180 --- /dev/null +++ b/src/library/scala/Enumeration.scala @@ -0,0 +1,140 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Enumeration.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import scala.collection.mutable._; +import Predef._; + +/** + * <p>The class <code>Enumeration</code> provides the same functionality as the + * <code>enum</code> construct found in C-like languages like C++ or Java. + * Here is an example:</p> + * <pre> + * <b>object</b> Main <b>with</b> Application { + * + * <b>object</b> WeekDays <b>extends</b> Enumeration { + * <b>val</b> Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + * } + * + * <b>def</b> isWorkingDay(d: WeekDays.Value) = + * ! (d == WeekDays.Sat || d == WeekDays.Sun); + * + * WeekDays filter (isWorkingDay) foreach { d => System.out.println(d) } + * } + * </pre> + * + * @param initial the initial integer value associated with the first element + * @param names the sequence of element names of the enumeration + * + * @author Matthias Zenger + * @version 1.0, 10/02/04 + */ +abstract class Enumeration(initial: Int, names: String*) { + + def this() = this(0, null); + + def this(names: String*) = this(0, names: _*); + + def name = { + val cname = scala.runtime.compat.Platform.getClassName(this); + if (cname.endsWith("$")) + cname.substring(0, cname.length() - 1); + else if (cname.endsWith("$class")) + cname.substring(0, cname.length() - 6); + else + cname; + } + + /** + * A mapping between the enumeration value id and the enumeration + * object. + */ + private var values: Map[Int, Value] = new HashMap; + + /** + * A cache listing all values of this enumeration. + */ + private var vcache: List[Value] = null; + + private def updateCache: List[Value] = + if (vcache == null) { + vcache = values.values.toList.sort((p1, p2) => p1.id < p2.id); + vcache + } else + vcache; + + protected var nextId = initial; + + protected var nextName = names.elements; + + private var topId = initial; + + final def maxId = topId; + + /** + * Returns the enumeration value for the given id. + */ + final def apply(x: Int): Value = values(x); + + /** + * Returns all values of this enumeration. + */ + final def elements: Iterator[Value] = updateCache.elements; + + def foreach(f: Value => Unit): Unit = elements foreach f; + + def forall(p: Value => Boolean): Boolean = elements forall p; + + def exists(p: Value => Boolean): Boolean = elements exists p; + + def map[b](f: Value => b): Iterator[b] = elements map f; + + def flatMap[b](f: Value => Iterator[b]): Iterator[b] = elements flatMap f; + + def filter(p: Value => Boolean): Iterator[Value] = elements filter p; + + override def toString(): String = updateCache.mkString("{", ", ", "}"); + + protected final def Value: Value = + new Val(nextId, if (nextName.hasNext) nextName.next else null); + + protected final def Value(i: Int): Value = + new Val(i, if (nextName.hasNext) nextName.next else null); + + protected final def Value(name: String): Value = new Val(nextId, name); + + protected final def Value(i: Int, name: String): Value = new Val(i, name); + + trait Value extends Ordered[Value] { + def id: Int; + override def compareTo[S >: Value <% Ordered[S]](that: S): Int = that match { + case that1: Value => id - that1.id + case _ => -(that compareTo this) + } + } + + 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(!values.isDefinedAt(i)); + values(i) = this; + nextId = i + 1; + if (nextId > topId) + topId = nextId; + def id = i; + override def toString() = if (name == null) + Enumeration.this.name + "(" + i + ")"; + else + name; + } +} diff --git a/src/library/scala/Float.cs b/src/library/scala/Float.cs new file mode 100644 index 0000000000..5a3a162de9 --- /dev/null +++ b/src/library/scala/Float.cs @@ -0,0 +1,81 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Float.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Float : AnyVal { + + public readonly float value; + + public Float(float value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return (other is Float) && (value == ((Float)other).value); + } + public override int GetHashCode() + { + return value.GetHashCode(); + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq(object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Float;")] + public float __plus ( ) { return +value ; } + [Meta("method []scala.Float;")] + public float __minus ( ) { return -value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce ( ) { return value; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Float.java b/src/library/scala/Float.java new file mode 100644 index 0000000000..a4a35f575e --- /dev/null +++ b/src/library/scala/Float.java @@ -0,0 +1,93 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Float extends AnyVal implements java.io.Serializable { + + public final float value; + + public Float (float value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Float && value == ((Float )other).value; + } + public int hashCode() { + int bits = java.lang.Float.floatToIntBits(value); + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Float ; */ + public float $plus ( ) { return +value ; } + /** @meta method []scala.Float ; */ + public float $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + +} diff --git a/src/library/scala/Function0.scala b/src/library/scala/Function0.scala new file mode 100644 index 0000000000..f689152741 --- /dev/null +++ b/src/library/scala/Function0.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function0[+R] extends AnyRef { + def apply(): R; +} diff --git a/src/library/scala/Function1.scala b/src/library/scala/Function1.scala new file mode 100644 index 0000000000..cfb5897a2c --- /dev/null +++ b/src/library/scala/Function1.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function1[-T0, +R] extends AnyRef { + def apply(v0: T0): R; +} diff --git a/src/library/scala/Function2.scala b/src/library/scala/Function2.scala new file mode 100644 index 0000000000..fd5eedc497 --- /dev/null +++ b/src/library/scala/Function2.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function2[-T0, -T1, +R] extends AnyRef { + def apply(v0: T0, v1: T1): R; +} diff --git a/src/library/scala/Function3.scala b/src/library/scala/Function3.scala new file mode 100644 index 0000000000..9212945a4e --- /dev/null +++ b/src/library/scala/Function3.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function3[-T0, -T1, -T2, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2): R; +} diff --git a/src/library/scala/Function4.scala b/src/library/scala/Function4.scala new file mode 100644 index 0000000000..4731986c30 --- /dev/null +++ b/src/library/scala/Function4.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function4[-T0, -T1, -T2, -T3, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3): R; +} diff --git a/src/library/scala/Function5.scala b/src/library/scala/Function5.scala new file mode 100644 index 0000000000..17abc7eb7f --- /dev/null +++ b/src/library/scala/Function5.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function5[-T0, -T1, -T2, -T3, -T4, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4): R; +} diff --git a/src/library/scala/Function6.scala b/src/library/scala/Function6.scala new file mode 100644 index 0000000000..02f6c94af2 --- /dev/null +++ b/src/library/scala/Function6.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function6[-T0, -T1, -T2, -T3, -T4, -T5, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4, v5: T5): R; +} diff --git a/src/library/scala/Function7.scala b/src/library/scala/Function7.scala new file mode 100644 index 0000000000..e97dc787ec --- /dev/null +++ b/src/library/scala/Function7.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function7[-T0, -T1, -T2, -T3, -T4, -T5, -T6, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6): R; +} diff --git a/src/library/scala/Function8.scala b/src/library/scala/Function8.scala new file mode 100644 index 0000000000..f905cae58d --- /dev/null +++ b/src/library/scala/Function8.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function8[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7): R; +} diff --git a/src/library/scala/Function9.scala b/src/library/scala/Function9.scala new file mode 100644 index 0000000000..8ae0a5d658 --- /dev/null +++ b/src/library/scala/Function9.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +trait Function9[-T0, -T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AnyRef { + def apply(v0: T0, v1: T1, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6, v7: T7, v8: T8): R; +} diff --git a/src/library/scala/Int.cs b/src/library/scala/Int.cs new file mode 100644 index 0000000000..1c909df500 --- /dev/null +++ b/src/library/scala/Int.cs @@ -0,0 +1,127 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Int.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Int : AnyVal { + + public readonly int value; + + public Int(int value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return (other is Int) && (value == ((Int)other).value); + } + public override int GetHashCode() + { + return value; + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Int;")] + public int __plus ( ) { return +value ; } + + [Meta("method []scala.Int;")] + public int __minus ( ) { return -value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce (double dummy) { return value; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + [Meta("method []scala.Float;")] + public float coerce (float dummy) { return value; } + [Meta("method []scala.Int;")] + public int __tilde ( ) { return ~value; } + + public int __less__less (int that) { return value << that; } + public int __less__less (long that) { return value << (int)that; } + public int __greater__greater(int that) { return value >> that; } + public int __greater__greater(long that) { return value >> (int)that; } + public int __greater__greater__greater(int that) { return (int)((uint)value >>that); } + public int __greater__greater__greater(long that) { return (int)((uint)value >>(int)that); } + + public bool __eq__eq (long that) { return value == that; } + public bool __bang__eq (long that) { return value != that; } + public bool __less (long that) { return value < that; } + public bool __greater (long that) { return value > that; } + public bool __less__eq (long that) { return value <= that; } + public bool __greater__eq(long that) { return value >= that; } + public long __plus (long that) { return value + that; } + public long __minus (long that) { return value - that; } + public long __times (long that) { return value * that; } + public long __div (long that) { return value / that; } + public long __percent (long that) { return value % that; } + public long __bar (long that) { return value | that; } + public long __amp (long that) { return value & that; } + public long __up (long that) { return value ^ that; } + + [Meta("method []scala.Long;")] + public long coerce (object dummy) { return value ; } + + public bool __eq__eq (int that) { return value == that; } + public bool __bang__eq (int that) { return value != that; } + public bool __less (int that) { return value < that; } + public bool __greater (int that) { return value > that; } + public bool __less__eq (int that) { return value <= that; } + public bool __greater__eq(int that) { return value >= that; } + public int __plus (int that) { return value + that; } + public int __minus (int that) { return value - that; } + public int __times (int that) { return value * that; } + public int __div (int that) { return value / that; } + public int __percent (int that) { return value % that; } + public int __bar (int that) { return value | that; } + public int __amp (int that) { return value & that; } + public int __up (int that) { return value ^ that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Int.java b/src/library/scala/Int.java new file mode 100644 index 0000000000..5c5d95a63e --- /dev/null +++ b/src/library/scala/Int.java @@ -0,0 +1,184 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Int extends AnyVal implements java.io.Serializable { + + public final int value; + + public Int (int value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Int && value == ((Int )other).value; + } + public int hashCode() { + int bits = value; + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Int ; */ + public int $plus ( ) { return +value ; } + + /** @meta method []scala.Int ; */ + public int $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + + /** @meta method []scala.Float ; */ + public float coerce ( ) { return value ; } + /** @meta method []scala.Int ; */ + public int $tilde ( ) { return ~value ; } + + public int $less$less (int that) { return value << that; } + public int $less$less (long that) { return value << that; } + public int $greater$greater(int that) { return value >> that; } + public int $greater$greater(long that) { return value >> that; } + public int $greater$greater$greater(int that) { return value >>>that; } + public int $greater$greater$greater(long that) { return value >>>that; } + + public boolean $eq$eq (long that) { return value == that; } + public boolean $bang$eq (long that) { return value != that; } + public boolean $less (long that) { return value < that; } + public boolean $greater (long that) { return value > that; } + public boolean $less$eq (long that) { return value <= that; } + public boolean $greater$eq(long that) { return value >= that; } + public long $plus (long that) { return value + that; } + public long $minus (long that) { return value - that; } + public long $times (long that) { return value * that; } + public long $div (long that) { return value / that; } + public long $percent (long that) { return value % that; } + public long $bar (long that) { return value | that; } + public long $amp (long that) { return value & that; } + public long $up (long that) { return value ^ that; } + + /** @meta method []scala.Long ; */ + public long coerce ( ) { return value ; } + + public boolean $eq$eq (int that) { return value == that; } + public boolean $bang$eq (int that) { return value != that; } + public boolean $less (int that) { return value < that; } + public boolean $greater (int that) { return value > that; } + public boolean $less$eq (int that) { return value <= that; } + public boolean $greater$eq(int that) { return value >= that; } + public int $plus (int that) { return value + that; } + public int $minus (int that) { return value - that; } + public int $times (int that) { return value * that; } + public int $div (int that) { return value / that; } + public int $percent (int that) { return value % that; } + public int $bar (int that) { return value | that; } + public int $amp (int that) { return value & that; } + public int $up (int that) { return value ^ that; } + + + public boolean $eq$eq (char that) { return value == that; } + public boolean $bang$eq (char that) { return value != that; } + public boolean $less (char that) { return value < that; } + public boolean $greater (char that) { return value > that; } + public boolean $less$eq (char that) { return value <= that; } + public boolean $greater$eq(char that) { return value >= that; } + public int $plus (char that) { return value + that; } + public int $minus (char that) { return value - that; } + public int $times (char that) { return value * that; } + public int $div (char that) { return value / that; } + public int $percent (char that) { return value % that; } + public int $bar (char that) { return value | that; } + public int $amp (char that) { return value & that; } + public int $up (char that) { return value ^ that; } + + public boolean $eq$eq (short that) { return value == that; } + public boolean $bang$eq (short that) { return value != that; } + public boolean $less (short that) { return value < that; } + public boolean $greater (short that) { return value > that; } + public boolean $less$eq (short that) { return value <= that; } + public boolean $greater$eq(short that) { return value >= that; } + public int $plus (short that) { return value + that; } + public int $minus (short that) { return value - that; } + public int $times (short that) { return value * that; } + public int $div (short that) { return value / that; } + public int $percent (short that) { return value % that; } + public int $bar (short that) { return value | that; } + public int $amp (short that) { return value & that; } + public int $up (short that) { return value ^ that; } + + public boolean $eq$eq (byte that) { return value == that; } + public boolean $bang$eq (byte that) { return value != that; } + public boolean $less (byte that) { return value < that; } + public boolean $greater (byte that) { return value > that; } + public boolean $less$eq (byte that) { return value <= that; } + public boolean $greater$eq(byte that) { return value >= that; } + public int $plus (byte that) { return value + that; } + public int $minus (byte that) { return value - that; } + public int $times (byte that) { return value * that; } + public int $div (byte that) { return value / that; } + public int $percent (byte that) { return value % that; } + public int $bar (byte that) { return value | that; } + public int $amp (byte that) { return value & that; } + public int $up (byte that) { return value ^ that; } +} diff --git a/src/library/scala/Iterable.scala b/src/library/scala/Iterable.scala new file mode 100644 index 0000000000..89103802fd --- /dev/null +++ b/src/library/scala/Iterable.scala @@ -0,0 +1,157 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Iterable.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import Predef._; + +object Iterable { + def view[A <% Ordered[A]](x: Iterable[A]): Ordered[Iterable[A]] = new Ordered[Iterable[A]] { + def compareTo[B >: Iterable[A] <% Ordered[B]](that: B): Int = that match { + case y: Iterable[A] => + val xs = x.elements; + val ys = y.elements; + var res = 0; + while (xs.hasNext && ys.hasNext && (res == 0)) { + res = xs.next compareTo ys.next; + } + if (xs.hasNext) 1 + else if (ys.hasNext) -1 + else res; + case _ => + -(that compareTo x) + } + } + + /** The minimum element of a non-empty sequence of ordered elements */ + def min[A <% Ordered[A]](seq: Iterable[A]): A = { + val xs = seq.elements; + if (!xs.hasNext) error("min(<empty>)"); + var min = xs.next; + while (xs.hasNext) { + val x = xs.next; + if (x < min) min = x; + } + min + } + + /** The maximum element of a non-empty sequence of ordered elements */ + def max[A <% Ordered[A]](seq: Iterable[A]): A = { + val xs = seq.elements; + if (!xs.hasNext) error("max(<empty>)"); + var max = xs.next; + while (xs.hasNext) { + val x = xs.next; + if (max < x) max = x; + } + max + } +} + +/** Collection classes supporting this trait provide a method + * <code>elements</code> which returns an iterator over all the + * elements contained in the collection. + * + * @author Matthias Zenger + * @version 1.1, 04/02/2004 + */ +trait Iterable[+A] { + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A]; + + /** Concatenates two iterable objects + * + * @return the new iterable object + * @author buraq + */ + def concat[B >: A](that:Iterable[B]): Iterable[B] = new Iterable[B] { + def elements: Iterator[B] = Iterable.this.elements.append(that.elements); + } + + /** Apply a function <code>f</code> to all elements of this + * iterable object. + * + * @param f a function that is applied to every element. + */ + def foreach(f: A => Unit): Unit = elements.foreach(f); + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff the predicate yields + * true for all elements. + * + * @param p the predicate + * @returns true, iff the predicate yields true for all elements. + */ + def forall(p: A => Boolean): Boolean = elements.forall(p); + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff there is at least one + * element for which <code>p</code> yields true. + * + * @param p the predicate + * @returns true, iff the predicate yields true for at least one element. + */ + def exists(p: A => Boolean): Boolean = elements.exists(p); + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the iterable object satisfying <code>p</code>, + * or <code>None</code> if none exists. + */ + def find(p: A => Boolean): Option[A] = elements.find(p); + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from left to right, and starting with + * the value <code>z</code>. + * @return <code>op(... (op(op(z,a0),a1) ...), an)</code> if the list + * is <code>List(a0, a1, ..., an)</code>. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = elements.foldLeft(z)(op); + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from rigth to left, and starting with + * the value <code>z</code>. + * @return <code>a0 op (... op (an op z)...)</code> if the list + * is <code>[a0, a1, ..., an]</code>. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = elements.foldRight(z)(op); + + /** Similar to <code>foldLeft</code> but can be used as + * an operator with the order of list and zero arguments reversed. + * That is, <code>z /: xs</code> is the same as <code>xs foldLeft z</code> + */ + def /:[B](z: B)(f: (B, A) => B): B = foldLeft(z)(f); + + /** An alias for <code>foldRight</code>. + * That is, <code>xs :\ z</code> is the same as <code>xs foldRight z</code> + */ + def :\[B](z: B)(f: (A, B) => B): B = foldRight(z)(f); + + /** Checks if the other iterable object contains the same elements. + * + * @param that the other iterable object + * @return true, iff both iterable objects contain the same elements. + */ + def sameElements[B >: A](that: Iterable[B]): Boolean = { + val ita = this.elements; + val itb = that.elements; + var res = true; + while (res && ita.hasNext && itb.hasNext) { + res = (ita.next == itb.next); + } + !ita.hasNext && !itb.hasNext && res + } +} diff --git a/src/library/scala/IterableProxy.scala b/src/library/scala/IterableProxy.scala new file mode 100644 index 0000000000..52ebe8806f --- /dev/null +++ b/src/library/scala/IterableProxy.scala @@ -0,0 +1,97 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:IterableProxy.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** This class implements a proxy for iterable objects. It forwards + * all calls to a different iterable object. + * + * @author Matthias Zenger + * @version 1.0, 26/04/2004 + */ +trait IterableProxy[+A] extends Iterable[A] with Proxy { + + def self: Iterable[A]; + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A] = self.elements; + + /** Apply a function <code>f</code> to all elements of this + * iterable object. + * + * @param f a function that is applied to every element. + */ + override def foreach(f: A => Unit): Unit = self.foreach(f); + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff the predicate yields + * true for all elements. + * + * @param p the predicate + * @returns true, iff the predicate yields true for all elements. + */ + override def forall(p: A => Boolean): Boolean = self.forall(p); + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff there is at least one + * element for which <code>p</code> yields true. + * + * @param p the predicate + * @returns true, iff the predicate yields true for at least one element. + */ + override def exists(p: A => Boolean): Boolean = self.exists(p); + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the iterable object satisfying <code>p</code>, + * or <code>None</code> if none exists. + */ + override def find(p: A => Boolean): Option[A] = self.find(p); + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from left to right, and starting with + * the value <code>z</code>. + * @return <code>op(... (op(op(z,a0),a1) ...), an)</code> if the list + * is <code>List(a0, a1, ..., an)</code>. + */ + override def foldLeft[B](z: B)(op: (B, A) => B): B = self.foldLeft(z)(op); + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from rigth to left, and starting with + * the value <code>z</code>. + * @return <code>a0 op (... op (an op z)...)</code> if the list + * is <code>[a0, a1, ..., an]</code>. + */ + override def foldRight[B](z: B)(op: (A, B) => B): B = self.foldRight(z)(op); + + /** Similar to <code>foldLeft</code> but can be used as + * an operator with the order of list and zero arguments reversed. + * That is, <code>z /: xs</code> is the same as <code>xs foldLeft z</code> + */ + override def /:[B](z: B)(f: (B, A) => B): B = self./:(z)(f); + + /** An alias for <code>foldRight</code>. + * That is, <code>xs :\ z</code> is the same as <code>xs foldRight z</code> + */ + override def :\[B](z: B)(f: (A, B) => B): B = self.:\(z)(f); + + /** Checks if the other iterable object contains the same elements. + * + * @param that the other iterable object + * @return true, iff both iterable objects contain the same elements. + */ + override def sameElements[B >: A](that: Iterable[B]): Boolean = self.sameElements(that); +} diff --git a/src/library/scala/Iterator.scala b/src/library/scala/Iterator.scala new file mode 100644 index 0000000000..1664eb2ffe --- /dev/null +++ b/src/library/scala/Iterator.scala @@ -0,0 +1,429 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Iterator.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +package scala; + +import Predef._; + +/** The <code>Iterator</code> object provides various functions for + * creating specialized iterators. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.1, 04/02/2004 + */ +object Iterator { + + def empty[a] = new Iterator[a] { + def hasNext: Boolean = false; + def next: a = Predef.error("next on empty iterator"); + } + + def single[a](x: a) = new Iterator[a] { + private var hasnext = true; + def hasNext: Boolean = hasnext; + def next: a = if (hasnext) { hasnext = false; x } else Predef.error("next on empty iterator"); + } + + def fromValues[a](xs: a*) = xs.elements; + + def fromArray[a](xs: Array[a]) = new Iterator[a] { + private var i = 0; + def hasNext: Boolean = i < xs.length; + def next: a = + if (i < xs.length) { val x = xs(i) ; i = i + 1 ; x } + else Predef.error("next on empty iterator"); + } + + def fromString(str: String): Iterator[Char] = new Iterator[Char] { + private var i = 0; + private val len = str.length(); + def hasNext = i < len; + def next = { val c = str charAt i; i = i + 1; c }; + } + + def fromCaseClass(n:CaseClass): Iterator[Any] = new Iterator[Any] { + private var c:Int = 0; + private val cmax = n.caseArity; + def hasNext = c < cmax; + def next = { val a = n caseElement c; c = c + 1; a } + } + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = e<sub>n</sub> + 1</code> + * where <code>e<sub>0</sub> = lo</code> + * and <code>e<sub>i</sub> < end</code>. + * + * @param lo the start value of the iterator + * @param end the end value of the iterator + * @return the iterator with values in range [lo;end). + */ + def range(lo: Int, end: Int): Iterator[Int] = + range(lo, end, 1); + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = e<sub>n</sub> + step</code> + * where <code>e<sub>0</sub> = lo</code> + * and <code>e<sub>i</sub> < end</code>. + * + * @param lo the start value of the iterator + * @param end the end value of the iterator + * @param step the increment value of the iterator (must be positive or negative) + * @return the iterator with values in range [lo;end). + */ + def range(lo: Int, end: Int, step: Int): Iterator[Int] = { + assert(step != 0); + new Iterator[Int] { + private var i = lo; + def hasNext: Boolean = if (step > 0) i < end else i > end; + def next: Int = + if (hasNext) { val j = i; i = i + step; j } else Predef.error("next on empty iterator"); + def head: Int = + if (hasNext) i else Predef.error("head on empty iterator"); + } + } + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = step(e<sub>n</sub>)</code> + * where <code>e<sub>0</sub> = lo</code> + * and <code>e<sub>i</sub> < end</code>. + * + * @param lo the start value of the iterator + * @param end the end value of the iterator + * @param step the increment function of the iterator + * @return the iterator with values in range [lo;end). + */ + def range(lo: Int, end: Int, step: Int => Int): Iterator[Int] = new Iterator[Int] { + private var i = lo; + def hasNext: Boolean = i < end; + def next: Int = + if (i < end) { val j = i; i = step(i); j } else Predef.error("next on empty iterator"); + def head: Int = + if (i < end) i else Predef.error("head on empty iterator"); + } + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = e<sub>n</sub> + 1</code> + * where <code>e<sub>0</sub> = lo</code>. + * + * @param lo the start value of the iterator + * @return the iterator starting at value <code>lo</code>. + */ + def from(lo: Int): Iterator[Int] = + from(lo, 1); + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = e<sub>n</sub> + step</code> + * where <code>e<sub>0</sub> = lo</code>. + * + * @param lo the start value of the iterator + * @param step the increment value of the iterator + * @return the iterator starting at value <code>lo</code>. + */ + def from(lo: Int, step: Int) = new Iterator[Int] { + private var i = lo; + def hasNext: Boolean = true; + def next: Int = { val j = i; i = i + step; j } + } + + /** Create an iterator with elements + * <code>e<sub>n+1</sub> = step(e<sub>n</sub>)</code> + * where <code>e<sub>0</sub> = lo</code>. + * + * @param lo the start value of the iterator + * @param step the increment function of the iterator + * @return the iterator starting at value <code>lo</code>. + */ + def from(lo: Int, step: Int => Int) = new Iterator[Int] { + private var i = lo; + def hasNext: Boolean = true; + def next: Int = { val j = i; i = step(i); j } + } + +} + +/** Iterators are data structures that allow to iterate over a sequence + * of elements. They have a <code>hasNext</code> method for checking + * if there is a next element available, and a <code>next</code> method + * which returns the next element and discards it from the iterator. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.2, 15/03/2004 + */ +trait Iterator[+A] { + + /** Does this iterator provide another element? + */ + def hasNext: Boolean; + + /** Returns the next element. + */ + def next: A; + + /** Returns a new iterator that iterates only over the first <code>n</code> + * elements. + */ + def take(n: Int) = new Iterator[A] { + var remaining = n; + def hasNext = remaining > 0 && Iterator.this.hasNext; + def next: A = + if (hasNext) { remaining = remaining - 1; Iterator.this.next } + else Predef.error("next on empty iterator"); + } + + /** Removes the first <code>n</code> elements from this iterator. + */ + def drop(n: Int): Iterator[A] = + if (n > 0) { next; drop(n - 1) } else this; + + /** Returns a new iterator that maps all elements of this iterator + * to new elements using function <code>f</code>. + */ + def map[B](f: A => B): Iterator[B] = new Iterator[B] { + def hasNext = Iterator.this.hasNext; + def next = f(Iterator.this.next) + } + + /** Returns a new iterator that first yields the elements of this + * iterator followed by the elements provided by iterator <code>that</code>. + */ + def append[B >: A](that: Iterator[B]) = new Iterator[B] { + def hasNext = Iterator.this.hasNext || that.hasNext; + def next = if (Iterator.this.hasNext) Iterator.this.next else that.next; + } + + /** Applies the given function <code>f</code> to each element of + * this iterator, then concatenates the results. + * + * @param f the function to apply on each element. + * @return an iterator over <code>f(a0), ... , f(an)</code> if this iterator + * yields the elements <code>a0, ..., an</code>. + */ + def flatMap[B](f: A => Iterator[B]): Iterator[B] = new Iterator[B] { + private var cur: Iterator[B] = Iterator.empty; + def hasNext: Boolean = + if (cur.hasNext) true + else if (Iterator.this.hasNext) { + cur = f(Iterator.this.next); + hasNext + } else false; + def next: B = + if (cur.hasNext) cur.next + else if (Iterator.this.hasNext) { + cur = f(Iterator.this.next); + next + } else Predef.error("next on empty iterator"); + } + + /** Returns an iterator over all the elements of this iterator that + * satisfy the predicate <code>p</code>. The order of the elements + * is preserved. + * + * @param p the redicate used to filter the iterator. + * @return the elements of this iterator satisfying <code>p</code>. + */ + def filter(p: A => Boolean): Iterator[A] = new BufferedIterator[A] { + private val source = Iterator.this.buffered; + private def skip: Unit = + while (source.hasNext && !p(source.head)) { source.next; () } + def hasNext: Boolean = { skip; source.hasNext } + def next: A = { skip; source.next } + def head: A = { skip; source.head; } + } + + /** Return an iterator formed from this iterator and the specified iterator + * <code>that</code> by associating each element of the former with + * the element at the same position in the latter. + * + * @param <code>that</code> must have the same number of elements as this + * iterator. + * @return an iterator yielding <code>(a0,b0), ..., (an,bn)</code> where + * <code>ai</code> are the elements from this iterator and + * <code>bi</code> are the elements from iterator <code>that</code>. + */ + def zip[B](that: Iterator[B]) = new Iterator[Pair[A, B]] { + def hasNext = Iterator.this.hasNext && that.hasNext; + def next = Pair(Iterator.this.next, that.next); + } + + /** Apply a function <code>f</code> to all elements of this + * iterable object. + * + * @param f a function that is applied to every element. + */ + def foreach(f: A => Unit): Unit = while (hasNext) { f(next) }; + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff the predicate yields + * true for all elements. + * + * @param p the predicate + * @returns true, iff the predicate yields true for all elements. + */ + def forall(p: A => Boolean): Boolean = { + var res = true; + while (res && hasNext) { res = p(next) } + res + } + + /** Apply a predicate <code>p</code> to all elements of this + * iterable object and return true, iff there is at least one + * element for which <code>p</code> yields true. + * + * @param p the predicate + * @returns true, iff the predicate yields true for at least one element. + */ + def exists(p: A => Boolean): Boolean = { + var res = false; + while (!res && hasNext) { res = p(next) } + res + } + + /** Tests if the given value <code>elem</code> is a member of this list. + * + * @param elem element whose membership has to be tested. + * @return True iff there is an element of this list which is + * equal (w.r.t. <code>==</code>) to <code>elem</code>. + */ + def contains(elem: Any): Boolean = exists { x => x == elem }; + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the iterable object satisfying <code>p</code>, + * or <code>None</code> if none exists. + */ + def find(p: A => Boolean): Option[A] = { + var res: Option[A] = None; + while (res.isEmpty && hasNext) { + val e = next; + if (p(e)) res = Some(e); + } + res + } + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from left to right, and starting with + * the value <code>z</code>. + * @return <code>op(... (op(op(z,a0),a1) ...), an)</code> if the list + * is <code>List(a0, a1, ..., an)</code>. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = { + var acc = z; + while (hasNext) { acc = op(acc, next) } + acc + } + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from rigth to left, and starting with + * the value <code>z</code>. + * @return <code>a0 op (... op (an op z)...)</code> if the list + * is <code>[a0, a1, ..., an]</code>. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = { + def fold(z: B): B = + if (hasNext) op(next, fold(z)) else z; + fold(z) + } + + /** Similar to <code>foldLeft</code> but can be used as + * an operator with the order of list and zero arguments reversed. + * That is, <code>z /: xs</code> is the same as <code>xs foldLeft z</code> + */ + def /:[B](z: B)(f: (B, A) => B): B = foldLeft(z)(f); + + /** An alias for <code>foldRight</code>. + * That is, <code>xs :\ z</code> is the same as <code>xs foldRight z</code> + */ + def :\[B](z: B)(f: (A, B) => B): B = foldRight(z)(f); + + /** Returns a buffered iterator from this iterator. + */ + def buffered: BufferedIterator[A] = new BufferedIterator[A] { + private var hd: A = _; + private var ahead: Boolean = false; + def head: A = { + if (!ahead) { + hd = Iterator.this.next; + ahead = true + } + hd + } + def next: A = + if (ahead) { ahead = false; hd } else head; + def hasNext: Boolean = ahead || Iterator.this.hasNext; + override def buffered: BufferedIterator[A] = this; + } + + /** Creates two new iterators that both iterate over the same elements + * than this iterator (in the same order). + */ + def duplicate: Pair[Iterator[A], Iterator[A]] = { + var xs: List[A] = Nil; + var ahead: Iterator[A] = null; + class Partner extends Iterator[A] { + var ys: List[A] = Nil; + def hasNext: Boolean = Iterator.this.synchronized ( + ((this == ahead) && Iterator.this.hasNext) || + ((this != ahead) && (!xs.isEmpty || !ys.isEmpty || Iterator.this.hasNext)) + ); + def next: A = Iterator.this.synchronized { + if (this == ahead) { + val e = Iterator.this.next; + xs = e :: xs; e + } else { + if (ys.isEmpty) { + ys = xs.reverse; + xs = Nil; + } + ys match { + case Nil => val e = Iterator.this.next; + ahead = this; + xs = e :: xs; e + case z :: zs => ys = zs; z + } + } + } + } + ahead = new Partner; + Pair(ahead, new Partner) + } + + /** Fills the given array <code>xs</code> with the elements of + * this sequence starting at position <code>start</code>. + * + * @param xs the array to fill. + * @param start starting index. + * @return the given array <code>xs</code> filled with this list. + */ + def copyToArray[B >: A](xs: Array[B], start: Int): Array[B] = { + var i = start; + while (hasNext) { + xs(i) = next; + i = i + 1; + } + xs + } + + /** Transform this iterator into a list of all elements. + * + * @return a list which enumerates all elements of this iterator. + */ + def toList: List[A] = { + var res: List[A] = Nil; + while (hasNext) { + res = next :: res; + } + res.reverse + } + +} diff --git a/src/library/scala/List.scala b/src/library/scala/List.scala new file mode 100644 index 0000000000..4da2611640 --- /dev/null +++ b/src/library/scala/List.scala @@ -0,0 +1,919 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:List.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import Predef._; + +/** This object provides methods for creating specialized lists, and for + * transforming special kinds of lists (e.g. lists of lists). + * + * @author Martin Odersky and others + * @version 1.0, 15/07/2003 + */ +object List { + + /** Create a list with given elements. + * + * @param xs the elements to put in the list + * @return the list containing elements xs. + */ + def apply[A](xs: A*): List[A] = + // TODO: remove the type test once nsc becomes standard + if (xs.isInstanceOf$erased[List[A]]) + xs.asInstanceOf$erased[List[A]]; + else + fromArray(xs.asInstanceOf$erased[Array[A]]); + + /** Create a sorted list of all integers in a range. + * + * @param from the start value of the list + * @param end the end value of the list + * @return the sorted list of all integers in range [from;end). + */ + def range(from: Int, end: Int): List[Int] = + range(from, end, 1); + + /** Create a sorted list of all integers in a range. + * + * @param from the start value of the list + * @param end the end value of the list + * @param step the increment value of the list + * @return the sorted list of all integers in range [from;end). + */ + def range(from: Int, end: Int, step: Int): List[Int] = + if (from >= end) Nil + else from :: range(from + step, end, step); + + /** Create a sorted list of all integers in a range. + * + * @param from the start value of the list + * @param end the end value of the list + * @param step the increment function of the list + * @return the sorted list of all integers in range [from;end). + */ + def range(from: Int, end: Int, step: Int => Int): List[Int] = + if (from >= end) Nil + else from :: range(step(from), end, step); + + /** Create a list containing several copies of an element. + * + * @param n the length of the resulting list + * @param elem the element composing the resulting list + * @return a list composed of n elements all equal to elem + */ + def make[a](n: int, elem: a): List[a] = + if (n == 0) Nil + else elem :: make(n - 1, elem); + + /** Create a list by applying a function to successive integers. + * + * @param n the length of the resulting list + * @param maker the procedure which, given an integer n, returns the + * nth element of the resulting list, where n is in [0;n). + * @return the list obtained by applying the maker function to successive + * integers from 0 to n (exclusive). + */ + def tabulate[a](n: int, maker: int => a): List[a] = { + def loop(i: int): List[a] = + if (i == n) Nil + else maker(i) :: loop(i + 1); + loop(0) + } + + /** Concatenate all the elements of a given list of lists. + * @param l the list of lists that are to be concatenated + * @return the concatenation of all the lists + */ + def flatten[a](l: List[List[a]]): List[a] = l match { + case Nil => Nil + case head :: tail => head ::: flatten(tail) + } + + /** Transforms a list of pair into a pair of lists. + * + * @param l the list of pairs to unzip + * @return a pair of lists: the first list in the pair contains the list + */ + def unzip[a,b](l: List[Pair[a,b]]): Pair[List[a], List[b]] = l match { + case Nil => new Pair(Nil, Nil) + case Pair(f, s) :: tail => + val Pair(fs, ss) = unzip(tail); + Pair(f :: fs, s :: ss) + } + + /** Converts an iterator to a list + * + * @param it the iterator to convert + * @return a list that contains the elements returned by successive + * calls to <code>it.next</code> + */ + def fromIterator[a](it: Iterator[a]): List[a] = + if (it.hasNext) it.next :: fromIterator(it); + else Nil; + + /** Converts an array into a list. + * + * @param arr the array to convert + * @return a list that contains the same elements than <code>arr</code> + * in the same order + */ + def fromArray[a](arr: Array[a]): List[a] = fromArray(arr, 0, arr.length); + + /** Converts a range of an array into a list. + * + * @param arr the array to convert + * @param start the first index to consider + * @param len the lenght of the range to convert + * @return a list that contains the same elements than <code>arr</code> + * in the same order + */ + def fromArray[a](arr: Array[a], start: Int, len: Int): List[a] = { + var res: List[a] = Nil; + var i = start + len; + while (i > start) { + i = i - 1; + res = arr(i) :: res; + } + res + } + + /** Parses a string which contains substrings separated by a + * + * separator character and returns a list of all substrings. + * @param str the string to parse + * @param separator the separator character + * @return the list of substrings + */ + def fromString(str: String, separator: Char): List[String] = { + var words: List[String] = List(); + var pos = str.length(); + while (pos > 0) { + val pos1 = str.lastIndexOf(separator, pos - 1); + if (pos1 + 1 < pos) + words = str.substring(pos1 + 1, pos) :: words; + pos = pos1 + } + words + } +/* + var res: List[String] = Nil; + var start = 0; + var end = str.indexOf(separator); + while (end >= 0) { + if (end > start) + res = str.substring(start, end) :: res; + end = end + 1; + start = end; + end = str.indexOf(separator, end); + } + res.reverse + } +*/ + /** Returns the given string as a list of characters. + * + * @param str the string to convert. + * @return the string as a list of characters. + */ + def fromString(str: String): List[Char] = { + var res: List[Char] = Nil; + var i = str.length(); + while (i > 0) { + i = i - 1; + res = str.charAt(i) :: res; + } + res + } + + /** Returns the given list of characters as a string. + * + * @param xs the list to convert. + * @return the list in form of a string. + */ + def toString(xs: List[Char]): String = { + val sb = new StringBuffer(); + var ys = xs; + while (!ys.isEmpty) { + sb.append(ys.head); + ys = ys.tail; + } + sb.toString() + } + + /** Returns the list resulting from applying the given function <code>f</code> to + * corresponding elements of the argument lists. + * + * @param f function to apply to each pair of elements. + * @return <code>[f(a0,b0), ..., f(an,bn)]</code> if the lists are + * <code>[a0, ..., ak]</code>, <code>[b0, ..., bl]</code> and + * <code>n = min(k,l)</code> + */ + def map2[a,b,c](xs: List[a], ys: List[b])(f: (a, b) => c): List[c] = + if (xs.isEmpty || ys.isEmpty) Nil + else f(xs.head, ys.head) :: map2(xs.tail, ys.tail)(f); + + /** Like xs map f, but returns xs unchanged if function `f' maps all elements to themselves + */ + def mapConserve[a <: AnyRef](xs: List[a])(f: a => a): List[a] = { + if (xs.isEmpty) xs + else { + val head = xs.head; + val head1 = f(head); + val tail = xs.tail; + val tail1 = mapConserve(tail)(f); + if ((head1 eq head) && (tail1 eq tail)) xs else head1 :: tail1 + } + } + + /** Returns the list resulting from applying the given function <code>f</code> to + * corresponding elements of the argument lists. + * + * @param f function to apply to each pair of elements. + * @return <code>[f(a0,b0,c0), ..., f(an,bn,cn)]</code> if the lists are + * <code>[a0, ..., ak]</code>, <code>[b0, ..., bl]</code>, <code>[c0, ..., cm]</code> and + * <code>n = min(k,l,m)</code> + */ + def map3[a,b,c, d](xs: List[a], ys: List[b], zs: List[c])(f: (a, b, c) => d): List[d] = + if (xs.isEmpty || ys.isEmpty || zs.isEmpty) Nil + else f(xs.head, ys.head, zs.head) :: map3(xs.tail, ys.tail, zs.tail)(f); + + /** Tests whether the given predicate <code>p</code> holds + * for all corresponding elements of the argument lists. + * + * @param p function to apply to each pair of elements. + * @return <code>n == 0 || (p(a0,b0) && ... && p(an,bn))]</code> if the lists are + * <code>[a0, ..., ak]</code>, <code>[b0, ..., bl]</code> and + * <code>m = min(k,l)</code> + */ + def forall2[a,b](xs: List[a], ys: List[b])(f: (a, b) => boolean): boolean = + if (xs.isEmpty || ys.isEmpty) true + else f(xs.head, ys.head) && forall2(xs.tail, ys.tail)(f); + + /** Tests whether the given predicate <code>p</code> holds + * for some corresponding elements of the argument lists. + * + * @param p function to apply to each pair of elements. + * @return <code>n != 0 && (p(a0,b0) || ... || p(an,bn))]</code> if the lists are + * <code>[a0, ..., ak]</code>, <code>[b0, ..., bl]</code> and + * <code>m = min(k,l)</code> + */ + def exists2[a,b](xs: List[a], ys: List[b])(f: (a, b) => boolean): boolean = + if (xs.isEmpty || ys.isEmpty) false + else f(xs.head, ys.head) || exists2(xs.tail, ys.tail)(f); + + /** Transposes a list of lists. + * pre: All element lists have the same length. + */ + def transpose[a](xss: List[List[a]]): List[List[a]] = + if (xss.head.isEmpty) List() + else (xss map (xs => xs.head)) :: transpose(xss map (xs => xs.tail)); + + /** Lists with ordered elements are ordered + */ + implicit def list2ordered[a <% Ordered[a]](x: List[a]): Ordered[List[a]] = new Ordered[List[a]] { + def compareTo [b >: List[a] <% Ordered[b]](y: b): int = y match { + case y1: List[a] => compareLists(x, y1); + case _ => -(y compareTo x) + } + private def compareLists(xs: List[a], ys: List[a]): int = { + if (xs.isEmpty && ys.isEmpty) 0 + else if (xs.isEmpty) -1 + else if (ys.isEmpty) 1 + else { + val s = xs.head compareTo ys.head; + if (s != 0) s + else compareLists(xs.tail, ys.tail) + } + } + } + def view[a <% Ordered[a]](x: List[a]): Ordered[List[a]] = list2ordered(x); +} + +/** A trait representing an ordered collection of elements of type + * <code>a</code>. This class comes with two implementing case + * classes <code>scala.Nil</code> and <code>scala.::</code> that + * implement the abstract members <code>isEmpty</code>, + * <code>head</code> and <code>tail</code>. + * + * @author Martin Odersky and others + * @version 1.0, 16/07/2003 + */ +sealed abstract class List[+a] extends Seq[a] { + + /** Returns true if the list does not contain any elements. + * @return true, iff the list is empty. + */ + def isEmpty: Boolean; + + /** Returns this first element of the list. + * @return the first element of this list. + * @throws <code>java.lang.RuntimeException</code> if the list is empty. + */ + def head: a; + + /** Returns this list without its first element. + * @return this list without its first element. + * @throws <code>java.lang.RuntimeException</code> if the list is empty. + */ + def tail: List[a]; + + /** Add an element <code>x</code> at the beginning of this list. + * <p/> + * Ex:<br/> + * <code>1 :: [2, 3] = [2, 3].::(1) = [1, 2, 3]</code>. + * @param x the element to append. + * @return the list with <code>x</code> appended at the beginning. + */ + def ::[b >: a](x: b): List[b] = + new scala.::(x, this); + + /** Returns a list resulting from the concatenation of the given + * list <code>prefix</code> and this list. + * <p/> + * Ex:<br/> + * <code>[1, 2] ::: [3, 4] = [3, 4].:::([1, 2]) = [1, 2, 3, 4]</code>. + * @param prefix the list to concatenate at the beginning of this list. + * @return the concatenation of the two lists. + */ + def :::[b >: a](prefix: List[b]): List[b] = prefix match { + case Nil => this + case head :: tail => head :: (tail ::: this); + } + + /** Reverse the given prefix and append the current list to that. + * This function is equivalent to an application of <code>reverse</code> + * on the prefix followed by a call to <code>:::</code>, but more + * efficient (and tail recursive). + * @param prefix the prefix to reverse and then prepend + * @return the concatenation of the reversed prefix and the current list. + */ + def reverse_:::[b >: a](prefix: List[b]): List[b] = prefix match { + case Nil => this + case head :: tail => tail.reverse_:::(head :: this)//todo: remove type annotation + } + + /** Returns the number of elements in the list. + * + * @return the number of elements in the list. + */ + def length: Int = { + var xs = this; + var len = 0; + while (!xs.isEmpty) { + len = len + 1; + xs = xs.tail + } + len + } + + /** Creates a list with all indices in the list. This is + * equivalent to a call to <code>List.range(0, xs.length)</code>. + * + * @return a list of all indices in the list. + */ + def indices: List[Int] = { + def loop(i: Int, xs: List[a]): List[Int] = xs match { + case Nil => Nil + case _ :: ys => i :: loop(i + 1, ys) + } + loop(0, this) + } + + /** Returns the elements in the list as an iterator + * + * @return an iterator on the list elements. + */ + def elements: Iterator[a] = new Iterator[a] { + var current = List.this; + def hasNext: Boolean = !current.isEmpty; + def next: a = + if (!hasNext) + error("next on empty Iterator") + else { + val result = current.head; current = current.tail; result + } + } + + /** Transform this sequence into a list of all elements. + * + * @return a list which enumerates all elements of this sequence. + */ + override def toList: List[a] = this; + + /** Returns the list without its last element. + * + * @return the list without its last element. + * @throws <code>java.lang.RuntimeException</code> if the list is empty. + */ + def init: List[a] = this match { + case Nil => error("Nil.init") + case _ :: Nil => Nil + case head :: tail => head :: tail.init + } + + /** Returns the last element of this list. + * + * @return the last element of the list. + * @throws <code>java.lang.RuntimeException</code> if the list is empty. + */ + def last: a = this match { + case Nil => error("Nil.last") + case last :: Nil => last + case _ :: tail => tail.last + } + + /** Returns the <code>n</code> first elements of this list. + * + * @param n the number of elements to take. + * @return the <code>n</code> first elements of this list. + */ + override def take(n: Int): List[a] = + if (n == 0 || isEmpty) Nil + else head :: (tail take (n-1)); + + /** Returns the list without its <code>n</code> first elements. + * + * @param n the number of elements to drop. + * @return the list without its <code>n</code> first elements. + */ + override def drop(n: Int): List[a] = + if (n == 0 || isEmpty) this + else (tail drop (n-1)); + + /** Returns the rightmost <code>n</code> elements from this list. + * + * @param n the number of elements to take + * @return the suffix of length <code>n</code> of the list + * @throws <code>java.lang.RuntimeException</code> if the list is too short. + */ + def takeRight(n: Int): List[a] = { + def loop(lead: List[a], lag: List[a]): List[a] = lead match { + case Nil => lag + case _ :: tail => loop(tail, lag.tail) + } + loop(drop(n), this) + } + + /** Returns the list wihout its rightmost <code>n</code> elements. + * + * @param n the number of elements to take + * @return the suffix of length <code>n</code> of the list + * @throws <code>java.lang.RuntimeException</code> if the list is too short. + */ + def dropRight(n: Int): List[a] = { + def loop(lead: List[a], lag: List[a]): List[a] = lead match { + case Nil => Nil + case _ :: tail => lag.head :: loop(tail, lag.tail) + } + loop(drop(n), this) + } + + /** Split the list at a given point and return the two parts thus + * created. + * + * @param n the position at which to split + * @return a pair of lists composed of the first <code>n</code> + * elements, and the other elements. + */ + def splitAt(n: Int): Pair[List[a], List[a]] = + if (n == 0) Pair(Nil, this) + else { + val Pair(tail1, tail2) = tail splitAt (n-1); + Pair(head :: tail1, tail2) + } + + /** Returns the longest prefix of this list whose elements satisfy + * the predicate <code>p</code>. + * + * @param p the test predicate. + * @return the longest prefix of this list whose elements satisfy + * the predicate <code>p</code>. + */ + def takeWhile(p: a => Boolean): List[a] = + if (isEmpty || !p(head)) Nil + else head :: (tail takeWhile p); + + /** Returns the longest suffix of this list whose first element + * does not satisfy the predicate <code>p</code>. + * + * @param p the test predicate. + * @return the longest suffix of the list whose first element + * does not satisfy the predicate <code>p</code>. + */ + def dropWhile(p: a => Boolean): List[a] = + if (isEmpty || !p(head)) this + else tail dropWhile p; + + /** Returns the longest prefix of the list whose elements all satisfy + * the given predicate, and the rest of the list. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of the list whose + * elements all satisfy <code>p</code>, and the rest of the list. + */ + def span(p: a => Boolean): Pair[List[a], List[a]] = this match { + case Nil => Pair(Nil, Nil) + case head :: tail => + if (p(head)) { + val Pair(tail1, tail2) = tail span p; + Pair(head :: tail1, tail2) + } else + Pair(Nil, this) + } + + /** Like <code>span</code> but with the predicate inverted. + */ + def break(p: a => Boolean): Pair[List[a], List[a]] = span { x => !p(x) } + + /** Returns the <code>n</code>-th element of this list. The first element + * (head of the list) is at position 0. + * + * @param n index of the element to return + * @return the element at position <code>n</code> in this list. + * @throws <code>java.lang.RuntimeException</code> if the list is too short. + */ + def apply(n: Int): a = drop(n).head; + + /** Returns the list resulting from applying the given function <code>f</code> to each + * element of this list. + * + * @param f function to apply to each element. + * @return <code>[f(a0), ..., f(an)]</code> if this list is <code>[a0, ..., an]</code>. + */ + def map[b](f: a => b): List[b] = this match { + case Nil => Nil + case head :: tail => f(head) :: (tail map f) + } + + /** Apply a function to all the elements of the list, and return the + * reversed list of results. This is equivalent to a call to <code>map</code> + * followed by a call to <code>reverse</code>, but more efficient. + * + * @param f the function to apply to each elements. + * @return the reversed list of results. + */ + def reverseMap[b](f: a => b): List[b] = { + def loop(l: List[a], res: List[b]): List[b] = l match { + case Nil => res + case head :: tail => loop(tail, f(head) :: res) + } + loop(this, Nil) + } + + /** Apply the given function <code>f</code> to each element of this list + * (while respecting the order of the elements). + * + * @param f the treatment to apply to each element. + */ + override def foreach(f: a => Unit): Unit = { + def loop(xs: List[a]): Unit = xs match { + case Nil => () + case head :: tail => f(head); loop(tail) + } + loop(this) + } + + /** Returns all the elements of this list that satisfy the + * predicate <code>p</code>. The order of the elements is preserved. + * + * @param p the redicate used to filter the list. + * @return the elements of this list satisfying <code>p</code>. + */ + def filter(p: a => Boolean): List[a] = this match { + case Nil => this + case head :: tail => + if (p(head)) { + val tail1 = tail filter p; + if (tail eq tail1) this else head :: tail1 + } else tail filter p + } + + /** Removes all elements of the list which satisfy the predicate + * <code>p</code>. This is like <code>filter</code> with the + * predicate inversed. + * + * @param p the predicate to use to test elements + * @return the list without all elements which satisfy <code>p</code> + */ + def remove(p: a => Boolean): List[a] = this match { + case Nil => this + case head :: tail => + if (p(head)) tail remove p else head :: (tail remove p) + } + + /** Partition the list in two sub-lists according to a predicate. + * + * @param p the predicate on which to partition + * @return a pair of lists: the list of all elements which satisfy + * <code>p</code> and the list of all elements which do not. The + * relative order of the elements in the sub-lists is the same as in + * the original list. + */ + def partition(p: a => Boolean): Pair[List[a], List[a]] = this match { + case Nil => Pair(Nil, Nil) + case head :: tail => + val Pair(taily, tailn) = tail partition p; + if (p(head)) Pair(head :: taily, tailn) + else Pair(taily, head :: tailn) + } + + /** Sort the list according to the comparison function + * <code><(e1: a, e2: a) => Boolean</code>, + * which should be true iff e1 is smaller than e2. + * Note: The current implementation is inefficent for + * already sorted lists. + * + * @param lt the comparison function + * @return a list sorted according to the comparison function + * <code><(e1: a, e2: a) => Boolean</code>. + */ + def sort(lt : (a,a) => Boolean): List[a] = { + def sort_1(smaller: List[a], acc: List[a]): List[a] = + smaller match { + case Nil => + acc + case List(x) => + x::acc + case List(x, y) => + if (lt(x, y)) x::(y::acc) else y::x::acc + case List(x, y, z) => + if (lt(x, y)) { + if (lt(y, z)) x::y::z::acc + else if (lt(x, z)) x::z::y::acc + else z::x::y::acc + } else if (lt(x, z)) y::x::z::acc + else if (lt(z, y)) z::y::x::acc + else y::z::x::acc + case hd1::hd2::hd3::tail => { + val List(x, y, z) = sort_1(hd1::hd2::hd3::Nil, Nil); + val Pair(small, large) = tail.partition((e2) => lt(e2, y)); + sort_1(x::small, y::sort_1(z::large, acc)) + } + } + this match { + case Nil => + this + case List(x) => + this + case List(x, y) => + if (lt(x, y)) this else y::x::Nil + case List(x, y, z) => + if (lt(x, y)) { + if (lt(y, z)) this + else if (lt(x, z)) x::z::y::Nil + else z::x::y::Nil + } else if (lt(x, z)) y::x::z::Nil + else if (lt(z, y)) z::y::x::Nil + else y::z::x::Nil + case hd1::hd2::hd3::tail => { + val List(x, y, z) = sort_1(hd1::hd2::hd3::Nil, Nil); + val Pair(small,large) = tail.partition((e2) => lt(e2, y)); + sort_1(x::small, y::sort_1(z::large, Nil)); + } + } + } + + + /** Count the number of elements in the list which satisfy a predicate. + * + * @param p the predicate for which to count + * @return the number of elements satisfying the predicate <code>p</code>. + */ + def count(p: a => Boolean): Int = this match { + case Nil => 0 + case head :: tail => if (p(head)) 1 + (tail count p) else (tail count p) + } + + /** Tests if the predicate <code>p</code> is satisfied by all elements + * in this list. + * + * @param p the test predicate. + * @return True iff all elements of this list satisfy the predicate <code>p</code>. + */ + override def forall(p: a => Boolean): Boolean = + isEmpty || (p(head) && (tail forall p)); + + /** Tests the existence in this list of an element that satisfies the predicate + * <code>p</code>. + * + * @param p the test predicate. + * @return true iff there exists an element in this list that satisfies + * the predicate <code>p</code>. + */ + override def exists(p: a => Boolean): Boolean = + !isEmpty && (p(head) || (tail exists p)); + + /** Tests if the given value <code>elem</code> is a member of this + * iterable object. + * + * @param elem element whose membership has to be tested. + * @return True iff there is an element of this list which is + * equal (w.r.t. <code>==</code>) to <code>elem</code>. + */ + def contains(elem: Any): boolean = exists { x => x == elem } + + /** Find and return the first element of the list satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the list satisfying <code>p</code>, + * or <code>None</code> if none exists. + */ + override def find(p: a => Boolean): Option[a] = this match { + case Nil => None + case head :: tail => if (p(head)) Some(head) else tail find p + } + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from left to right, and starting with + * the value <code>z</code>. + * + * @return <code>op(... (op(op(z,a0),a1) ...), an)</code> if the list + * is <code>[a0, a1, ..., an]</code>. + */ + override def foldLeft[b](z: b)(f: (b, a) => b): b = this match { + case Nil => z + case x :: xs => xs.foldLeft[b](f(z, x))(f) + } + + /** Combines the elements of this list together using the binary + * operator <code>op</code>, from rigth to left, and starting with + * the value <code>z</code>. + * + * @return <code>a0 op (... op (an op z)...)</code> if the list + * is <code>[a0, a1, ..., an]</code>. + */ + override def foldRight[b](z: b)(f: (a, b) => b): b = this match { + case Nil => z + case x :: xs => f(x, xs.foldRight(z)(f)) + } + + def reduceLeft[b >: a](f: (b, b) => b): b = this match { + case Nil => error("Nil.reduceLeft") + case x :: xs => ((xs: List[b]) foldLeft (x: b))(f) + } + + def reduceRight[b >: a](f: (b, b) => b): b = this match { + case Nil => error("Nil.reduceRight") + case x :: Nil => x: b + case x :: xs => f(x, xs reduceRight f) + } + + /** Applies the given function <code>f</code> to each element of + * this list, then concatenates the results. + * + * @param f the function to apply on each element. + * @return <code>f(a0) ::: ... ::: f(an)</code> if this list is + * <code>[a0, ..., an]</code>. + */ + def flatMap[b](f: a => List[b]): List[b] = this match { + case Nil => Nil + case head :: tail => f(head) ::: (tail flatMap f) + } + + /** Reverses the elements of this list. + * <p/> + * Ex: <br/> + * <code>[1, 2, 3] reverse = [3, 2, 1]</code>. + * + * @return the elements of this list in reverse order. + */ + def reverse: List[a] = + foldLeft(Nil : List[a])((xs, x) => x :: xs); + + /** Returns a string representation of this list. The resulting string + * begins with the string <code>start</code> and is finished by the string + * <code>end</code>. Inside, the string representations of elements (w.r.t. + * the method <code>toString()</code>) are separated by the string + * <code>sep</code>. + * <p/> + * Ex: <br/> + * <code>List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"</code> + * + * @param start starting string. + * @param sep separator string. + * @param end ending string. + * @return a string representation of this list. + */ + def mkString(start: String, sep: String, end: String): String = this match { + case Nil => start + end + case last :: Nil => start + last + end + case fst :: tail => start + fst + sep + tail.mkString("", sep, end) + } + + override def toString() = mkString("List(", ",", ")"); + + /** Returns a list formed from this list and the specified list + * <code>that</code> by associating each element of the former with + * the element at the same position in the latter. + * + * @param <code>that</code> must have the same length as the self list. + * @return <code>[(a0,b0), ..., (an,bn)]</code> when + * <code>[a0, ..., an] zip [b0, ..., bn]</code> is invoked. + */ + def zip[b](that: List[b]): List[Pair[a,b]] = + if (this.isEmpty || that.isEmpty) Nil + else Pair(this.head, that.head) :: this.tail.zip(that.tail); + + /** Returns a list formed from this list and the specified list + * <code>that</code> by associating each element of the former with + * the element at the same position in the latter. + * + * @param <code>that</code> may have a different length as the self list. + * @param <code>thisElem</code> is used to fill up the resulting list if + * the self list is shorter than <code>that</code> + * @param <code>thatElem</code> is used to fill up the resulting list if + * <code>that</code> is shorter than the self list + * @return <code>[(a0,b0), ..., (an,bn), (elem,bn+1), ..., (elem,bm)]</code> + * when <code>[a0, ..., an] zip [b0, ..., bm]</code> is invoked where + * <code>m > n</code>. + */ + def zipAll[c >: a, b](that: List[b], thisElem: c, thatElem: b): List[Pair[c,b]] = + if (this.isEmpty && that.isEmpty) + Nil + else if (this.isEmpty) + List.make(that.length, thisElem) zip that + else if (that.isEmpty) + this zip List.make(this.length, thatElem) + else + Pair(this.head, that.head) :: this.tail.zipAll(that.tail, thisElem, thatElem); + + /** Computes the union of this list and the given list + * <code>that</code>. + * + * @param that the list of elements to add to the list. + * @return a list without doubles containing the elements of this + * list and those of the given list <code>that</code>. + */ + def union[b >: a](that: List[b]): List[b] = this match { + case Nil => that + case head :: tail => + if (that contains head) tail union that + else head :: (tail union that) + } + + /** Computes the difference between this list and the given list + * <code>that</code>. + * + * @param that the list of elements to remove from this list. + * @return this list without the elements of the given list <code>that</code>. + */ + def diff[b >: a](that: List[b]): List[b] = this match { + case Nil => Nil + case head :: tail => + if (that contains head) tail diff that + else head :: (tail diff that) + } + + /** Computes the intersection between this list and the given list + * <code>that</code>. + * + * @param that the list to intersect. + * @return the list of elements contained both in this list and + * in the given list <code>that</code>. + */ + def intersect[b >: a](that: List[b]): List[b] = filter(x => that contains x); + + /** Removes redundant elements from the list. Uses the method <code>==</code> + * to decide if two elements are identical. + * + * @return the list without doubles. + */ + def removeDuplicates: List[a] = this match { + case Nil => this + case head :: tail => + if (tail contains head) tail.removeDuplicates + else head :: tail.removeDuplicates + } +} + +/** The empty list. + * + * @author Martin Odersky + * @version 1.0, 15/07/2003 + */ +[SerialVersionUID(0 - 8256821097970055419L)] +case object Nil extends List[All] { + def isEmpty = true; + def head: All = error("head of empty list"); + def tail: List[All] = error("tail of empty list"); +} + +/** A non empty list characterized by a head and a tail. + * + * @author Martin Odersky + * @version 1.0, 15/07/2003 + */ +[SerialVersionUID(0L - 8476791151983527571L)] +final case class ::[+b](hd: b, tl: List[b]) extends List[b] { + def isEmpty: boolean = false; + def head: b = hd; + def tail: List[b] = tl; +} + diff --git a/src/library/scala/Long.cs b/src/library/scala/Long.cs new file mode 100644 index 0000000000..cf147b04b3 --- /dev/null +++ b/src/library/scala/Long.cs @@ -0,0 +1,108 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Long.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Long : AnyVal { + + public readonly long value; + + public Long (long value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return (other is Long) && value == ((Long)other).value; + } + public override int GetHashCode() + { + return value.GetHashCode(); + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Long;")] + public long __plus ( ) { return +value ; } + [Meta("method []scala.Long;")] + public long __minus ( ) { return -value ; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce ( ) { return value ; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + [Meta("method []scala.Float;")] + public float coerce (object dummy) { return value ; } + [Meta("method []scala.Long;")] + public long __tilde ( ) { return ~value ; } + + public long __less__less (int that) { return value << that; } + public long __less__less (long that) { return value << (int)that; } + public long __greater__greater(int that) { return value >> that; } + public long __greater__greater(long that) { return value >> (int)that; } + public long __greater__greater__greater(int that) { return (int)((ulong)value >>that); } + public long __greater__greater__greater(long that) { return (int)((ulong)value >>(int)that); } + + public bool __eq__eq (long that) { return value == that; } + public bool __bang__eq (long that) { return value != that; } + public bool __less (long that) { return value < that; } + public bool __greater (long that) { return value > that; } + public bool __less__eq (long that) { return value <= that; } + public bool __greater__eq(long that) { return value >= that; } + public long __plus (long that) { return value + that; } + public long __minus (long that) { return value - that; } + public long __times (long that) { return value * that; } + public long __div (long that) { return value / that; } + public long __percent (long that) { return value % that; } + public long __bar (long that) { return value | that; } + public long __amp (long that) { return value & that; } + public long __up (long that) { return value ^ that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Long.java b/src/library/scala/Long.java new file mode 100644 index 0000000000..bcaf38eb36 --- /dev/null +++ b/src/library/scala/Long.java @@ -0,0 +1,181 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Long extends AnyVal implements java.io.Serializable { + + public final long value; + + public Long (long value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Long && value == ((Long )other).value; + } + public int hashCode() { + long bits = value; + return (int)(bits ^ (bits >>> 32)); + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Long ; */ + public long $plus ( ) { return +value ; } + /** @meta method []scala.Long ; */ + public long $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + + /** @meta method []scala.Float ; */ + public float coerce ( ) { return value ; } + /** @meta method []scala.Long ; */ + public long $tilde ( ) { return ~value ; } + + public long $less$less (int that) { return value << that; } + public long $less$less (long that) { return value << that; } + public long $greater$greater(int that) { return value >> that; } + public long $greater$greater(long that) { return value >> that; } + public long $greater$greater$greater(int that) { return value >>>that; } + public long $greater$greater$greater(long that) { return value >>>that; } + + public boolean $eq$eq (long that) { return value == that; } + public boolean $bang$eq (long that) { return value != that; } + public boolean $less (long that) { return value < that; } + public boolean $greater (long that) { return value > that; } + public boolean $less$eq (long that) { return value <= that; } + public boolean $greater$eq(long that) { return value >= that; } + public long $plus (long that) { return value + that; } + public long $minus (long that) { return value - that; } + public long $times (long that) { return value * that; } + public long $div (long that) { return value / that; } + public long $percent (long that) { return value % that; } + public long $bar (long that) { return value | that; } + public long $amp (long that) { return value & that; } + public long $up (long that) { return value ^ that; } +/* + + public boolean $eq$eq (int that) { return value == that; } + public boolean $bang$eq (int that) { return value != that; } + public boolean $less (int that) { return value < that; } + public boolean $greater (int that) { return value > that; } + public boolean $less$eq (int that) { return value <= that; } + public boolean $greater$eq(int that) { return value >= that; } + public long $plus (int that) { return value + that; } + public long $minus (int that) { return value - that; } + public long $times (int that) { return value * that; } + public long $div (int that) { return value / that; } + public long $percent (int that) { return value % that; } + public long $bar (int that) { return value | that; } + public long $amp (int that) { return value & that; } + public long $up (int that) { return value ^ that; } + + public boolean $eq$eq (short that) { return value == that; } + public boolean $bang$eq (short that) { return value != that; } + public boolean $less (short that) { return value < that; } + public boolean $greater (short that) { return value > that; } + public boolean $less$eq (short that) { return value <= that; } + public boolean $greater$eq(short that) { return value >= that; } + public long $plus (short that) { return value + that; } + public long $minus (short that) { return value - that; } + public long $times (short that) { return value * that; } + public long $div (short that) { return value / that; } + public long $percent (short that) { return value % that; } + public long $bar (short that) { return value | that; } + public long $amp (short that) { return value & that; } + public long $up (short that) { return value ^ that; } + + public boolean $eq$eq (char that) { return value == that; } + public boolean $bang$eq (char that) { return value != that; } + public boolean $less (char that) { return value < that; } + public boolean $greater (char that) { return value > that; } + public boolean $less$eq (char that) { return value <= that; } + public boolean $greater$eq(char that) { return value >= that; } + public long $plus (char that) { return value + that; } + public long $minus (char that) { return value - that; } + public long $times (char that) { return value * that; } + public long $div (char that) { return value / that; } + public long $percent (char that) { return value % that; } + public long $bar (char that) { return value | that; } + public long $amp (char that) { return value & that; } + public long $up (char that) { return value ^ that; } + + public boolean $eq$eq (byte that) { return value == that; } + public boolean $bang$eq (byte that) { return value != that; } + public boolean $less (byte that) { return value < that; } + public boolean $greater (byte that) { return value > that; } + public boolean $less$eq (byte that) { return value <= that; } + public boolean $greater$eq(byte that) { return value >= that; } + public long $plus (byte that) { return value + that; } + public long $minus (byte that) { return value - that; } + public long $times (byte that) { return value * that; } + public long $div (byte that) { return value / that; } + public long $percent (byte that) { return value % that; } + public long $bar (byte that) { return value | that; } + public long $amp (byte that) { return value & that; } + public long $up (byte that) { return value ^ that; } +*/ +} diff --git a/src/library/scala/MatchError.scala b/src/library/scala/MatchError.scala new file mode 100644 index 0000000000..4979df8717 --- /dev/null +++ b/src/library/scala/MatchError.scala @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** ** +** $Id$ +\* */ +package scala; + + +/** This class implements errors which are thrown whenever an + * object doesn't match any pattern of a pattern matching + * expression. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 1.1, 05/03/2004 + */ +object MatchError { + + // todo: change pattern matcher so that dummy type parameter T can be removed. + def fail[T](source: String, line: Int): All = throw new MatchError(source, line); + + def report(source: String, line: Int, obj: Any) = + try { + throw new MatchError(source, line, obj.toString()) + } catch { + case e: MatchError => throw e + case e: Throwable => throw new MatchError(source, line) + } +} + +final class MatchError(msg: String) extends Error(msg) { + def this(source: String, line: Int) = + this(" in '" + source + "' at line " + line); + def this(source: String, line: Int, obj: String) = + this("for object " + obj + " in '" + source + "' at line " + line); +} diff --git a/src/library/scala/None.scala b/src/library/scala/None.scala new file mode 100644 index 0000000000..337426b1ee --- /dev/null +++ b/src/library/scala/None.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:None.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** This case object represents non-existent values. + * + * @author Martin Odersky + * @version 1.0, 16/07/2003 + */ +case object None extends Option[All]; diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala new file mode 100644 index 0000000000..c19748c875 --- /dev/null +++ b/src/library/scala/Option.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Option.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import Predef._; + + +/** This class represents optional values. Instances of <code>Option</code> + * are either instances of case class <code>Some</code> or it is case + * object <code>None</code>. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.0, 16/07/2003 + */ +trait Option[+A] extends Iterable[A] { + + def isEmpty: Boolean = this match { + case None => true + case _ => false + } + + def get: A = this match { + case None => error("None.get") + case Some(x) => x + } + + def get[B >: A](default: B): B = this match { + case None => default + case Some(x) => x + } + + def map[B](f: A => B): Option[B] = this match { + case None => None + case Some(x) => Some(f(x)) + } + + def flatMap[B](f: A => Option[B]): Option[B] = this match { + case None => None + case Some(x) => f(x) + } + + def filter(p: A => Boolean): Option[A] = this match { + case None => None + case Some(x) => if (p(x)) Some(x) else None + } + + override def foreach(f: A => Unit): Unit = this match { + case None => () + case Some(x) => f(x) + } + + def elements: Iterator[A] = this match { + case None => Iterator.empty + case Some(x) => Iterator.fromValues(x) + } + + def toList: List[A] = this match { + case None => List() + case Some(x) => List(x) + } + +} diff --git a/src/library/scala/Ordered.scala b/src/library/scala/Ordered.scala new file mode 100644 index 0000000000..b8ef8c0ab9 --- /dev/null +++ b/src/library/scala/Ordered.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Ordered.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** A trait for totally ordered data. + * + * @author Martin Odersky + * @version 1.0, 23/04/2004 + */ +trait Ordered[+a] { + + /** Result of comparing `this' with operand `that'. + * returns `x' where + * <code>x < 0</code> iff <code>this < that</code> + * <code>x == 0</code> iff <code>this == that</code> + * <code>x > 0</code> iff <code>this > that</code> + */ + def compareTo [b >: a <% Ordered[b]](that: b): Int; + + def < [b >: a <% Ordered[b]](that: b): Boolean = (this compareTo that) < 0; + + def > [b >: a <% Ordered[b]](that: b): Boolean = (this compareTo that) > 0; + + def <= [b >: a <% Ordered[b]](that: b): Boolean = (this compareTo that) <= 0; + + def >= [b >: a <% Ordered[b]](that: b): Boolean = (this compareTo that) >= 0; +} diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala new file mode 100644 index 0000000000..9bea5386c5 --- /dev/null +++ b/src/library/scala/PartialFunction.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:PartialFunction.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** A partial function of type <code>PartialFunction[A, B]</code> is a + * unary function where the domain does not include all values of type + * <code>A</code>. The function <code>isDefinedAt</code> allows to + * test dynamically, if a value is in the domain of the function. + * + * @author Martin Odersky + * @version 1.0, 16/07/2003 + */ +trait PartialFunction[-A, +B] extends AnyRef with Function1[A, B] { + + /** Checks if a value is contained in the functions domain. + * + * @param x the value to test + * @return true, iff <code>x</code> is in the domain of this function. + */ + def isDefinedAt(x: A): Boolean; +} diff --git a/src/library/scala/PartiallyOrdered.scala b/src/library/scala/PartiallyOrdered.scala new file mode 100644 index 0000000000..424e50b77d --- /dev/null +++ b/src/library/scala/PartiallyOrdered.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:PartiallyOrdered.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** A trait for partially ordered data. + * + * @author Martin Odersky + * @version 1.0, 23/04/2004 + */ +trait PartiallyOrdered[+a] { + + /** Result of comparing `this' with operand `that'. + * Returns `None' if operands are not comparable. + * If operands are comparable, returns `Some(x)' where + * <code>x < 0</code> iff <code>this < that</code> + * <code>x == 0</code> iff <code>this == that</code> + * <code>x > 0</code> iff <code>this > that</code> + */ + def tryCompareTo [b >: a <% PartiallyOrdered[b]](that: b): Option[int]; + + def < [b >: a <% PartiallyOrdered[b]](that: b): boolean = + (this tryCompareTo that) match { + case Some(x) if x < 0 => true + case _ => false + } + def > [b >: a <% PartiallyOrdered[b]](that: b): boolean = + (this tryCompareTo that) match { + case Some(x) if x > 0 => true + case _ => false + } + def <= [b >: a <% PartiallyOrdered[b]](that: b): boolean = + (this tryCompareTo that) match { + case Some(x) if x <= 0 => true + case _ => false + } + def >= [b >: a <% PartiallyOrdered[b]](that: b): boolean = + (this tryCompareTo that) match { + case Some(x) if x >= 0 => true + case _ => false + } +} diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala new file mode 100644 index 0000000000..94bd73b79d --- /dev/null +++ b/src/library/scala/Predef.scala @@ -0,0 +1,359 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Predef.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** The <code>Predef</code> object provides definitions that are + * accessible in all Scala compilation units without explicit + * qualification. + */ +object Predef { + + // aliases ------------------------------------------------------- + + type byte = scala.Byte; + type short = scala.Short; + type char = scala.Char; + type int = scala.Int; + type long = scala.Long; + type float = scala.Float; + type double = scala.Double; + type boolean = scala.Boolean; + type unit = scala.Unit; + + type String = java.lang.String; + type NullPointerException = java.lang.NullPointerException; + type Throwable = java.lang.Throwable; + + type Pair[+p, +q] = Tuple2[p, q]; + def Pair[a, b](x: a, y: b) = Tuple2(x, y); + + type Triple[+a, +b, +c] = Tuple3[a, b, c]; + def Triple[a, b, c](x: a, y: b, z: c) = Tuple3(x, y, z); + + def id[a](x: a): a = x; + def fst[a](x: a, y: Any): a = x; + def scd[a](x: Any, y: a): a = y; + + val namespace$default = ""; + val $scope = scala.xml.TopScope; + + type Function[-a,+b] = Function1[a,b]; + + // arrays ----------------------------------------------------------- + + /** Create an array with given elements. + * + * @param xs the elements to put in the array + * @return the array containing elements xs. + */ +/* + def Array[A](xs: A*): Array[A] = { + val array = new Array[A](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } +*/ + def Array[A <: AnyRef](xs: A*): Array[A] = { + val array = new Array[A](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: boolean*): Array[boolean] = { + val array = new Array[boolean](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: byte*): Array[byte] = { + val array = new Array[byte](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: short*): Array[short] = { + val array = new Array[short](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: char*): Array[char] = { + val array = new Array[char](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: int*): Array[int] = { + val array = new Array[int](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: long*): Array[long] = { + val array = new Array[long](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: float*): Array[float] = { + val array = new Array[float](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: double*): Array[double] = { + val array = new Array[double](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + def Array(xs: unit*): Array[unit] = { + val array = new Array[unit](xs.length); + var i = 0; + for (val x <- xs.elements) { array(i) = x; i = i + 1; } + array; + } + + // errors and asserts ------------------------------------------------- + + def error(message: String): All = throw new Error(message); + + def exit: All = exit(0); + + def exit(status: Int): All = { + java.lang.System.exit(status); + throw new Throwable() + } + + def assert(assertion: Boolean): Unit = { + if (!assertion) + throw new Error("assertion failed"); + } + + def assert(assertion: Boolean, message: Any): Unit = { + if (!assertion) + throw new Error("assertion failed: " + message); + } + + def assume(assumption: Boolean): Unit = { + if (!assumption) + throw new Error("assumption failed"); + } + + def assume(assumption: Boolean, message: Any): Unit = { + if (!assumption) + throw new Error("assumption failed: " + message); + } + + // views ------------------------------------------------------------- + + implicit def identity[a](x: a): a = x; + + implicit def int2ordered(x: int): Ordered[int] = new Ordered[int] with Proxy { + def self: Any = x; + def compareTo [b >: int <% Ordered[b]](y: b): int = y match { + case y1: int => + if (x < y1) -1 + else if (x > y1) 1 + else 0 + case _ => -(y compareTo x) + } + } + def view(x: int): Ordered[int] = int2ordered(x); + + implicit def char2ordered(x: char): Ordered[char] = new Ordered[char] with Proxy { + def self: Any = x; + def compareTo [b >: char <% Ordered[b]](y: b): int = y match { + case y1: char => + if (x < y1) -1 + else if (x > y1) 1 + else 0 + case _ => -(y compareTo x) + } + } + def view(x: char): Ordered[char] = char2ordered(x); + + implicit def long2ordered(x: long): Ordered[long] = new Ordered[long] with Proxy { + def self: Any = x; + def compareTo [b >: long <% Ordered[b]](y: b): int = y match { + case y1: long => + if (x < y1) -1 + else if (x > y1) 1 + else 0 + case _ => -(y compareTo x) + } + } + def view(x: long): Ordered[long] = long2ordered(x); + + implicit def float2ordered(x: float): Ordered[float] = new Ordered[float] with Proxy { + def self: Any = x; + def compareTo [b >: float <% Ordered[b]](y: b): int = y match { + case y1: float => + if (x < y1) -1 + else if (x > y1) 1 + else 0 + case _ => -(y compareTo x) + } + } + def view(x: float): Ordered[float] = float2ordered(x); + + implicit def double2ordered(x: double): Ordered[double] = new Ordered[double] with Proxy { + def self: Any = x; + def compareTo [b >: double <% Ordered[b]](y: b): int = y match { + case y1: double => + if (x < y1) -1 + else if (x > y1) 1 + else 0 + case _ => -(y compareTo x) + } + } + def view(x: double): Ordered[double] = double2ordered(x); + + implicit def boolean2ordered(x: boolean): Ordered[boolean] = new Ordered[boolean] with Proxy { + def self: Any = x; + def compareTo [b >: boolean <% Ordered[b]](y: b): int = y match { + case y1: boolean => + if (x == y1) 0 + else if (x) 1 + else -1 + case _ => -(y compareTo x) + } + } + def view(x: boolean): Ordered[boolean] = boolean2ordered(x); + + implicit def array2ordered[A <% Ordered[A]](xs: Array[A]): Ordered[Array[A]] = new Ordered[Array[A]] with Proxy { + def self: Any = xs; + def compareTo[B >: Array[A] <% Ordered[B]](that: B): Int = that match { + case ys: Array[A] => + var i, res = 0; + while ((i < xs.length) && (i < ys.length) && (res == 0)) { + res = xs(i) compareTo ys(i); + i = i + 1; + } + if (res != 0) res + else if (i < xs.length) 1 + else if (i < ys.length) -1 + else 0 + case _ => + -(that compareTo xs) + } + } + def view[A <% Ordered[A]](xs: Array[A]): Ordered[Array[A]] = array2ordered(xs); + + private def first(xs: Int*): Int = xs.elements.find(x => x != 0) match { + case Some(r) => r + case _ => 0 + } + + /* We can't bootstrap currently with the following views included. We have to + * wait for the next release... + * + implicit def view[A <% Ordered[A], B <% Ordered[B]](x: Tuple2[A, B]): Ordered[Tuple2[A, B]] = + new Ordered[Tuple2[A, B]] with Proxy(x) { + def compareTo[T >: Tuple2[A, B] <% Ordered[T]](y: T): Int = y match { + case y1: Tuple2[A, B] => first(x._1.compareTo(y1._1), + x._2.compareTo(y1._2)); + case _ => -(y compareTo x) + } + } + + implicit def view[A <% Ordered[A], B <% Ordered[B], C <% Ordered[C]] + (x: Tuple3[A, B, C]): Ordered[Tuple3[A, B, C]] = + new Ordered[Tuple3[A, B, C]] with Proxy(x) { + def compareTo[T >: Tuple3[A, B, C] <% Ordered[T]](y: T): Int = y match { + case y1: Tuple3[A, B, C] => first(x._1.compareTo(y1._1), + x._2.compareTo(y1._2), + x._3.compareTo(y1._3)); + case _ => -(y compareTo x) + } + } + + implicit def view[A <% Ordered[A], B <% Ordered[B], C <% Ordered[C], D <% Ordered[D]] + (x: Tuple4[A, B, C, D]): Ordered[Tuple4[A, B, C, D]] = + new Ordered[Tuple4[A, B, C, D]] with Proxy(x) { + def compareTo[T >: Tuple4[A, B, C, D] <% Ordered[T]](y: T): Int = y match { + case y1: Tuple4[A, B, C, D] => first(x._1.compareTo(y1._1), + x._2.compareTo(y1._2), + x._3.compareTo(y1._3), + x._4.compareTo(y1._4)); + case _ => -(y compareTo x) + } + } + + implicit def view[A <% Ordered[A], B <% Ordered[B], C <% Ordered[C], D <% Ordered[D], E <% Ordered[E]] + (x: Tuple5[A, B, C, D, E]): Ordered[Tuple5[A, B, C, D, E]] = + new Ordered[Tuple5[A, B, C, D, E]] with Proxy(x) { + def compareTo[T >: Tuple5[A, B, C, D, E] <% Ordered[T]](y: T): Int = y match { + case y1: Tuple5[A, B, C, D, E] => first(x._1.compareTo(y1._1), + x._2.compareTo(y1._2), + x._3.compareTo(y1._3), + x._4.compareTo(y1._4), + x._5.compareTo(y1._5)); + case _ => -(y compareTo x) + } + } + */ + + implicit def string2ordered(x: String): Ordered[String] = new Ordered[String] with Proxy { + def self: Any = x; + def compareTo [b >: String <% Ordered[b]](y: b): int = y match { + case y1: String => x compareTo y1; + case _ => -(y compareTo x) + } + } + def view(x: String): Ordered[String] = string2ordered(x); + + implicit def array2seq[A](xs: Array[A]): Seq[A] = new Seq[A] { + def length = xs.length; + def elements = Iterator.fromArray(xs); + def apply(n: Int) = xs(n); + override def hashCode(): Int = xs.hashCode(); + override def equals(y: Any): Boolean = (xs == y); + override protected def stringPrefix: String = "Array"; + } + def view[A](xs: Array[A]): Seq[A] = array2seq(xs); + + implicit def string2seq(str: String): Seq[Char] = new Seq[Char] { + def length = str.length(); + def elements = Iterator.fromString(str); + def apply(n: Int) = str.charAt(n); + override def hashCode(): Int = str.hashCode(); + override def equals(y: Any): Boolean = (str == y); + override protected def stringPrefix: String = "String"; + } + def view(x: String): Seq[Char] = string2seq(x); + + implicit def byte2short(x: byte): short = x.toShort; + implicit def byte2int(x: byte): int = x.toInt; + implicit def byte2long(x: byte): long = x.toLong; + implicit def byte2float(x: byte): float = x.toFloat; + implicit def byte2double(x: byte): double = x.toDouble; + + implicit def short2int(x: short): int = x.toInt; + implicit def short2long(x: short): long = x.toLong; + implicit def short2float(x: short): float = x.toFloat; + implicit def short2double(x: short): double = x.toDouble; + + implicit def char2int(x: char): int = x.toInt; + implicit def char2long(x: char): long = x.toLong; + implicit def char2float(x: char): float = x.toFloat; + implicit def char2double(x: char): double = x.toDouble; + + implicit def int2long(x: int): long = x.toLong; + implicit def int2float(x: int): float = x.toFloat; + implicit def int2double(x: int): double = x.toDouble; + + implicit def long2float(x: long): float = x.toFloat; + implicit def long2double(x: long): double = x.toDouble; + + implicit def float2double(x: float): double = x.toDouble; +} diff --git a/src/library/scala/Proxy.scala b/src/library/scala/Proxy.scala new file mode 100644 index 0000000000..5bf65c2ed4 --- /dev/null +++ b/src/library/scala/Proxy.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Proxy.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** This class implements a simple proxy that forwards all calls to + * methods of class <code>Any</code> to another object <code>self</code>. + * Please note that only those methods can be forwarded that are + * overridable and public. + * + * @author Matthias Zenger + * @version 1.0, 26/04/2004 + */ +trait Proxy { + def self: Any; + override def hashCode(): Int = self.hashCode(); + override def equals(y: Any): Boolean = self.equals(y); + override def toString(): String = self.toString(); +} diff --git a/src/library/scala/Ref.cs b/src/library/scala/Ref.cs new file mode 100644 index 0000000000..660b07321c --- /dev/null +++ b/src/library/scala/Ref.cs @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $OldId: Ref.java,v 1.2 2002/03/12 13:16:04 zenger Exp $ +// $Id:Ref.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class [?T] extends scala.AnyRef;")] + [Serializable] + public class Ref : object { + + [Meta("field ?T;")] + public object elem = null; + + [Meta("constr (?T);")] + public Ref(object x) + { + elem = x; + } + } +}
\ No newline at end of file diff --git a/src/library/scala/Ref.java b/src/library/scala/Ref.java new file mode 100644 index 0000000000..3c47ae5573 --- /dev/null +++ b/src/library/scala/Ref.java @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $OldId: Ref.java,v 1.2 2002/03/12 13:16:04 zenger Exp $ +// $Id$ + +package scala; + +/** @meta class [?T] extends java.lang.Object with java.io.Serializable; + */ +public class Ref extends java.lang.Object implements java.io.Serializable { + + /** @meta field ?T; + */ + public java.lang.Object elem = null; + + /** @meta constr (?T); + */ + public Ref(java.lang.Object x) { + elem = x; + } +} diff --git a/src/library/scala/ScalaObject.scala b/src/library/scala/ScalaObject.scala new file mode 100644 index 0000000000..21c1fdbfba --- /dev/null +++ b/src/library/scala/ScalaObject.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +trait ScalaObject extends AnyRef { + + /** This method is needed for optimizing pattern matching expressions + * which match on constructors of case classes. + */ + def $tag(): Int = 0; + +} diff --git a/src/library/scala/Seq.scala b/src/library/scala/Seq.scala new file mode 100644 index 0000000000..0298f5913e --- /dev/null +++ b/src/library/scala/Seq.scala @@ -0,0 +1,194 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Seq.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +import Predef._; + +object Seq { + + /** builds a singleton sequence + * @author buraq + */ + def single[A](x:A) = new Seq[A] { + def length = 1; + def elements = Iterator.single(x); + override def isDefinedAt(x: Int): Boolean = (x == 0); + def apply(i:Int) = x; // caller's responsibility to check isDefinedAt + } + + def view[A <% Ordered[A]](xs: Seq[A]): Ordered[Seq[A]] = new Ordered[Seq[A]] with Proxy { + def self: Any = xs; + def compareTo[B >: Seq[A] <% Ordered[B]](that: B): Int = that match { + case ys: Seq[A] => + var res = 0; + val xsit = xs.elements; + val ysit = ys.elements; + while (xsit.hasNext && ysit.hasNext && (res == 0)) { + res = xsit.next compareTo ysit.next; + } + if (res != 0) res else if (xsit.hasNext) 1 else -1 + case _ => + -(that compareTo xs) + } + } +} + + +/** Class <code>Seq[A]</code> represents finite sequences of elements + * of type <code>A</code>. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.0, 16/07/2003 + */ +[serializable] +trait Seq[+A] extends AnyRef with PartialFunction[Int, A] with Iterable[A] { + + /** Returns the length of the sequence. + * + * @return the sequence length. + */ + def length: Int; + + /** Returns the concatenation of two sequences. + * + * @return concatenation of this sequence with argument + * @author buraq + */ + def concat[B >: A](that:Seq[B]): Seq[B] = new Seq[B] { + def length = Seq.this.length + that.length; + def elements: Iterator[B] = Seq.this.elements.append(that.elements); + def apply(i:Int) = { + if(Seq.this.isDefinedAt(i)) + Seq.this.apply(i) + else + that.apply(i - Seq.this.length); + } + } + + /** Is this partial function defined for the index <code>x</code>? + * + * @return true, iff <code>x</code> is a legal sequence index. + */ + def isDefinedAt(x: Int): Boolean = (x >= 0) && (x < length); + + /** Returns the index of the first occurence of the specified + * object in this sequence. + * + * @param elem element to search for. + * @return the index in this sequence of the first occurence of the specified + * element, or -1 if the sequence does not contain this element. + */ + def indexOf[B >: A](elem: B): Int = { + val it = elements; + var i = 0; + var found = false; + while (!found && it.hasNext) { + if (it.next == elem) { + found = true; + } else { + i = i + 1 + } + } + if (found) i else -1; + } + + /** Returns the index of the last occurence of the specified + * element in this sequence, or -1 if the sequence does not + * contain this element. + * + * @param elem element to search for. + * @return the index in this sequence of the last occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def lastIndexOf[B >: A](elem: B): Int = { + var i = length; + var found = false; + while (!found && (i > 0)) { + i = i - 1; + if (this(i) == elem) { + found = true; + } + } + if (found) i else -1; + } + + /** Returns the sub-sequence starting from index <code>n</code>. + */ + def take(n: Int): Seq[A] = subseq(0, n); + + /** Returns a new sub-sequence that drops the first <code>n</code> + * elements of this sequence. + */ + def drop(n: Int): Seq[A] = subseq(n, length - n); + + /** Returns a subsequence starting from index <code>from</code> + * consisting of <code>len</code> elements. + */ + def subseq(from: Int, len: Int): Seq[A] = + if ((from + len) <= length) new Seq[A] { + def apply(n: Int): A = Seq.this.apply(n + from); + def length: Int = len; + def elements: Iterator[A] = new Iterator[A] { + var i = from; + def hasNext = (i < (from + len)); + def next = { + val res = Seq.this.apply(i); + i = i + 1; + res + } + } + } else + error("cannot create subsequence"); + + /** Fills the given array <code>xs</code> with the elements of + * this sequence starting at position <code>start</code>. + * + * @param xs the array to fill. + * @param start starting index. + * @return the given array <code>xs</code> filled with this list. + */ + def copyToArray[B >: A](xs: Array[B], start: Int): Array[B] = { + val it = elements; + var i = start; + while (it.hasNext) { + xs(i) = it.next; + i = i + 1; + } + xs + } + + /** Transform this sequence into a list of all elements. + * + * @return a list which enumerates all elements of this sequence. + */ + def toList: List[A] = elements.toList; + + /** Customizes the <code>toString</code> method. + * + * @return a string representation of this sequence. + */ + override def toString() = { + val iter = elements; + var res = stringPrefix + "("; + if (iter.hasNext) { + res = res + iter.next; + while (iter.hasNext) + res = res + ", " + iter.next; + } + res + ")" + } + + /** Defines the prefix of the string representation. + */ + protected def stringPrefix: String = "Seq"; +} + diff --git a/src/library/scala/SeqProxy.scala b/src/library/scala/SeqProxy.scala new file mode 100644 index 0000000000..aa4496a8fe --- /dev/null +++ b/src/library/scala/SeqProxy.scala @@ -0,0 +1,91 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:SeqProxy.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** Class <code>Seq[A]</code> represents finite sequences of elements + * of type <code>A</code>. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.0, 16/07/2003 + */ +trait SeqProxy[+A] extends Seq[A] with IterableProxy[A] { + + def self: Seq[A]; + + /** Returns the length of the sequence. + * + * @return the sequence length. + */ + def length: Int = self.length; + + /** Access element number <code>n</code>. + * + * @return the element at index <code>n</code>. + */ + def apply(n: Int): A = self.apply(n); + + /** Is this partial function defined for the index <code>x</code>? + * + * @return true, iff <code>x</code> is a legal sequence index. + */ + override def isDefinedAt(y: Int): Boolean = self.isDefinedAt(y); + + /** Returns the index of the first occurence of the specified + * object in this sequence. + * + * @param elem element to search for. + * @return the index in this sequence of the first occurence of the specified + * element, or -1 if the sequence does not contain this element. + */ + override def indexOf[B >: A](elem: B): Int = self.indexOf(elem); + + /** Returns the index of the last occurence of the specified + * element in this sequence, or -1 if the sequence does not + * contain this element. + * + * @param elem element to search for. + * @return the index in this sequence of the last occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + override def lastIndexOf[B >: A](elem: B): Int = self.lastIndexOf(elem); + + /** Returns the sub-sequence starting from index <code>n</code>. + */ + override def take(n: Int): Seq[A] = self.take(n); + + /** Returns a new sub-sequence that drops the first <code>n</code> + * elements of this sequence. + */ + override def drop(n: Int): Seq[A] = self.drop(n); + + /** Returns a subsequence starting from index <code>from</code> + * consisting of <code>len</code> elements. + */ + override def subseq(from: Int, len: Int): Seq[A] = self.subseq(from, len); + + /** Fills the given array <code>xs</code> with the elements of + * this sequence starting at position <code>start</code>. + * + * @param xs the array to fill. + * @param start starting index. + * @return the given array <code>xs</code> filled with the elements + * of this sequence. + */ + override def copyToArray[B >: A](xs: Array[B], start: Int): Array[B] = self.copyToArray(xs, start); + + /** Transform this sequence into a list of all elements. + * + * @return a list which enumerates all elements of this sequence. + */ + override def toList: List[A] = self.toList; +} diff --git a/src/library/scala/SerialVersionUID.scala b/src/library/scala/SerialVersionUID.scala new file mode 100644 index 0000000000..9b42c09411 --- /dev/null +++ b/src/library/scala/SerialVersionUID.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:SerialVersionUID.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +*/ + +package scala; + +case class SerialVersionUID(uid: Long) extends Attribute {} diff --git a/src/library/scala/Short.cs b/src/library/scala/Short.cs new file mode 100644 index 0000000000..5503b9360a --- /dev/null +++ b/src/library/scala/Short.cs @@ -0,0 +1,129 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Short.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + [Meta("class extends scala.AnyVal;")] + [Serializable] + public abstract class Short : AnyVal { + + public readonly short value; + + public Short(short value) + { + this.value = value; + } + + public override bool Equals(object other) + { + return (other is Short) && value == ((Short)other).value; + } + public override int GetHashCode() + { + return value; + } + public override string ToString() + { + return value.ToString(); + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + [Meta("method []scala.Int;")] + public int __plus () { return +value; } + [Meta("method []scala.Int;")] + public int __minus () { return -value; } + + public string __plus (string that) { return value + that; } + + public bool __eq__eq (double that) { return value == that; } + public bool __bang__eq (double that) { return value != that; } + public bool __less (double that) { return value < that; } + public bool __greater (double that) { return value > that; } + public bool __less__eq (double that) { return value <= that; } + public bool __greater__eq(double that) { return value >= that; } + public double __plus (double that) { return value + that; } + public double __minus (double that) { return value - that; } + public double __times (double that) { return value * that; } + public double __div (double that) { return value / that; } + public double __percent (double that) { return value % that; } + + [Meta("method []scala.Double;")] + public double coerce ( ) { return value; } + + public bool __eq__eq (float that) { return value == that; } + public bool __bang__eq (float that) { return value != that; } + public bool __less (float that) { return value < that; } + public bool __greater (float that) { return value > that; } + public bool __less__eq (float that) { return value <= that; } + public bool __greater__eq(float that) { return value >= that; } + public float __plus (float that) { return value + that; } + public float __minus (float that) { return value - that; } + public float __times (float that) { return value * that; } + public float __div (float that) { return value / that; } + public float __percent (float that) { return value % that; } + + [Meta("method []scala.Float;")] + public float coerce (float dummy) { return value ; } + [Meta("method []scala.Int;")] + public int __tilde ( ) { return ~value ; } + + public int __less__less (int that) { return value << that; } + public int __less__less (long that) { return value << (int)that; } + public int __greater__greater(int that) { return value >> that; } + public int __greater__greater(long that) { return value >> (int)that; } + public int __greater__greater__greater(int that) { return (int)((uint)value >>that); } + public int __greater__greater__greater(long that) { return (int)((uint)value >>(int)that); } + + public bool __eq__eq (long that) { return value == that; } + public bool __bang__eq (long that) { return value != that; } + public bool __less (long that) { return value < that; } + public bool __greater (long that) { return value > that; } + public bool __less__eq (long that) { return value <= that; } + public bool __greater__eq(long that) { return value >= that; } + public long __plus (long that) { return value + that; } + public long __minus (long that) { return value - that; } + public long __times (long that) { return value * that; } + public long __div (long that) { return value / that; } + public long __percent (long that) { return value % that; } + public long __bar (long that) { return value | that; } + public long __amp (long that) { return value & that; } + public long __up (long that) { return value ^ that; } + + [Meta("method []scala.Long;")] + public long coerce (long dummy) { return value ; } + + public bool __eq__eq (int that) { return value == that; } + public bool __bang__eq (int that) { return value != that; } + public bool __less (int that) { return value < that; } + public bool __greater (int that) { return value > that; } + public bool __less__eq (int that) { return value <= that; } + public bool __greater__eq(int that) { return value >= that; } + public int __plus (int that) { return value + that; } + public int __minus (int that) { return value - that; } + public int __times (int that) { return value * that; } + public int __div (int that) { return value / that; } + public int __percent (int that) { return value % that; } + public int __bar (int that) { return value | that; } + public int __amp (int that) { return value & that; } + public int __up (int that) { return value ^ that; } + + [Meta("method []scala.Int;")] + public int coerce (int dummy) { return value ; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Short.java b/src/library/scala/Short.java new file mode 100644 index 0000000000..e800678202 --- /dev/null +++ b/src/library/scala/Short.java @@ -0,0 +1,186 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +/** @meta class extends scala.AnyVal; */ +public abstract class Short extends AnyVal implements java.io.Serializable { + + public final short value; + + public Short (short value) { + this.value = value; + } + + public boolean equals(java.lang.Object other) { + return other instanceof Short && value == ((Short )other).value; + } + public int hashCode() { + int bits = value; + return bits; + } + public String toString() { + return String.valueOf(value); + } + + /** @meta method []scala.Byte; */ + public byte toByte() { return (byte)value; } + + /** @meta method []scala.Short; */ + public short toShort() { return (short)value; } + + /** @meta method []scala.Char; */ + public char toChar() { return (char)value; } + + /** @meta method []scala.Int; */ + public int toInt() { return (int)value; } + + /** @meta method []scala.Long; */ + public long toLong() { return (long)value; } + + /** @meta method []scala.Float; */ + public float toFloat() { return (float)value; } + + /** @meta method []scala.Double; */ + public double toDouble() { return (double)value; } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + + /** @meta method []scala.Int ; */ + public int $plus ( ) { return +value ; } + /** @meta method []scala.Int ; */ + public int $minus ( ) { return -value ; } + + public String $plus (String that) { return value + that; } + + public boolean $eq$eq (double that) { return value == that; } + public boolean $bang$eq (double that) { return value != that; } + public boolean $less (double that) { return value < that; } + public boolean $greater (double that) { return value > that; } + public boolean $less$eq (double that) { return value <= that; } + public boolean $greater$eq(double that) { return value >= that; } + public double $plus (double that) { return value + that; } + public double $minus (double that) { return value - that; } + public double $times (double that) { return value * that; } + public double $div (double that) { return value / that; } + public double $percent (double that) { return value % that; } + + /** @meta method []scala.Double ; */ + public double coerce ( ) { return value ; } + + public boolean $eq$eq (float that) { return value == that; } + public boolean $bang$eq (float that) { return value != that; } + public boolean $less (float that) { return value < that; } + public boolean $greater (float that) { return value > that; } + public boolean $less$eq (float that) { return value <= that; } + public boolean $greater$eq(float that) { return value >= that; } + public float $plus (float that) { return value + that; } + public float $minus (float that) { return value - that; } + public float $times (float that) { return value * that; } + public float $div (float that) { return value / that; } + public float $percent (float that) { return value % that; } + + /** @meta method []scala.Float ; */ + public float coerce ( ) { return value ; } + /** @meta method []scala.Int ; */ + public int $tilde ( ) { return ~value ; } + + public int $less$less (int that) { return value << that; } + public int $less$less (long that) { return value << that; } + public int $greater$greater(int that) { return value >> that; } + public int $greater$greater(long that) { return value >> that; } + public int $greater$greater$greater(int that) { return value >>>that; } + public int $greater$greater$greater(long that) { return value >>>that; } + + public boolean $eq$eq (long that) { return value == that; } + public boolean $bang$eq (long that) { return value != that; } + public boolean $less (long that) { return value < that; } + public boolean $greater (long that) { return value > that; } + public boolean $less$eq (long that) { return value <= that; } + public boolean $greater$eq(long that) { return value >= that; } + public long $plus (long that) { return value + that; } + public long $minus (long that) { return value - that; } + public long $times (long that) { return value * that; } + public long $div (long that) { return value / that; } + public long $percent (long that) { return value % that; } + public long $bar (long that) { return value | that; } + public long $amp (long that) { return value & that; } + public long $up (long that) { return value ^ that; } + + /** @meta method []scala.Long ; */ + public long coerce ( ) { return value ; } + + public boolean $eq$eq (int that) { return value == that; } + public boolean $bang$eq (int that) { return value != that; } + public boolean $less (int that) { return value < that; } + public boolean $greater (int that) { return value > that; } + public boolean $less$eq (int that) { return value <= that; } + public boolean $greater$eq(int that) { return value >= that; } + public int $plus (int that) { return value + that; } + public int $minus (int that) { return value - that; } + public int $times (int that) { return value * that; } + public int $div (int that) { return value / that; } + public int $percent (int that) { return value % that; } + public int $bar (int that) { return value | that; } + public int $amp (int that) { return value & that; } + public int $up (int that) { return value ^ that; } + + /** @meta method []scala.Int ; */ + public int coerce ( ) { return value ; } + + + public boolean $eq$eq (char that) { return value == that; } + public boolean $bang$eq (char that) { return value != that; } + public boolean $less (char that) { return value < that; } + public boolean $greater (char that) { return value > that; } + public boolean $less$eq (char that) { return value <= that; } + public boolean $greater$eq(char that) { return value >= that; } + public int $plus (char that) { return value + that; } + public int $minus (char that) { return value - that; } + public int $times (char that) { return value * that; } + public int $div (char that) { return value / that; } + public int $percent (char that) { return value % that; } + public int $bar (char that) { return value | that; } + public int $amp (char that) { return value & that; } + public int $up (char that) { return value ^ that; } + + public boolean $eq$eq (short that) { return value == that; } + public boolean $bang$eq (short that) { return value != that; } + public boolean $less (short that) { return value < that; } + public boolean $greater (short that) { return value > that; } + public boolean $less$eq (short that) { return value <= that; } + public boolean $greater$eq(short that) { return value >= that; } + public int $plus (short that) { return value + that; } + public int $minus (short that) { return value - that; } + public int $times (short that) { return value * that; } + public int $div (short that) { return value / that; } + public int $percent (short that) { return value % that; } + public int $bar (short that) { return value | that; } + public int $amp (short that) { return value & that; } + public int $up (short that) { return value ^ that; } + + public boolean $eq$eq (byte that) { return value == that; } + public boolean $bang$eq (byte that) { return value != that; } + public boolean $less (byte that) { return value < that; } + public boolean $greater (byte that) { return value > that; } + public boolean $less$eq (byte that) { return value <= that; } + public boolean $greater$eq(byte that) { return value >= that; } + public int $plus (byte that) { return value + that; } + public int $minus (byte that) { return value - that; } + public int $times (byte that) { return value * that; } + public int $div (byte that) { return value / that; } + public int $percent (byte that) { return value % that; } + public int $bar (byte that) { return value | that; } + public int $amp (byte that) { return value & that; } + public int $up (byte that) { return value ^ that; } +} diff --git a/src/library/scala/Some.scala b/src/library/scala/Some.scala new file mode 100644 index 0000000000..4d5d210ab8 --- /dev/null +++ b/src/library/scala/Some.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Some.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** Class <code>Option[A]</code> represents existing values of type + * <code>A</code>. + * + * @author Martin Odersky + * @version 1.0, 16/07/2003 + */ +final case class Some[+A1](x: A1) extends Option[A1]; diff --git a/src/library/scala/Stream.scala b/src/library/scala/Stream.scala new file mode 100644 index 0000000000..a281577bf8 --- /dev/null +++ b/src/library/scala/Stream.scala @@ -0,0 +1,243 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Stream.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +/** + * The object <code>Stream</code> provides helper functions + * to manipulate streams. + * + * @author Martin Odersky, Matthias Zenger + * @version 1.1 08/08/03 + */ +object Stream { + + val empty: Stream[All] = new Stream[All] { + def isEmpty = true; + def head: All = error("head of empty stream"); + def tail: Stream[All] = error("tail of empty stream"); + def printElems(buf: StringBuffer, prefix: String): StringBuffer = buf; + } + + def cons[a](hd: a, tl: => Stream[a]) = new Stream[a] { + def isEmpty = false; + def head = hd; + private var tlVal: Stream[a] = _; + private var tlDefined = false; + def tail: Stream[a] = { + if (!tlDefined) { tlVal = tl; tlDefined = true; } + tlVal + } + def printElems(buf: StringBuffer, prefix: String): StringBuffer = { + val buf1 = buf.append(prefix).append(hd); + if (tlDefined) printElems(buf1, ", ") else buf1 append ", ?"; + } + } + + def fromIterator[a](it: Iterator[a]): Stream[a] = + if (it.hasNext) cons(it.next, fromIterator(it)) else empty; + + def concat[a](xs: Seq[Stream[a]]): Stream[a] = concat(xs.elements); + + def concat[a](xs: Iterator[Stream[a]]): Stream[a] = { + if (xs.hasNext) xs.next append concat(xs) + else empty; + } + + /** + * Create a stream with element values + * <code>v<sub>n+1</sub> = v<sub>n</sub> + 1</code> + * where <code>v<sub>0</sub> = start</code> + * and <code>v<sub>i</sub> < end</code>. + * + * @param start the start value of the stream + * @param end the end value of the stream + * @return the stream starting at value <code>start</code>. + */ + def range(start: Int, end: Int): Stream[Int] = + range(start, end, 1); + + /** + * Create a stream with element values + * <code>v<sub>n+1</sub> = v<sub>n</sub> + step</code> + * where <code>v<sub>0</sub> = start</code> + * and <code>v<sub>i</sub> < end</code>. + * + * @param start the start value of the stream + * @param end the end value of the stream + * @param step the increment value of the stream + * @return the stream starting at value <code>start</code>. + */ + def range(start: Int, end: Int, step: Int): Stream[Int] = { + def loop(lo: Int): Stream[Int] = + if (lo >= end) empty + else cons(lo, loop(lo + step)); + loop(start) + } + + /** + * Create a stream with element values + * <code>v<sub>n+1</sub> = step(v<sub>n</sub>)</code> + * where <code>v<sub>0</sub> = start</code> + * and <code>v<sub>i</sub> < end</code>. + * + * @param start the start value of the stream + * @param end the end value of the stream + * @param step the increment function of the stream + * @return the stream starting at value <code>start</code>. + */ + def range(start: Int, end: Int, step: Int => Int): Stream[Int] = { + def loop(lo: Int): Stream[Int] = + if (lo >= end) empty + else cons(lo, loop(step(lo))); + loop(start) + } +} + +/** + * <p>The class <code>Stream</code> implements lazy lists where elements + * are only evaluated when they are needed. Here is an example:</p> + * <pre> + * <b>object</b> Main <b>with</b> Application { + * + * <b>def</b> from(n: Int): Stream[Int] = + * Stream.cons(n, from(n + 1)); + * + * <b>def</b> sieve(s: Stream[Int]): Stream[Int] = + * Stream.cons(s.head, sieve(s.tail filter { x => x % s.head != 0 })); + * + * <b>def</b> primes = sieve(from(2)); + * + * primes take 10 print + * } + * </pre> + * + * @author Martin Odersky, Matthias Zenger + * @version 1.1 08/08/03 + */ +trait Stream[+a] extends Seq[a] { + + def isEmpty: Boolean; + def head: a; + def tail: Stream[a]; + + def length: int = if (isEmpty) 0 else tail.length + 1; + + def append[b >: a](rest: => Stream[b]): Stream[b] = + if (isEmpty) rest + else Stream.cons(head, tail.append(rest)); + + def elements: Iterator[a] = new Iterator[a] { + var current = Stream.this; + def hasNext: boolean = !current.isEmpty; + def next: a = { val result = current.head; current = current.tail; result } + } + + def init: Stream[a] = + if (isEmpty) error("Stream.empty.init") + else if (tail.isEmpty) Stream.empty + else Stream.cons(head, tail.init); + + def last: a = + if (isEmpty) error("Stream.empty.last") + else if (tail.isEmpty) head + else tail.last; + + override def take(n: int): Stream[a] = + if (n == 0) Stream.empty + else Stream.cons(head, tail.take(n-1)); + + override def drop(n: int): Stream[a] = + if (n == 0) this + else tail.drop(n-1); + + def apply(n: int) = drop(n).head; + def at(n: int) = drop(n).head; + + def takeWhile(p: a => Boolean): Stream[a] = + if (isEmpty || !p(head)) Stream.empty + else Stream.cons(head, tail.takeWhile(p)); + + def dropWhile(p: a => Boolean): Stream[a] = + if (isEmpty || !p(head)) this + else tail.dropWhile(p); + + def map[b](f: a => b): Stream[b] = + if (isEmpty) Stream.empty + else Stream.cons(f(head), tail.map(f)); + + override def foreach(f: a => unit): unit = + if (isEmpty) {} + else { f(head); tail.foreach(f) } + + def filter(p: a => Boolean): Stream[a] = + if (isEmpty) this + else if (p(head)) Stream.cons(head, tail.filter(p)) + else tail.filter(p); + + override def forall(p: a => Boolean): Boolean = + isEmpty || (p(head) && tail.forall(p)); + + override def exists(p: a => Boolean): Boolean = + !isEmpty && (p(head) || tail.exists(p)); + + override def foldLeft[b](z: b)(f: (b, a) => b): b = + if (isEmpty) z + else tail.foldLeft[b](f(z, head))(f); + + override def foldRight[b](z: b)(f: (a, b) => b): b = + if (isEmpty) z + else f(head, tail.foldRight(z)(f)); + + def reduceLeft[b >: a](f: (b, b) => b): b = + if (isEmpty) error("Stream.empty.reduceLeft") + else ((tail: Stream[b]) foldLeft (head: b))(f); + + def reduceRight[b >: a](f: (b, b) => b): b = + if (isEmpty) error("Stream.empty.reduceRight") + else if (tail.isEmpty) head: b + else f(head, tail.reduceRight(f)); + + def flatMap[b](f: a => Stream[b]): Stream[b] = + if (isEmpty) Stream.empty + else f(head).append(tail.flatMap(f)); + + def reverse: Stream[a] = + foldLeft(Stream.empty: Stream[a])((xs, x) => Stream.cons(x, xs)); + + // The following method is not compilable without run-time type + // information. It should therefore be left commented-out for + // now. + // def toArray: Array[a] = { + // val xs = new Array[a](length); + // copyToArray(xs, 0); + // xs + // } + + override def copyToArray[b >: a](xs: Array[b], start: int): Array[b] = + if (isEmpty) xs + else { xs(start) = head; tail.copyToArray(xs, start + 1) } + + def zip[b](that: Stream[b]): Stream[Tuple2[a, b]] = + if (this.isEmpty || that.isEmpty) Stream.empty + else Stream.cons(Tuple2(this.head, that.head), this.tail.zip(that.tail)); + + def print: unit = + if (isEmpty) Console.println("Stream.empty") + else { + Console.print(head); + Console.print(", "); + tail.print + } + + override def toString() = + "Stream(" + printElems(new StringBuffer(), "") + ")"; + + def printElems(buf: StringBuffer, prefix: String): StringBuffer; +} diff --git a/src/library/scala/Symbol.scala b/src/library/scala/Symbol.scala new file mode 100644 index 0000000000..4e5805f545 --- /dev/null +++ b/src/library/scala/Symbol.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:Symbol.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + + +/** Instances of <code>Symbol</code> can be created easily with + * Scala's built-in quote mechanism. For instance, the Scala term + * <code>'mysym</code> will invoke the constructor of the + * <code>Symbol</code> class in the following way: + * <code>new Symbol("mysym")</code>. . + * + * @author Martin Odersky + * @version 1.7, 08/12/2003 + */ +final case class Symbol(name: String) { + + /** Converts this symbol to a string. + */ + override def toString(): String = { + "'" + name + } + +} diff --git a/src/library/scala/Tuple1.scala b/src/library/scala/Tuple1.scala new file mode 100644 index 0000000000..620ad276bc --- /dev/null +++ b/src/library/scala/Tuple1.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple1[+T1](_1: T1) { + override def toString(): String = "(" + _1 + ")"; +} diff --git a/src/library/scala/Tuple2.scala b/src/library/scala/Tuple2.scala new file mode 100644 index 0000000000..e830a4f769 --- /dev/null +++ b/src/library/scala/Tuple2.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple2[+T1, +T2](_1: T1, _2: T2) { + override def toString(): String = "(" + _1 + "," + _2 + ")"; +} diff --git a/src/library/scala/Tuple3.scala b/src/library/scala/Tuple3.scala new file mode 100644 index 0000000000..53ce97a5b1 --- /dev/null +++ b/src/library/scala/Tuple3.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + ")"; +} diff --git a/src/library/scala/Tuple4.scala b/src/library/scala/Tuple4.scala new file mode 100644 index 0000000000..419afd7eb1 --- /dev/null +++ b/src/library/scala/Tuple4.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple4[+T1, +T2, +T3, +T4](_1: T1, _2: T2, _3: T3, _4: T4) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + ")"; +} diff --git a/src/library/scala/Tuple5.scala b/src/library/scala/Tuple5.scala new file mode 100644 index 0000000000..24bca2d443 --- /dev/null +++ b/src/library/scala/Tuple5.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple5[+T1, +T2, +T3, +T4, +T5](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + ")"; +} diff --git a/src/library/scala/Tuple6.scala b/src/library/scala/Tuple6.scala new file mode 100644 index 0000000000..205afce10d --- /dev/null +++ b/src/library/scala/Tuple6.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple6[+T1, +T2, +T3, +T4, +T5, +T6](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + ")"; +} diff --git a/src/library/scala/Tuple7.scala b/src/library/scala/Tuple7.scala new file mode 100644 index 0000000000..61dcac85b5 --- /dev/null +++ b/src/library/scala/Tuple7.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple7[+T1, +T2, +T3, +T4, +T5, +T6, +T7](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + ")"; +} diff --git a/src/library/scala/Tuple8.scala b/src/library/scala/Tuple8.scala new file mode 100644 index 0000000000..6857e876d3 --- /dev/null +++ b/src/library/scala/Tuple8.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + ")"; +} diff --git a/src/library/scala/Tuple9.scala b/src/library/scala/Tuple9.scala new file mode 100644 index 0000000000..50b3c66b40 --- /dev/null +++ b/src/library/scala/Tuple9.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala; + +case class Tuple9[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9) { + override def toString(): String = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + "," + _6 + "," + _7 + "," + _8 + "," + _9 + ")"; +} diff --git a/src/library/scala/Unit.cs b/src/library/scala/Unit.cs new file mode 100644 index 0000000000..44fd1aa3ca --- /dev/null +++ b/src/library/scala/Unit.cs @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:Unit.cs 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +using System; +using scala.runtime; + +namespace scala +{ + + public abstract class Unit : AnyVal { + + public void value() {} + + public Unit() {} + + public override bool Equals(object other) + { + return other is Unit; + } + public override int GetHashCode() + { + int bits = 4041; + return bits; + } + public override string ToString() + { + return "()"; + } + + [Meta("method (scala.Any)scala.Boolean;")] + public bool __eq__eq (object other) { return Equals(other); } + [Meta("method (scala.Any)scala.Boolean;")] + public bool __bang__eq(object other) { return !Equals(other); } + + public string __plus (string that) { return this + that; } + + } +}
\ No newline at end of file diff --git a/src/library/scala/Unit.java b/src/library/scala/Unit.java new file mode 100644 index 0000000000..dd50889c10 --- /dev/null +++ b/src/library/scala/Unit.java @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala; + +public abstract class Unit extends AnyVal { + + public final void value() {} + + public Unit ( ) { + + } + + public boolean equals(java.lang.Object other) { + return other instanceof Unit; + } + public int hashCode() { + int bits = 4041; + return bits; + } + public String toString() { + return "()"; + } + + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $eq$eq (java.lang.Object other) { return equals(other); } + /** @meta method (scala.Any)scala.Boolean; */ + public boolean $bang$eq(java.lang.Object other) { return !equals(other); } + +} diff --git a/src/library/scala/_trait_.scala b/src/library/scala/_trait_.scala new file mode 100644 index 0000000000..087508da64 --- /dev/null +++ b/src/library/scala/_trait_.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +*/ + +// $Id:_trait_.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +package scala; + +/** Temporary class. + * When this appears in the attribute list of an abstract class, the class + * is assumed to be a trait. Used to ensure that code that compiles under + * (old) <code>scalac</code> can also compile under <code>nsc</code>. + */ +class _trait_ extends Attribute {} diff --git a/src/library/scala/cloneable.scala b/src/library/scala/cloneable.scala new file mode 100644 index 0000000000..4067a3d732 --- /dev/null +++ b/src/library/scala/cloneable.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:cloneable.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +*/ + +package scala; + +class cloneable extends Attribute {} diff --git a/src/library/scala/collection/BitSet.scala b/src/library/scala/collection/BitSet.scala new file mode 100644 index 0000000000..bd5855c1de --- /dev/null +++ b/src/library/scala/collection/BitSet.scala @@ -0,0 +1,82 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection; + +/** The class <code>BitSet</code> ... + * + * @author Burak Emir, Stephane Micheloud + * @version 1.0 + */ +[_trait_] abstract class BitSet extends AnyRef with Function1[Int,Boolean] { + + /** number of bits in this bitset */ + def size: Int; + + /** returns true if bit i is set */ + def apply(i: Int): Boolean; + + /** returns an iterator over the truth values of all bits */ + final def booleanElements: Iterator[Boolean] = new Iterator[Boolean] { + var i = 0; + def hasNext: Boolean = i < size; + def next: Boolean = { i = i + 1; apply(i-1) } + } + + /** + * Returns the subset of <code>[0..size]</code> whose elements are + * indices of bits set to <code>v</code>. + * + * @param v + */ + final def toSet(v: Boolean) = { + var res = new immutable.TreeSet[Int](); + var j = 0; + while (j < size) { + if (v == apply(j)) + res = res + j; + j = j + 1; + } + res + } + + /** + * Checks if two bitsets are structurally identical. + * + * @return true, iff both bitsets contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = ( + that.isInstanceOf[BitSet] && + { val other = that.asInstanceOf[BitSet]; + ( size == other.size) && ( + Iterator.range(0, size) forall { i => apply(i) == other.apply(i)} + ) + } + ); + + /** + * applies f to any index which is set to true. + */ + def foreach(f: Int => Unit): Unit = toSet(true).foreach(f); + + /** + * Returns a string representation of this bitset in hexadecimal form, + * e.g. the bitset 001100000001 (12 bits) is represented as "{0, 8, 9}". + * + * @return the string representation for this bitset + */ + override def toString() = + toSet(true).toString(); + + /** returns number of Int cells needed to store n bits */ + protected def memsize(n:Int) = (n >>> 5) + { + if((n & 0x1F) != 0) 1 else 0 + }; + +} diff --git a/src/library/scala/collection/Map.scala b/src/library/scala/collection/Map.scala new file mode 100644 index 0000000000..14546e9e71 --- /dev/null +++ b/src/library/scala/collection/Map.scala @@ -0,0 +1,180 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection; + +/** This trait defines the interface of collections that unambiguously map + * keys to values (i.e. a key is mapped to at least one value). + * Trait <code>Map</code> may only be used for + * accessing elements from map implementations. Two different extensions + * of trait <code>Map</code> in the package <code>scala.collections.mutable</code> + * and <code>scala.collections.immutable</code> provide functionality for + * adding new key/value mappings to a map. The trait in the first package is + * implemented by maps that are modified destructively, whereas the trait in + * the second package is used by functional map implementations that rely on + * immutable data structures. + * + * @author Matthias Zenger + * @version 1.1, 02/05/2004 + */ +trait Map[A, +B] extends AnyRef with PartialFunction[A, B] with Iterable[Pair[A, B]] { + + /** Compute the number of key-to-value mappings. + * + * @return the number of mappings + */ + def size: Int; + + /** Check if this map maps <code>key</code> to a value and return the + * value if it exists. + * + * @param key the key of the mapping of interest + * @return the value of the mapping, if it exists + */ + def get(key: A): Option[B]; + + /** Is this an empty map? + * + * @return true, iff the map is empty. + */ + def isEmpty: Boolean = (size == 0); + + /** Retrieve the value which is associated with the given key. This + * method throws an exception if there is no mapping from the given + * key to a value. + * + * @param key the key + * @return the value associated with the given key. + */ + def apply(key: A): B = get(key) match { + case None => default + case Some(value) => value + } + + /** Is the given key mapped to a value by this map? + * + * @param key the key + * @return true, iff there is a mapping for key in this map + */ + def contains(key: A): Boolean = get(key) match { + case None => false + case Some(_) => true + } + + /** Does this map contain a mapping from the given key to a value? + * + * @param key the key + * @return true, iff there is a mapping for key in this map + */ + def isDefinedAt(key: A) = contains(key); + + /** Creates an iterator for all keys. + * + * @return an iterator over all keys. + */ + def keys: Iterator[A] = new Iterator[A] { + val iter = Map.this.elements; + def hasNext = iter.hasNext; + def next = iter.next._1; + } + + /** Creates an iterator for a contained values. + * + * @return an iterator over all values. + */ + def values: Iterator[B] = new Iterator[B] { + val iter = Map.this.elements; + def hasNext = iter.hasNext; + def next = iter.next._2; + } + + /** Executes the given function for all (key, value) pairs + * contained in this map. + * + * @param f the function to execute. + */ + def foreach(f: (A, B) => Unit) = { + val iter = elements; + while (iter.hasNext) { + val Pair(key, value) = iter.next; + f(key, value); + } + } + + /** Applies the given predicate to all (key, value) mappings + * contained in this map and returns true if this predicate + * yields true for all mappings. + * + * @param p the predicate + * @return true, iff p yields true for all mappings. + */ + def forall(p: (A, B) => Boolean): Boolean = elements.forall { + case Pair(key, value) => p(key, value) + } + + /** Applies the given predicate to all (key, value) mappings + * contained in this map and returns true if there is at least + * one mapping for which this predicate yields true. + * + * @param p the predicate + * @return true, iff there is at least one mapping for which + * p yields true. + */ + def exists(p: (A, B) => Boolean): Boolean = elements.exists { + case Pair(key, value) => p(key, value) + } + + /** Compares two maps structurally; i.e. checks if all mappings + * contained in this map are also contained in the other map, + * and vice versa. + * + * @return true, iff both maps contain exactly the same mappings. + */ + override def equals(that: Any): Boolean = that match { + case other: Map[A, B] => + this.size == other.size && this.elements.forall { + case Pair(key, value) => other.get(key) match { + case None => false; + case Some(otherval) => value == otherval; + } + } + case _ => false + } + + /** Returns the mappings of this map as a list. + * + * @return a list containing all mappings + */ + def toList: List[Pair[A, B]] = elements.toList; + + /** Creates a string representation for this map. + * + * @return a string showing all mappings + */ + override def toString() = + if (size == 0) + "{}" + else + "{" + { + val iter = elements; + var res = iter.next.toString(); + while (iter.hasNext) { + res = res + ", " + iter.next; + } + res; + } + "}"; + + /** The default value for the map, returned when a key is not found + * The method implemented here yields an error, + * but it might be overridden in subclasses. + */ + def default: B = + error("key not found") +} + diff --git a/src/library/scala/collection/MapProxy.scala b/src/library/scala/collection/MapProxy.scala new file mode 100644 index 0000000000..8284a628d2 --- /dev/null +++ b/src/library/scala/collection/MapProxy.scala @@ -0,0 +1,43 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection; + + +/** This is a simple wrapper class for <code>scala.collection.Map</code>. + * It is most useful for assembling customized map abstractions + * dynamically using object composition and forwarding. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +trait MapProxy[A, +B] extends Map[A, B] with IterableProxy[Pair[A, B]] { + + def self: Map[A, B]; + + def size: Int = self.size; + + def get(key: A): Option[B] = self.get(key); + + override def isEmpty: Boolean = self.isEmpty; + + override def apply(key: A): B = self.apply(key); + + override def contains(key: A): Boolean = self.contains(key); + + override def isDefinedAt(key: A) = self.isDefinedAt(key); + + override def keys: Iterator[A] = self.keys; + + override def values: Iterator[B] = self.values; + + override def foreach(f: (A, B) => Unit) = self.foreach(f); + + override def toList: List[Pair[A, B]] = self.toList; +} diff --git a/src/library/scala/collection/Set.scala b/src/library/scala/collection/Set.scala new file mode 100644 index 0000000000..00ac17f6df --- /dev/null +++ b/src/library/scala/collection/Set.scala @@ -0,0 +1,89 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection; + + +/** This trait defines the interface of collections that do not contain + * duplicate elements. Trait <code>Set</code> may only be used for + * accessing elements from set implementations. Two different extensions + * of trait <code>Set</code> in the package <code>scala.collections.mutable</code> + * and <code>scala.collections.immutable</code> provide functionality for + * adding new elements to a set. The trait in the first package is implemented + * by sets the are modified destructively, whereas the trait in the second + * package is used by functional set implementations that rely on immutable + * data structures. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait Set[A] extends AnyRef with Function1[A, Boolean] with Iterable[A] { + + /** Returns the number of elements in this set. + * + * @return number of set elements. + */ + def size: Int; + + /** Checks if this set contains element <code>elem</code>. + * + * @param elem the element to check for membership. + * @return true, iff <code>elem</code> is contained in this set. + */ + def contains(elem: A): Boolean; + + /** This method allows sets to be interpreted as predicates. + * It returns true, iff this set contains element <code>elem</code>. + * + * @param elem the element to check for membership. + * @return true, iff <code>elem</code> is contained in this set. + */ + def apply(elem: A): Boolean = contains(elem); + + /** Checks if this set is empty. + * + * @return true, iff there is no element in the set. + */ + def isEmpty: Boolean = (size == 0); + + /** Checks if this set is a subset of set <code>that</code>. + * + * @param that another set. + * @return true, iff the other set is a superset of this set. + */ + def subsetOf(that: Set[A]): Boolean = forall(that.contains); + + /** Compares this set with another object and returns true, iff the + * other object is also a set which contains the same elements as + * this set. + * + * @param that the other object + * @return true, iff this set and the other set contain the same + * elements. + */ + override def equals(that: Any): Boolean = that match { + case other: Set[A] => + this.size == other.size && this.elements.forall(other.contains) + case _ => + false + } + + /** Returns the elements of this set as a list. + * + * @return a list containing all set elements. + */ + def toList: List[A] = elements.toList; + + /** Returns a string representation of this set. + * + * @return a string showing all elements of this set. + */ + override def toString(): String = + if (size == 0) "{}" else elements.toList.mkString("{", ", ", "}"); +} diff --git a/src/library/scala/collection/SetProxy.scala b/src/library/scala/collection/SetProxy.scala new file mode 100644 index 0000000000..0fc1931385 --- /dev/null +++ b/src/library/scala/collection/SetProxy.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection; + + +/** This is a simple wrapper class for <code>scala.collection.Set</code>. + * It is most useful for assembling customized set abstractions + * dynamically using object composition and forwarding. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +trait SetProxy[A] extends Set[A] with IterableProxy[A] { + + def self: Set[A]; + + def size: Int = self.size; + + override def isEmpty: Boolean = self.isEmpty; + + def contains(elem: A): Boolean = self.contains(elem); + + override def subsetOf(that: Set[A]): Boolean = self.subsetOf(that); + + override def foreach(f: A => Unit): Unit = self.foreach(f); + + override def exists(p: A => Boolean): Boolean = self.exists(p); + + override def toList: List[A] = self.toList; +} diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala new file mode 100644 index 0000000000..61299bf069 --- /dev/null +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -0,0 +1,117 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + +/** The class <code>BitSet</code>provides an immutable bitset view on an + * int array. Instances can conveniently be created from instances of + * <code>mutable.ResizableBitSet</code>. Bit indices are between 0..(size-1) inclusive + * + * @param <code>n</code> represents the number of relevant bits + * @param ba: array of ints of length <code>n</code>>>>5 + * @param copy: if yes, then <code>ba</code> is copied and updates will + * not affect this bitset + * + * @author Burak Emir + * @version 1.0 + */ +[serializable] +class BitSet(n:Int, ba: Array[Int], copy: Boolean) extends collection.BitSet with Ordered[BitSet] { + import scala.runtime.compat.Platform.arraycopy; + /** lexicographic ordering */ + def compareTo [b >: BitSet <% Ordered[b]](other: b): int = other match { + case that:BitSet => + val it1 = this.toSet(true).elements; + val it2 = that.toSet(true).elements; + var res = 0; + while((0==res) && it1.hasNext) { + while((0==res) && it2.hasNext) { + if(!it1.hasNext) + res = -1 + else { + val i1 = it1.next; + val i2 = it2.next; + if(i1<i2) + res = -1 + else if(i1>i2) + res = 1 + } + } + if(it1.hasNext) + res = 1 + } + if(it2.hasNext) + res = -1; + res + + case _ => -(other.compareTo(this)) + } + + + final def size = n; + + protected val array: Array[Int] = + if (copy) { + val arr = new Array[Int](ba.length); + arraycopy(ba, 0, arr, 0, ba.length); + arr + } + else + ba; + + /** + * Checks if two bitsets are structurally identical. + * + * @return true, iff both bitsets contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = ( + that.isInstanceOf[BitSet] && + { val other = that.asInstanceOf[BitSet]; + (size == other.size) && ( size == 0 || { + var len = memsize( size ); + var i = 0; + var res=true; + while(( i< len ) && res ) { // 32 x faster equality check + res = array(i) == other.array(i); + i = i + 1; + } + res + }) + } || super.equals(that) + ); + + def this(rbs: mutable.BitSet) = { + this(rbs.size, rbs.toArray, false); + } + + /** returns true if bit i is set + * + * @param i + */ + def apply(i: Int):Boolean = { + val j = (i >>> 5); + val mask = (1 << (i & 0x1F)); + (array(j) & mask) != 0; + } + + def makeMutable = { + val rbs = new mutable.BitSet(size) ; + val it = this.toSet(true).elements; + while(it.hasNext) { + rbs.set(it.next) + } + rbs + } + + def toArray: Array[Int] = { + val arr = new Array[Int](array.length); + arraycopy(arr, 0, array, 0, array.length); + arr + } +} diff --git a/src/library/scala/collection/immutable/ListMap.scala b/src/library/scala/collection/immutable/ListMap.scala new file mode 100644 index 0000000000..b6c9989531 --- /dev/null +++ b/src/library/scala/collection/immutable/ListMap.scala @@ -0,0 +1,150 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + + +object ListMap { + def Empty[A, B] = new ListMap[A, B]; +} + +/** This class implements immutable maps using a list-based data + * structure. Instances of <code>ListMap</code> represent + * empty maps; they can be either created by calling the constructor + * directly, or by applying the function <code>ListMap.Empty</code>. + * + * @author Matthias Zenger + * @version 1.0, 09/07/2003 + */ +[serializable] +class ListMap[A, B] extends AnyRef with Map[A, B] { + + /** This method returns a new ListMap instance mapping keys of the + * same type to values of type <code>C</code>. + */ + def empty[C] = new ListMap[A, C]; + + /** Returns the number of mappings in this map. + * + * @return number of mappings. + */ + def size: Int = 0; + + /** Check if this map maps <code>key</code> to a value and return the + * value if it exists. + * + * @param key the key of the mapping of interest + * @return the value of the mapping, if it exists + */ + def get(key: A): Option[B] = None; + + /** This method allows one to create a new map with an + * additional mapping from <code>key</code> + * to <code>value</code>. If the map contains already a + * mapping for <code>key</code>, it will be overridden by this + * function. + */ + def update(key: A, value: B): ListMap[A, B] = new Node(key, value); + + /** This creates a new mapping without the given <code>key</code>. + * If the map does not contain a mapping for the given key, the + * method returns the same map. + */ + def -(key: A): ListMap[A, B] = this; + + /** This returns an iterator over key-value pairs. + */ + def elements: Iterator[Pair[A, B]] = toList.elements; + + /** This return a list of key-value pairs. + */ + override def toList: List[Pair[A, B]] = Nil; + + /** Compares two maps for equality. + * Two maps are equal iff they contain exactly the + * same key-value pairs. + */ + override def equals(obj: Any): Boolean = + if (obj.isInstanceOf[scala.collection.Map[A, B]]) { + val that = obj.asInstanceOf[scala.collection.Map[A, B]]; + if (size != that.size) false else toList.forall { + case Pair(key, value) => that.get(key) match { + case None => false; + case Some(v) => v == value; + } + }; + } else + false; + + override def hashCode(): Int = 0; + + protected class Node(key: A, value: B) extends ListMap[A, B] { + /** Returns the number of mappings in this map. + * + * @return number of mappings. + */ + override def size: Int = ListMap.this.size + 1; + + /** Is this an empty map? + * + * @return true, iff the map is empty. + */ + override def isEmpty: Boolean = false; + + /** Retrieve the value which is associated with the given key. This + * method throws an exception if there is no mapping from the given + * key to a value. + * + * @param key the key + * @return the value associated with the given key. + */ + override def apply(k: A): B = if (k == key) value else ListMap.this(k); + + /** Check if this map maps <code>key</code> to a value and return the + * value if it exists. + * + * @param key the key of the mapping of interest + * @return the value of the mapping, if it exists + */ + override def get(k: A): Option[B] = + if (k == key) Some(value) else ListMap.this.get(k); + + /** This method allows one to create a new map with an + * additional mapping from <code>key</code> + * to <code>value</code>. If the map contains already a + * mapping for <code>key</code>, it will be overridden by this + * function. + */ + override def update(k: A, v: B): ListMap[A, B] = + if (k == key) { + new ListMap.this.Node(k, v); + } else { + val tail = ListMap.this.update(k,v); new tail.Node(key, value) + } + + /** This creates a new mapping without the given <code>key</code>. + * If the map does not contain a mapping for the given key, the + * method returns the same map. + */ + override def -(k: A): ListMap[A, B] = + if (k == key) + ListMap.this + else { + val tail = ListMap.this - k; new tail.Node(key, value) + } + + /** This return a list of key-value pairs. + */ + override def toList: List[Pair[A, B]] = Pair(key, value) :: ListMap.this.toList; + + override def hashCode(): Int = + (key.hashCode() ^ value.hashCode()) + ListMap.this.hashCode(); + } +} + diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala new file mode 100644 index 0000000000..305e54cb90 --- /dev/null +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -0,0 +1,118 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.collection.immutable; + + +object ListSet { + /** constructs an empty ListSet + */ + def Empty[A] = new ListSet[A]; +} + + +/** This class implements immutable sets using a list-based data + * structure. Instances of <code>ListSet</code> represent + * empty sets; they can be either created by calling the constructor + * directly, or by applying the function <code>ListSet.Empty</code>. + * + * @author Matthias Zenger + * @version 1.0, 09/07/2003 + */ +[serializable] +class ListSet[A] extends AnyRef with Set[A] { + + /** Returns the number of elements in this set. + * + * @return number of set elements. + */ + def size: Int = 0; + + /** Checks if this set contains element <code>elem</code>. + * + * @param elem the element to check for membership. + * @return true, iff <code>elem</code> is contained in this set. + */ + def contains(elem: A): Boolean = false; + + /** This method creates a new set with an additional element. + */ + def +(elem: A): ListSet[A] = new Node(elem); + + /** <code>-</code> can be used to remove a single element from + * a set. + */ + def -(elem: A): ListSet[A] = this; + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A] = toList.elements; + + /** Transform this set into a list of all elements. + * + * @return a list which enumerates all elements of this set. + */ + override def toList: List[A] = Nil; + + /** Compares two sets for equality. + * Two set are equal iff they contain the same elements. + */ + override def equals(obj: Any): Boolean = + if (obj.isInstanceOf[scala.collection.Set[A]]) { + val that = obj.asInstanceOf[scala.collection.Set[A]]; + if (size != that.size) false else toList.forall(that.contains); + } else + false; + + override def hashCode(): Int = 0; + + protected class Node(elem: A) extends ListSet[A] { + /** Returns the number of elements in this set. + * + * @return number of set elements. + */ + override def size = ListSet.this.size + 1; + + /** Checks if this set is empty. + * + * @return true, iff there is no element in the set. + */ + override def isEmpty: Boolean = false; + + /** Checks if this set contains element <code>elem</code>. + * + * @param elem the element to check for membership. + * @return true, iff <code>elem</code> is contained in this set. + */ + override def contains(e: A) = (e == elem) || ListSet.this.contains(e); + + /** This method creates a new set with an additional element. + */ + override def +(e: A): ListSet[A] = if (contains(e)) this else new Node(e); + + /** <code>-</code> can be used to remove a single element from + * a set. + */ + override def -(e: A): ListSet[A] = if (e == elem) ListSet.this else { + val tail = ListSet.this - e; new tail.Node(elem) + } + + /** Transform this set into a list of all elements. + * + * @return a list which enumerates all elements of this set. + */ + override def toList: List[A] = elem :: ListSet.this.toList; + + override def hashCode(): Int = elem.hashCode() + ListSet.this.hashCode(); + } +} diff --git a/src/library/scala/collection/immutable/Map.scala b/src/library/scala/collection/immutable/Map.scala new file mode 100644 index 0000000000..b737f48886 --- /dev/null +++ b/src/library/scala/collection/immutable/Map.scala @@ -0,0 +1,144 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + + +/** This trait extends the Map interface of collections that unambiguously map + * keys to values (i.e. a key is mapped to at least one value). + * This trait defines the interface for functional map implementations + * relying on immutable data structures. + * Concrete map implementations have to provide functionality for the + * abstract methods in scala.collection.Map as well as for + * <code>factory</code>, <code>update</code>, and -. + * + * @author Matthias Zenger + * @author Erik Stenman + * @version 1.1, 22/03/2004 + */ +trait Map[A, B] extends AnyRef with scala.collection.Map[A, B] { + + /** This method returns a new map instance of the same class + * mapping keys of the same type to values of type <code>C</code>. + */ + def empty[C]: Map[A, C]; + + /** This method allows one to create a new map with an + * additional mapping from <code>key</code> + * to <code>value</code>. If the map contains already a + * mapping for <code>key</code>, it will be overridden by this + * function. + */ + def update(key: A, value: B): Map[A, B]; + + /** This creates a new mapping without the given <code>key</code>. + * If the map does not contain a mapping for the given key, the + * method returns the same map. + */ + def -(key: A): Map[A, B]; + + /** This method defines syntactic sugar for adding a + * mapping. It is typically used in the following way: + * <pre> + * map + key -> value; + * </pre> + */ + def +(key: A): MapTo = new MapTo(key); + + /** <code>incl</code> can be used to add many mappings at the same time + * to the map. The method assumes that a mapping is represented + * by a <code>Pair</code> object who's first component denotes the + * key, and who's second component refers to the value. + */ + def incl(mappings: Pair[A, B]*): Map[A, B] = incl(mappings); + + /** <code>incl</code> can be used to add many mappings at the same time + * to the map. The method assumes that each mapping is represented + * by an Iterator over <code>Pair</code> objects who's first component + * denotes the key, and who's second component refers to the value. + */ + def incl(map: Iterable[Pair[A, B]]): Map[A, B] = { + val iter = map.elements; + var res = this; + while (iter.hasNext) { + val Pair(key, value) = iter.next; + res = res.update(key, value); + } + res + } + + /** This method will return a map where all the mappings + * for the given sequence of keys are removed from the map. + */ + def excl(keys: A*): Map[A, B] = excl(keys); + + /** This method removes all the mappings for keys provided by an + * iterator over the elements of the <code>keys</code> object. + */ + def excl(keys: Iterable[A]): Map[A, B] = { + val iter = keys.elements; + var res = this; + while (iter.hasNext) { + res = res - iter.next; + } + res; + } + + /** This function transforms all the values of mappings contained + * in this map with function <code>f</code>. + */ + def map[C](f: (A, B) => C): Map[A, C] = { + var res = empty[C]; + elements foreach { + case Pair(key, value) => res = res.update(key, f(key, value)); + } + res; + } + + /** This method removes all the mappings for which the predicate + * <code>p</code> returns <code>false</code>. + */ + def filter(p: (A, B) => Boolean): Map[A, B] = { + var res = this; + toList foreach { + case Pair(key, value) => if (!p(key, value)) { res = res.excl(key); } + } + res; + } + + /** Returns a string representation of this map which shows + * all the mappings. + */ + override def toString() = + if (size == 0) + "{}" + else + "{" + { + val iter = elements; + var res = mappingToString(iter.next); + while (iter.hasNext) { + res = res + ", " + mappingToString(iter.next); + } + res; + } + "}"; + + override def hashCode() = { + elements.foldLeft(0)((hash:Int,pair:AnyRef) => hash + pair.hashCode()); + } + + /** This method controls how a mapping is represented in the string + * representation provided by method <code>toString</code>. + */ + def mappingToString(p: Pair[A, B]) = p._1.toString() + " -> " + p._2; + + class MapTo(key: A) { + def ->(value: B) = update(key, value); + } +} + diff --git a/src/library/scala/collection/immutable/Queue.scala b/src/library/scala/collection/immutable/Queue.scala new file mode 100644 index 0000000000..cb005d3b5e --- /dev/null +++ b/src/library/scala/collection/immutable/Queue.scala @@ -0,0 +1,173 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + + +object Queue { + val Empty: Queue[All] = new Queue(); +} + +/** <code>Queue</code> objects implement data structures that allow to + * insert and retrieve elements in a first-in-first-out (FIFO) manner. + * + * @author Erik Stenman + * @version 1.0, 08/07/2003 + */ +[serializable] +class Queue[+A](elem: A*) extends Seq[A] { + protected val in: List[A] = Nil; + protected val out: List[A] = itToList(elem.elements); + + protected def itToList[B >: A](elems: Iterator[B]): List[B] = + if (elems.hasNext) { + val hd = elems.next; + hd :: itToList(elems) + } + else + Nil; + + protected def mkQueue[A](i: List[A], o: List[A]): Queue[A] = + new Queue[A]() { + override protected val in = i; + override protected val out = o + }; + + /** Returns the <code>n</code>-th element of this queue. + * The first element is at position 0. + * + * @param n index of the element to return + * @return the element at position <code>n</code> in this list. + * @throws java.lang.RuntimeException if the list is too short. + */ + def apply(n: Int): A = + if (n < out.length) out.apply(n) + else in.reverse.apply(n - out.length); + + /** Returns the elements in the list as an iterator + */ + def elements: Iterator[A] = (out ::: in.reverse).elements; + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + def isEmpty: Boolean = in.isEmpty && out.isEmpty; + + /** Returns the length of the queue. + */ + def length = in.length + out.length; + + /** Creates a new queue with element added at the end + * of the old queue. + * + * @param elem the element to insert + */ + def +[B >: A](elem: B) = mkQueue(elem :: in, out); + + /** Returns a new queue with all all elements provided by + * an <code>Iterable</code> object added at the end of + * the queue. + * The elements are prepended in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + def +[B >: A](iter: Iterable[B]) = { + var q: List[B] = in; + iter.elements.foreach(e => q = e :: q); + mkQueue(q, out); + } + + /** Returns a new queue with all elements added. + * + * @param elems the elements to add. + */ + def enqueue [B >: A](elems: B*) = this + elems; + + /** Returns a tuple with the first element in the queue, + * and a new queue with this element removed. + * + * @return the first element of the queue. + */ + def dequeue: Pair[A, Queue[A]] = { + val Pair(newOut, newIn) = + if (out.isEmpty) Pair(in.reverse, Nil) + else Pair(out, in); + if (newOut.isEmpty) error("queue empty"); + else Pair(newOut.head, mkQueue(newIn, newOut.tail)); + } + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @return the first element. + */ + def front: A = + if (out.isEmpty) { + if (in.isEmpty) error("queue empty") else in.last; + } else + out.head; + + /** Returns a string representation of this queue. The resulting string + * begins with the string <code>start</code> and is finished by the string + * <code>end</code>. Inside, the string representations of elements (w.r.t. + * the method <code>toString()</code>) are separated by the string + * <code>sep</code>. + * <p/> + * Ex: <br/> + * <code>Queue(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)"</code> + * + * @param start starting string. + * @param sep separator string. + * @param end ending string. + * @return a string representation of this list. + */ + def mkString(start: String, sep: String, end: String): String = + (out ::: in.reverse).mkString(start, sep, end); + + /** Returns a string representation of this queue. + */ + override def toString() = (out ::: in.reverse).mkString("Queue(", ",", ")"); + + /** Compares two queues for equality by comparing + * each element in the queues. + * + * @return true, iff the two queues are structurally equal. + */ + override def equals(o: Any): Boolean = o match { + case q: Queue[Any] => + /* A function that compares the element at + position index in q with the element at + the same position in this (queue). + If they are equal the next element is + compared. */ + def eqe(index: Int): Boolean = ( + /* If all elements are compared + the queues are equal. */ + index >= this.length || + /* Otherwise: compare the elements */ + (q.apply(index) == this.apply(index) && + /* if they are equal compare the rest. */ + eqe(index + 1)) + ); + /* If the length of the ques are the same, + compare each element, starting at index 0. */ + (q.length == this.length) && eqe(0); + + case _ => false; /* o is not a queue: not equal to this. */ + } + + override def hashCode(): Int = + if (isEmpty) 0 + else { + val q: Pair[A,Queue[A]] = dequeue; + q._1.hashCode() + q._2.hashCode(); + } +} diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala new file mode 100644 index 0000000000..af3340e8f1 --- /dev/null +++ b/src/library/scala/collection/immutable/Set.scala @@ -0,0 +1,81 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + + +/** This trait represents immutable sets. Concrete set implementations + * just have to provide functionality for the abstract methods in + * <code>scala.collection.Set</code> as well as for <code>+</code> and + * <code>-</code>. + * + * @author Matthias Zenger + * @version 1.1, 03/05/2004 + */ +trait Set[A] extends AnyRef with scala.collection.Set[A] { + + /** This method creates a new set with an additional element. + */ + def +(elem: A): Set[A]; + + /** <code>incl</code> can be used to add many elements to the set + * at the same time. + */ + def incl(elems: A*): Set[A] = incl(elems); + + /** This method will add all the elements provided by an iterator + * of the iterable object <code>that</code> to the set. + */ + def incl(that: Iterable[A]): Set[A] = { + var res = this; + that.elements.foreach(elem => res = res + elem); + res; + } + + /** <code>-</code> can be used to remove a single element from + * a set. + */ + def -(elem: A): Set[A]; + + /** <code>excl</code> removes many elements from the set. + */ + def excl(elems: A*): Set[A] = excl(elems); + + /** This method removes all the elements provided by an iterator + * of the iterable object <code>that</code> from the set. + */ + def excl(that: Iterable[A]): Set[A] = { + var res = this; + that.elements.foreach(elem => res = res - elem); + res; + } + + /** This method computes an intersection with set <code>that</code>. + * It removes all the elements that are not present in <code>that</code>. + */ + def intersect(that: scala.collection.Set[A]): Set[A] = filter(that.contains); + + /** Method <code>filter</code> removes all elements from the set for + * which the predicate <code>p</code> yields the value <code>false</code>. + */ + def filter(p: A => Boolean): Set[A] = { + var res = this; + toList foreach { + elem => if (!p(elem)) { res = res - elem; } + } + res; + } + + /** hashcode for this set */ + override def hashCode() = { + elements.foldLeft(0)((hash: Int, e: A) => hash + e.hashCode()); + } + +} + diff --git a/src/library/scala/collection/immutable/Stack.scala b/src/library/scala/collection/immutable/Stack.scala new file mode 100644 index 0000000000..46b15794a3 --- /dev/null +++ b/src/library/scala/collection/immutable/Stack.scala @@ -0,0 +1,139 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + + +object Stack { + def Empty[A] = new Stack[A]; +} + +/** This class implements immutable stacks using a list-based data + * structure. Instances of <code>Stack</code> represent + * empty stacks; they can be either created by calling the constructor + * directly, or by applying the function <code>Stack.Empty</code>. + * + * @author Matthias Zenger + * @version 1.0, 10/07/2003 + */ +[serializable] +class Stack[+A] extends Seq[A] { + + /** Checks if this stack is empty. + * + * @return true, iff there is no element on the stack. + */ + def isEmpty: Boolean = true; + + /** Returns the size of this stack. + * + * @return the stack size. + */ + def length: Int = 0; + + /** Push an element on the stack. + * + * @param elem the element to push on the stack. + * @return the stack with the new element on top. + */ + def +[B >: A](elem: B): Stack[B] = new Node(elem); + + /** Push all elements provided by the given iterable object onto + * the stack. The last element returned by the iterable object + * will be on top of the new stack. + * + * @param elems the iterable object. + * @return the stack with the new elements on top. + */ + def +[B >: A](elems: Iterable[B]): Stack[B] = { + var res: Stack[B] = this; + elems.elements.foreach { elem => res = res + elem; } + res + } + + /** Push a sequence of elements onto the stack. The last element + * of the sequence will be on top of the new stack. + * + * @param elems the element sequence. + * @return the stack with the new elements on top. + */ + def push[B >: A](elems: B*): Stack[B] = this + elems; + + /** Returns the top element of the stack. An error is signaled if + * there is no element on the stack. + * + * @return the top element. + */ + def top: A = error("no element on stack"); + + /** Removes the top element from the stack. + * + * @return the new stack without the former top element. + */ + def pop: Stack[A] = error("no element on stack"); + + /** Returns the n-th element of this stack. The top element has index + * 0, elements below are indexed with increasing numbers. + * + * @param n the index number. + * @return the n-th element on the stack. + */ + def apply(n: Int): A = error("no element on stack"); + + /** Returns an iterator over all elements on the stack. The iterator + * issues elements in the reversed order they were inserted into the + * stack (LIFO order). + * + * @return an iterator over all stack elements. + */ + def elements: Iterator[A] = toList.elements; + + /** Creates a list of all stack elements in LIFO order. + * + * @return the created list. + */ + override def toList: List[A] = Nil; + + /** Compares this stack with the given object. + * + * @return true, iff the two stacks are equal; i.e. they contain the + * same elements in the same order. + */ + override def equals(obj: Any): Boolean = + if (obj.isInstanceOf[Stack[A]]) + toList.equals((obj.asInstanceOf[Stack[A]]).toList); + else + false; + + /** Returns the hash code for this stack. + * + * @return the hash code of the stack. + */ + override def hashCode(): Int = 0; + + /** + * Redefines the prefix of the string representation. + */ + override def stringPrefix: String = "Stack"; + + // Here comes true magic: covariant lists with implicit tail references + [serializable] + protected class Node[+B >: A](elem: B) extends Stack[B] { + override def isEmpty: Boolean = false; + override def length: Int = Stack.this.length + 1; + override def +[C >: B](elem: C): Stack[C] = new Node(elem); + override def +[C >: B](elems: Iterable[C]): Stack[C] = super.+(elems); + override def top: B = elem; + override def pop: Stack[B] = Stack.this; + override def apply(n: Int): B = if (n > 0) Stack.this(n - 1) else elem; + override def toList: List[B] = elem :: Stack.this.toList; + override def hashCode(): Int = elem.hashCode() + Stack.this.hashCode(); + } + +} diff --git a/src/library/scala/collection/immutable/Tree.scala b/src/library/scala/collection/immutable/Tree.scala new file mode 100644 index 0000000000..fdb3f8cf46 --- /dev/null +++ b/src/library/scala/collection/immutable/Tree.scala @@ -0,0 +1,374 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +/* General Balanced Trees - highly efficient functional dictionaries. +** +** This is a scala version of gb_trees.erl which is +** copyrighted (C) 1999-2001 by Sven-Olof Nystrom, and Richard Carlsson +** +** An efficient implementation of Prof. Arne Andersson's General +** Balanced Trees. These have no storage overhead compared to plain +** unbalanced binary trees, and their performance is in general better +** than AVL trees. +** --------------------------------------------------------------------- +** This library is free software; you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as +** published by the Free Software Foundation; either version 2 of the +** License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +** USA +** +** Author contact: erik.stenman@epfl.ch +** --------------------------------------------------------------------- +*/ + +package scala.collection.immutable; + +/** General Balanced Trees - highly efficient functional dictionaries. + * + * An efficient implementation of Prof. Arne Andersson's General + * Balanced Trees. These have no storage overhead compared to plain + * unbalanced binary trees, and their performance is in general better + * than AVL trees. + * <p/> + * This implementation does not balance the trees after deletions. + * Since deletions don't increase the height of a tree, this should + * be OK in most applications. A balance method is provided for those + * cases where rebalancing is needed. + * <p/> + * The tree consists of entries conatining a key with an order. + * <p/> + * When instanciating the tree an order for the keys has to be + * supplied. + * + * @author Erik Stenman + * @author Michel Schinz + * @version 1.1, 2005-01-20 + */ +//[serializable] +abstract class Tree[A <% Ordered[A], B]() extends AnyRef with java.io.Serializable { + /* Data structure: + ** - size:Int - the number of elements in the tree. + ** - tree:T, which is composed of nodes of the form: + ** - GBNode(key: A, entry:B, smaller:T, bigger:T), + ** - and the "empty tree" node GBLeaf. + ** + ** Original balance condition h(T) <= ceil(c * log(|T|)) has been + ** changed to the similar (but not quite equivalent) condition + ** 2 ^ h(T) <= |T| ^ c. + ** + */ + + /** The type returned when creating a new tree. + * This type should be defined by concrete implementations + * e.g. <pre> + * class C[T](...) extends Tree[A,B](...) { + * type This = C[T]; + * </pre> + */ + protected type This <: Tree[A,B]; + protected def getThis: This; + + /** + * The type of nodes that the tree is build from. + */ + protected type aNode = GBTree[A,B]; + + private val empty: aNode = GBLeaf[A,B](); + + /** The nodes in the tree. + */ + protected def tree: aNode = empty; + + /** This abstract method should be defined by a concrete implementation + ** C[T] as something like: + ** <pre> + ** override def New(sz:Int,t:aNode):This { + ** new C[T](order) { + ** override def size=sz; + ** override protected def tree:aNode=t; + ** } + ** </pre> + ** The concrete implementation should also override the def of This + ** <code>override type This = C[T];</code> + ** + */ + protected def New(sz: Int, t: aNode): This; + + /** The size of the tree, returns 0 (zero) if the tree is empty. + ** @Returns The number of nodes in the tree as an integer. + **/ + def size: Int = 0; + + /** + * A new tree with the entry added is returned, + * assuming that key is <em>not</em> in the tree. + */ + protected def add(key: A, entry: B): This = { + val newSize = size+1; + New(newSize, tree.insert(key, entry, newSize * newSize).node); + } + + /** + * A new tree with the entry added is returned, + * if key is <em>not</em> in the tree, otherwise + * the key is updated with the new entry. + */ + protected def updateOrAdd(key: A, entry: B): This = { + if (tree.isDefinedAt(key)) + New(size,tree.update(key,entry)) + else + add(key,entry); + } + + /** Removes the key from the tree. */ + protected def deleteAny(key: A): This = + if (tree.isDefinedAt(key)) + delete(key) + else + getThis; + + /** Removes the key from the tree, assumimg that key is present. */ + private def delete(key:A): This = + New(size - 1, tree.delete(key)); + + /** Check if this map maps <code>key</code> to a value and return the + * value if it exists. + * + * @param key the key of the mapping of interest + * @return the value of the mapping, if it exists + */ + protected def findValue(key:A): Option[B] = + tree.get(key); + + /** + * Gives you an iterator over all elements in the tree. + * The iterator structure corresponds to + * the call stack of an in-order traversal. + * + * Note: The iterator itself has a state, i.e., it is not functional. + */ + protected def entries: Iterator[B] = + new Iterator[B] { + var iter = tree.mk_iter(scala.Nil); + def hasNext = !iter.isEmpty; + def next = + iter match { + case (GBNode(_,v,_,t)::iter_tail) => { + iter= t.mk_iter(iter_tail); + v; + } + case scala.Nil => + error("next on empty iterator"); + } + } + + /** + * Create a new balanced tree from the tree. Might be useful to call + * after many deletions, since deletion does not rebalance the tree. + */ + def balance: This = + New(size, tree.balance(size)); +} + +protected abstract class InsertTree[A <% Ordered[A],B]() extends AnyRef { + def insertLeft(k: A, v: B, t: GBTree[A,B]): InsertTree[A,B]; + def insertRight(k: A, v: B, t: GBTree[A,B]): InsertTree[A,B]; + def node: GBTree[A,B]; +} + +private case class ITree[A <% Ordered[A],B](t: GBTree[A,B]) + extends InsertTree[A,B] { + def insertLeft(key: A, value: B, bigger: GBTree[A,B]) = + ITree(GBNode(key, value, t, bigger)); + def insertRight(key: A, value: B, smaller: GBTree[A,B]) = + ITree(GBNode(key, value, smaller, t)); + def node = t; +} + +private case class INode[A <% Ordered[A],B](t1: GBTree[A,B], + height: int, + size: int) + extends InsertTree[A,B] { + def insertLeft(key: A, value: B, bigger: GBTree[A,B]) = + balance_p(GBNode(key, value, t1, bigger), bigger); + def insertRight(key: A, value: B, smaller: GBTree[A,B]) = + balance_p(GBNode(key, value, smaller, t1),smaller); + protected def balance_p(t:GBTree[A,B],subtree:GBTree[A,B]):InsertTree[A,B] = { + val Pair(subHeight, subSize) = subtree.count; + val totalHeight = 2 * scala.runtime.compat.Math.max(height, subHeight); + val totalSize = size + subSize + 1; + val BalanceHeight = totalSize * totalSize; + if(totalHeight > BalanceHeight) ITree(t.balance(totalSize)); + else INode(t, totalHeight, totalSize); + } + def node = t1; +} + +/** +* GBTree is an internal class used by Tree. +*/ +//[serializable] +protected abstract class GBTree[A <% Ordered[A],B] extends AnyRef with java.io.Serializable { + type aNode = GBTree[A,B]; + type anInsertTree = InsertTree[A,B]; + + /** Calculates 2^h, and size, where h is the height of the tree + * and size is the number of nodes in the tree. + */ + def count:Pair[Int,Int]; + def isDefinedAt(Key:A):Boolean; + def get(key:A):Option[B]; + def apply(key:A):B; + def update(key:A, value:B):aNode; + def insert(key:A, value:B, size:int):anInsertTree; + def toList(acc: List[Pair[A,B]]): List[Pair[A,B]]; + def mk_iter(iter_tail:List[aNode]): List[aNode]; + def delete(key:A):aNode; + def merge(t:aNode):aNode; + def takeSmallest:Triple[A,B,aNode]; + def balance(s:int):GBTree[A,B]; +} + +private case class GBLeaf[A <% Ordered[A],B]() extends GBTree[A,B] { + def count = Pair(1, 0); + def isDefinedAt(key:A) = false; + def get(_key:A) = None; + def apply(key:A) = error("key " + key + " not found"); + def update(key:A, value:B) = error("key " + key + " not found"); + def insert(key:A, value:B, s:int):anInsertTree = { + if (s == 0) + INode(GBNode(key, value, this, this), 1, 1) + else + ITree(GBNode(key, value, this, this)) + } + def toList(acc: List[Pair[A,B]]): List[Pair[A,B]] = acc; + def mk_iter(iter_tail:List[GBTree[A,B]]) = iter_tail; + def merge(larger:GBTree[A,B]) = larger; + def takeSmallest:Triple[A,B,GBTree[A,B]] = + error("Take Smallest on empty tree"); + def delete(_key:A) = error("Delete on empty tree."); + def balance(s:int) = this; + override def hashCode() = 0; +} + +private case class GBNode[A <% Ordered[A],B](key: A, + value: B, + smaller: GBTree[A,B], + bigger: GBTree[A,B]) + extends GBTree[A,B] { + def count: Pair[Int,Int] = { + val Pair(sHeight, sSize) = smaller.count; + val Pair(bHeight, bSize) = bigger.count; + val mySize = sSize + bSize + 1; + if (mySize == 1) + Pair(1, mySize) + else + Pair(2 * scala.runtime.compat.Math.max(sHeight, bHeight), mySize); + } + + def isDefinedAt(sKey:A):Boolean = { + if (sKey < key) smaller.isDefinedAt(sKey) + else if (sKey > key) bigger.isDefinedAt(sKey) + else true; + } + + def get(sKey:A):Option[B] = + if (sKey < key) smaller.get(sKey); + else if (sKey > key) bigger.get(sKey); + else Some(value); + + def apply(sKey:A):B = + if (sKey < key) smaller.apply(sKey); + else if (sKey > key) bigger.apply(sKey); + else value; + + def update(newKey:A, newValue:B):aNode = + if (newKey < key) + GBNode(key, value, smaller.update(newKey,newValue), bigger); + else if (newKey > key) + GBNode(key, value, smaller, bigger.update(newKey,newValue)); + else + GBNode(newKey, newValue, smaller, bigger); + + def insert(newKey:A, newValue:B, s:int): anInsertTree = { + if (newKey < key) + smaller.insert(newKey, newValue, s / 2).insertLeft(key, value, bigger); + else if (newKey > key) + bigger.insert(newKey, newValue, s / 2).insertRight(key, value, smaller); + else + error("Key exists: " + newKey); + } + + def toList(acc: List[Pair[A,B]]): List[Pair[A,B]] = + smaller.toList(Pair(key, value) :: bigger.toList(acc)); + + def mk_iter(iter_tail:List[aNode]):List[aNode] = + smaller.mk_iter(this :: iter_tail); + + def delete(sKey:A):aNode = { + if (sKey < key) + GBNode(key, value, smaller.delete(sKey), bigger); + else if (sKey > key) + GBNode(key, value, smaller, bigger.delete(sKey)); + else + smaller.merge(bigger) + } + + def merge(larger: aNode): GBTree[A,B] = larger match { + case GBLeaf() => + this + case _ => + val Triple(key1, value1, larger1) = larger.takeSmallest; + GBNode(key1, value1, this, larger1) + } + + def takeSmallest: Triple[A, B, aNode] = smaller match { + case GBLeaf() => + Triple(key, value, bigger) + case _ => + val Triple(key1, value1, smaller1) = smaller.takeSmallest; + Triple(key1, value1, GBNode(key, value, smaller1, bigger)) + } + + def balance(s:int): GBTree[A,B] = + balance_list(toList(scala.Nil), s); + + protected def balance_list(list: List[Pair[A,B]], s:int): GBTree[A,B] = { + val empty = GBLeaf[A,B](); + def bal(list: List[Pair[A,B]], s:int): Pair[aNode,List[Pair[A,B]]] = { + if (s > 1) { + val sm = s - 1; + val s2 = sm / 2; + val s1 = sm - s2; + val Pair(t1, Pair(k, v) :: l1) = bal(list, s1); + val Pair(t2, l2) = bal(l1, s2); + val t = GBNode(k, v, t1, t2); + Pair(t, l2) + } else + if (s == 1) { + val Pair(k,v) :: rest = list; + Pair(GBNode(k, v, empty, empty), rest) + } else + Pair(empty, list) + } + bal(list, s)._1 + } + + override def hashCode() = + value.hashCode() + smaller.hashCode() + bigger.hashCode(); +} diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala new file mode 100644 index 0000000000..12814b7a41 --- /dev/null +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -0,0 +1,107 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.immutable; + +object TreeMap { + def Empty[A <% Ordered[A], B] = new TreeMap[A, B]; +} + +/** This class implements immutable maps using a tree. + * + * @author Erik Stenman + * @author Matthias Zenger + * @version 1.1, 03/05/2004 + */ +//[serializable] +class TreeMap[A <% Ordered[A], B] extends Tree[A, Pair[A, B]] with Map[A, B] with java.io.Serializable { + + override protected type This = TreeMap[A, B]; + override protected def getThis: This = this; + + /** A factory to create empty maps of the same type of keys. + */ + def empty[C] = new TreeMap[A, C]; + + /** Creates a new TreeMap from a GBTree and its size. + */ + protected def New(sz:Int,t:aNode):This = new TreeMap[A,B] { + override def size=sz; + override protected def tree:aNode=t; + } + + /** A new TreeMap with the entry added is returned, + * if key is <em>not</em> in the TreeMap, otherwise + * the key is updated with the new entry. + */ + def update(key:A, value:B) = updateOrAdd(key, Pair(key, value)); + + /** A new TreeMap with the entry added is returned, + * assuming that key is <em>not</em> in the TreeMap. + */ + def insert(key:A,value:B) = add(key, Pair(key, value)); + + /** Removes the key from the TreeMap. + */ + def -(key:A) = deleteAny(key); + + /** Check if this map maps <code>key</code> to a value and return the + * value if it exists. + * + * @param key the key of the mapping of interest + * @return the value of the mapping, if it exists + */ + override def get(key: A): Option[B] = + findValue(key) match { + case Some(Pair(_, value: B)) => Some(value) + case _ => None + } + + /** Retrieve the value which is associated with the given key. This + * method throws an exception if there is no mapping from the given + * key to a value. + * + * @param key the key + * @return the value associated with the given key. + * @throws Error("key not found"). + */ + override def apply(key:A):B = tree.apply(key)._2; + + /** Creates a list of all (key, value) mappings. + * + * @return the list of all mappings + */ + override def toList: List[Pair[A,B]] = + tree.toList(scala.Nil) map (._2); + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[Pair[A,B]] = entries; + + /** Compares two maps structurally; i.e. checks if all mappings + * contained in this map are also contained in the other map, + * and vice versa. + * + * @return true, iff both maps contain exactly the same mappings. + */ + override def equals(obj: Any): Boolean = + obj.isInstanceOf[scala.collection.Map[A, B]] && { + val that = obj.asInstanceOf[scala.collection.Map[A, B]]; + size == that.size && elements.forall { + case Pair(key, value) => that.get(key) match { + case None => false; + case Some(v) => v == value; + } + } + }; +} + diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala new file mode 100644 index 0000000000..bb3099e007 --- /dev/null +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -0,0 +1,69 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.collection.immutable; + + +/** This class implements immutable sets using a tree. + * + * @author Matthias Zenger + * @author Burak Emir + * @version 1.1, 03/05/2004 + */ +//[serializable] +class TreeSet[A <% Ordered[A]]() extends Tree[A, A] with Set[A] with java.io.Serializable { + + override protected type This = TreeSet[A]; + override protected def getThis: This = this; + + protected def New(sz: Int, t: aNode): This = new TreeSet[A] { + override def size = sz; + override protected def tree: aNode = t; + } + + /** Checks if this set contains element <code>elem</code>. + * + * @param elem the element to check for membership. + * @return true, iff <code>elem</code> is contained in this set. + */ + def contains(elem: A): Boolean = !findValue(elem).isEmpty; + + /** This method creates a new set with an additional element. + */ + def +(elem: A): TreeSet[A] = updateOrAdd(elem, elem); + + /** <code>-</code> can be used to remove a single element from + * a set. + */ + def -(elem: A): TreeSet[A] = deleteAny(elem); + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A] = entries; + + /** Transform this set into a list of all elements. + * + * @return a list which enumerates all elements of this set. + */ + override def toList: List[A] = + tree.toList(scala.Nil) map (._2); + + /** Compares two sets for equality. + * Two set are equal iff they contain the same elements. + */ + override def equals(obj: Any): Boolean = + obj.isInstanceOf[scala.collection.Set[A]] && { + val that = obj.asInstanceOf[scala.collection.Set[A]]; + (size == that.size) && toList.forall(that.contains); + } +} diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala new file mode 100644 index 0000000000..e89e1a9c81 --- /dev/null +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -0,0 +1,148 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** An implementation of the Buffer trait using an array to + * represent the assembled sequence internally. + * + * @author Matthias Zenger + * @version 1.0, 15/03/2004 + */ +[serializable] +class ArrayBuffer[A] extends Buffer[A] with ResizableArray[A] { + + def apply(n: Int): A = { + if ((n < 0) || (n >= size)) + error("cannot access element " + n + " in ArrayBuffer"); + else + array(n); + } + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +(elem: A): Buffer[A] = { + ensureSize(size+1); + array(size) = elem; + size = size + 1; + this + } + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++(iter: Iterable[A]): Buffer[A] = { insertAll(size, iter); this } + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +:(elem: A): Buffer[A] = { + ensureSize(size+1); + copy(0, 1, size); + array(0) = elem; + size = size + 1; + this + } + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++:(iter: Iterable[A]): Buffer[A] = { insertAll(0, iter); this } + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert a new element at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + */ + def insertAll(n: Int, iter: Iterable[A]): Unit = { + if ((n < 0) || (n > size)) + error("cannot insert element " + n + " in ListBuffer"); + val xs = iter.elements.toList; + val len = xs.length; + ensureSize(size+len); + copy(n, n + len, size - n); + xs.copyToArray(array, n); + size = size + len; + } + + /** Replace element at index <code>n</code> with the new element + * <code>newelem</code>. + * + * @param n the index of the element to replace. + * @param newelem the new element. + */ + def update(n: Int, newelem: A): Unit = { + if ((n < 0) || (n >= size)) + error("cannot update element " + n + " in ArrayBuffer"); + else { + val res = array(n); + array(n) = newelem; + res + } + } + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + def remove(n: Int): A = { + if ((n < 0) || (n >= size)) + error("cannot remove element " + n + " in Buffer"); + val res = array(n); + copy(n + 1, n, size - n - 1); + size = size - 1; + res + } + + /** Clears the buffer contents. + */ + def clear: Unit = { + size = 0; + } + + /** Return a clone of this buffer. + * + * @return an <code>ArrayBuffer</code> with the same elements. + */ + override def clone(): Buffer[A] = { + val res = new ArrayBuffer[A]; + res ++= this; + res + } + + /** Checks if two buffers are structurally identical. + * + * @return true, iff both buffers contain the same sequence of elements. + */ + override def equals(obj: Any): Boolean = obj match { + case that: ArrayBuffer[A] => + elements.zip(that.elements).forall { + case Pair(thiselem, thatelem) => thiselem == thatelem; + } + case _ => false + } + + /** Defines the prefix of the string representation. + */ + override protected def stringPrefix: String = "ArrayBuffer"; +} diff --git a/src/library/scala/collection/mutable/BitSet.scala b/src/library/scala/collection/mutable/BitSet.scala new file mode 100644 index 0000000000..40d0bf0529 --- /dev/null +++ b/src/library/scala/collection/mutable/BitSet.scala @@ -0,0 +1,87 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable ; + +/** mutable, resizable bit sets, to represent dense sets of small integers + * Bit indices are between 0..(size-1) inclusive + * @author Burak Emir + * @param initSize: initial size in nbits + */ +[serializable] +class BitSet(initSize: Int) extends scala.collection.BitSet { + import scala.runtime.compat.Platform.arraycopy; + + /** default constructor, initial size of 512 bits */ + def this() = this( 512 ); // ResizableArray.initialSize << 5 + + [serializable] + class ByteArray extends AnyRef with ResizableArray[Int] { + + final def ensureBits(nbits: Int): Unit = { + super[ResizableArray].ensureSize(memsize( nbits )); + } + final def and(j: Int, mask:Int): Unit = { + array.update( j, array(j) & mask ); + } + final def or(j: Int, mask:Int): Unit = { + array.update( j, array(j) | mask ); + } + def get(j:Int, mask:Int):Boolean = { + (array(j) & mask) != 0; + } + def freeze: Array[Int] = { + val arr = new Array[Int]( array.length ); + arraycopy(array, 0, arr, 0, arr.length); + arr + } + } + + /** size of this bitset in nbytes */ + var size: Int = initSize; + protected val internal = new ByteArray(); + internal.ensureBits( size ); + + /** ensure that this bitset can store at least nbits bits */ + def ensureSize(n: Int): Unit = { + internal.ensureBits( n ); + if( size < n ) size = n; + } + + /** calls set or clear for i-th bit */ + final def set(i: Int, b: Boolean): Unit = if( b ) set(i) else clear(i); + + /** sets i-th bit to true. Grows to size i+1 if needed. */ + final def set(i: Int): Unit = { + ensureSize(i+1); + val j = (i >>> 5); + val mask = (1 << (i & 0x1F)); + internal.or(j, mask); + } + + /** clears i-th bit. Grows to size i+1 if needed. */ + def clear(i: Int): Unit = { + ensureSize(i+1); + val j = (i >>> 5); + val mask = (1 << (i & 0x1F)); + internal.and(j, ~mask); + } + + /** gets i-th bit. Grows to size i+1 if needed. */ + def apply(i: Int):Boolean = { + val j = (i >>> 5); + val mask = (1 << (i & 0x1F)); + internal.get(j, mask); + } + + def toArray: Array[Int] = internal.freeze; + + def makeImmutable = new scala.collection.immutable.BitSet(this); + +} diff --git a/src/library/scala/collection/mutable/Buffer.scala b/src/library/scala/collection/mutable/Buffer.scala new file mode 100644 index 0000000000..f1e8d0fe9b --- /dev/null +++ b/src/library/scala/collection/mutable/Buffer.scala @@ -0,0 +1,214 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** Buffers are used to create sequences of elements incrementally by + * appending, prepending, or inserting new elements. It is also + * possible to access and modify elements in a random access fashion + * via the index of the element in the current sequence. + * + * @author Matthias Zenger + * @version 1.1, 02/03/2004 + */ +[cloneable] +trait Buffer[A] extends AnyRef with Seq[A] with Scriptable[Message[Pair[Location, A]]] { + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +(elem: A): Buffer[A]; + + /** Append a single element to this buffer. + * + * @param elem the element to append. + */ + def +=(elem: A): Unit = this + elem; + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def ++(elems: Iterable[A]): Buffer[A] = this ++ elems.elements; + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def ++(iter: Iterator[A]): Buffer[A] = { + iter.foreach(e => this += e); + this + } + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + def ++=(elems: Iterable[A]): Unit = this ++ elems.elements; + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + def ++=(it: Iterator[A]): Unit = this ++ it; + + /** Appends a sequence of elements to this buffer. + * + * @param elems the elements to append. + */ + def append(elems: A*): Unit = this ++ elems; + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + def appendAll(iter: Iterable[A]): Unit = this ++ iter; + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +:(elem: A): Buffer[A]; + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def ++:(iter: Iterable[A]): Buffer[A] = { + iter.elements.toList.reverse.foreach(e => e +: this); + this + } + + /** Prepend an element to this list. + * + * @param elem the element to prepend. + */ + def prepend(elems: A*): Unit = elems ++: this; + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def prependAll(elems: Iterable[A]): Unit = elems ++: this; + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert the new elements at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param elems the new elements to insert. + */ + def insert(n: Int, elems: A*): Unit = insertAll(n, elems); + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert a new element at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + */ + def insertAll(n: Int, iter: Iterable[A]): Unit; + + /** Replace element at index <code>n</code> with the new element + * <code>newelem</code>. + * + * @param n the index of the element to replace. + * @param newelem the new element. + */ + def update(n: Int, newelem: A): Unit; + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + def remove(n: Int): A; + + /** Removes the first <code>n</code> elements. + * + * @param n the number of elements to remove from the beginning + * of this buffer. + */ + def trimStart(n: Int): Unit = { + var i = n; + while (i > 0) { remove(0); i = i - 1; } + } + + /** Removes the last <code>n</code> elements. + * + * @param n the number of elements to remove from the end + * of this buffer. + */ + def trimEnd(n: Int): Unit = { + var i = n; + while (i > 0) { remove(length - 1); i = i - 1; } + } + + /** Clears the buffer contents. + */ + def clear: Unit; + + /** Send a message to this scriptable object. + * + * @param cmd the message to send. + */ + def <<(cmd: Message[Pair[Location, A]]): Unit = cmd match { + case Include(Pair(l, elem)) => l match { + case Start => prepend(elem); + case End => append(elem); + case Index(n) => insert(n, elem); + case _ => error("message " + cmd + " not understood"); + } + case Update(Pair(l, elem)) => l match { + case Start => update(0, elem); + case End => update(length - 1, elem); + case Index(n) => update(n, elem); + case _ => error("message " + cmd + " not understood"); + } + case Remove(Pair(l, _)) => l match { + case Start => remove(0); + case End => remove(length - 1); + case Index(n) => remove(n); + case _ => error("message " + cmd + " not understood"); + } + case Reset() => clear; + case s: Script[Pair[Location, A]] => s.elements foreach <<; + case _ => error("message " + cmd + " not understood"); + } + + /** Return a clone of this buffer. + * + * @return a buffer with the same elements. + */ + override def clone(): Buffer[A] = super.clone().asInstanceOf[Buffer[A]]; + + /** The hashCode method always yields an error, since it is not + * safe to use buffers as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); + + /** Defines the prefix of the string representation. + */ + override protected def stringPrefix: String = "Buffer"; +} diff --git a/src/library/scala/collection/mutable/BufferProxy.scala b/src/library/scala/collection/mutable/BufferProxy.scala new file mode 100644 index 0000000000..c06202024a --- /dev/null +++ b/src/library/scala/collection/mutable/BufferProxy.scala @@ -0,0 +1,147 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This is a simple proxy class for <code>scala.collection.mutable.Buffer</code>. + * It is most useful for assembling customized set abstractions + * dynamically using object composition and forwarding. + * + * @author Matthias Zenger + * @version 1.0, 16/04/2004 + */ +trait BufferProxy[A] extends Buffer[A] with Proxy { + + def self: Buffer[A]; + + def length: Int = self.length; + + def elements: Iterator[A] = self.elements; + + def apply(n: Int): A = self.apply(n); + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +(elem: A): Buffer[A] = self.+(elem); + + /** Append a single element to this buffer. + * + * @param elem the element to append. + */ + override def +=(elem: A): Unit = self.+=(elem); + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++(iter: Iterable[A]): Buffer[A] = self.++(iter); + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + override def ++=(iter: Iterable[A]): Unit = self.++=(iter); + + /** Appends a sequence of elements to this buffer. + * + * @param elems the elements to append. + */ + override def append(elems: A*): Unit = self.++=(elems); + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + override def appendAll(iter: Iterable[A]): Unit = self.appendAll(iter); + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +:(elem: A): Buffer[A] = self.+:(elem); + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++:(iter: Iterable[A]): Buffer[A] = self.++:(iter); + + /** Prepend an element to this list. + * + * @param elem the element to prepend. + */ + override def prepend(elems: A*): Unit = self.prependAll(elems); + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def prependAll(elems: Iterable[A]): Unit = self.prependAll(elems); + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert the new elements at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param elems the new elements to insert. + */ + override def insert(n: Int, elems: A*): Unit = self.insertAll(n, elems); + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert a new element at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + */ + def insertAll(n: Int, iter: Iterable[A]): Unit = self.insertAll(n, iter); + + /** Replace element at index <code>n</code> with the new element + * <code>newelem</code>. + * + * @param n the index of the element to replace. + * @param newelem the new element. + */ + def update(n: Int, newelem: A): Unit = self.update(n, newelem); + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + def remove(n: Int): A = self.remove(n); + + /** Clears the buffer contents. + */ + def clear: Unit = self.clear; + + /** Send a message to this scriptable object. + * + * @param cmd the message to send. + */ + override def <<(cmd: Message[Pair[Location, A]]): Unit = self << cmd; + + /** Return a clone of this buffer. + * + * @return a <code>Buffer</code> with the same elements. + */ + override def clone(): Buffer[A] = new BufferProxy[A] { def self = BufferProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/DefaultMapModel.scala b/src/library/scala/collection/mutable/DefaultMapModel.scala new file mode 100644 index 0000000000..6ae089bc3e --- /dev/null +++ b/src/library/scala/collection/mutable/DefaultMapModel.scala @@ -0,0 +1,52 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This trait is used internally. It implements the mutable <code>Map</code> + * trait in terms of three functions: <code>findEntry</code>, <code>addEntry</code>, + * and <code>entries</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait DefaultMapModel[A, B] extends AnyRef with scala.collection.mutable.Map[A, B] { + protected type Entry = DefaultEntry[A,B]; + + protected def findEntry(key: A): Option[Entry]; + + protected def addEntry(e: Entry): Unit; + + protected def entries: Iterator[Entry]; + + def get(key: A) = findEntry(key) match { + case None => None + case Some(e) => Some(e.value); + } + + def update(key: A, value: B) = findEntry(key) match { + case None => addEntry(new Entry(key, value)); + case Some(e) => e.value = value; + } + + def elements = new Iterator[Pair[A, B]] { + val iter = entries; + def hasNext = iter.hasNext; + def next = iter.next.toPair; + } +} + +[serializable] +protected class DefaultEntry[A,B](k: A, v: B) extends AnyRef { + def key = k; + var value = v; + def toPair = Pair(k, value); + override def toString() = k.toString() + " -> " + value; +} diff --git a/src/library/scala/collection/mutable/DoubleLinkedList.scala b/src/library/scala/collection/mutable/DoubleLinkedList.scala new file mode 100644 index 0000000000..977f42b23a --- /dev/null +++ b/src/library/scala/collection/mutable/DoubleLinkedList.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** This extensible class may be used as a basis for implementing double + * linked lists. Type variable <code>A</code> refers to the element type + * of the list, type variable <code>This</code> is used to model self + * types of linked lists. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +abstract class DoubleLinkedList[A, This <: DoubleLinkedList[A, This]]: This + extends SingleLinkedList[A, This] { + + var prev: This = _; + + override def append(that: This): Unit = + if (that == null) + () + else if (next == null) { + next = that; + that.prev = this; + } else + next.append(that); + + override def insert(that: This): Unit = if (that != null) { + that.append(next); + next = that; + that.prev = this; + } + + def remove: Unit = { + if (next != null) + next.prev = prev; + if (prev != null) + prev.next = next; + prev = null; + next = null; + } +} diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala new file mode 100644 index 0000000000..dcdde2e40c --- /dev/null +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** This class implements mutable maps using a hashtable. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +class HashMap[A, B] extends scala.collection.mutable.Map[A, B] + with HashTable[A] + with DefaultMapModel[A, B] { + + def -=(key: A): Unit = removeEntry(key); + + protected def entryKey(e: Entry) = e.key; + + override def clear = { + initTable(table); + tableSize = 0; + } + + override def clone(): Map[A, B] = { + val res = new HashMap[A, B]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/HashSet.scala b/src/library/scala/collection/mutable/HashSet.scala new file mode 100644 index 0000000000..7f71b037b4 --- /dev/null +++ b/src/library/scala/collection/mutable/HashSet.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class implements mutable sets using a hashtable. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +class HashSet[A] extends scala.collection.mutable.Set[A] with HashTable[A] { + + def contains(elem: A): Boolean = findEntry(elem) match { + case None => false + case Some(_) => true + } + + def +=(elem: A): Unit = findEntry(elem) match { + case None => addEntry(elem); + case Some(_) => + } + + def -=(elem: A): Unit = removeEntry(elem); + + def elements = entries; + + def clear = { + initTable(table); + tableSize = 0; + } + + protected type Entry = A; + + protected def entryKey(e: Entry) = e; + + override def clone(): HashSet[A] = { + val res = new HashSet[A]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/HashTable.scala b/src/library/scala/collection/mutable/HashTable.scala new file mode 100644 index 0000000000..984f33f561 --- /dev/null +++ b/src/library/scala/collection/mutable/HashTable.scala @@ -0,0 +1,148 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +import Predef._; + +/** This class can be used to construct data structures that are based + * on hashtables. Class <code>HashTable[A]</code> implements a hashtable + * that maps keys of type <code>A</code> to values of the fully abstract + * member type <code>Entry</code>. Classes that make use of <code>HashTable</code> + * have to provide an implementation for <code>Entry</code> and implement the + * function <code>entryKey</code>.<p/> + * + * There are mainly two parameters that affect the performance of a hashtable: + * the <i>initial size</i> and the <i>load factor</i>. The <i>size</i> + * refers to the number of <i>buckets</i> in the hashtable, and the <i>load + * factor</i> is a measure of how full the hashtable is allowed to get before + * its size is automatically doubled. Both parameters may be changed by + * overriding the corresponding values in class <code>HashTable</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class HashTable[A] extends AnyRef { + + /** The load factor for the hash table. + */ + protected val loadFactor: Float = 0.75f; + + /** The initial size of the hash table. + */ + protected val initialSize: Int = 16; + + /** The initial threshold + */ + protected val initialThreshold: Int = newThreshold(initialSize); + + /** The actual hash table. + */ + protected var table: Array[List[Entry]] = new Array(initialSize); + initTable(table); + + /** The number of mappings contained in this hash table. + */ + protected var tableSize: Int = 0; + + /** The next size value at which to resize (capacity * load factor). + */ + protected var threshold: Int = initialThreshold; + + /** Returns the size of this hash map. + */ + def size = tableSize; + + protected def findEntry(key: A): Option[Entry] = + table(index(elemHashCode(key))).find(entryFor(key)); + + protected def addEntry(e: Entry): Unit = { + val h = index(elemHashCode(entryKey(e))); + table(h) = e :: table(h); + tableSize = tableSize + 1; + if (tableSize > threshold) + resize(2 * table.length); + } + + protected def removeEntry(key: A): Unit = { + val old = findEntry(key); + old match { + case None => + case Some(e) => { + val idx = index(elemHashCode(key)); + table(idx) = table(idx).filter(e => !elemEquals(entryKey(e), key)); + tableSize = tableSize - 1; + } + } + } + + protected type Entry; + + protected def entryKey(e: Entry): A; + + protected def entries: Iterator[Entry] = new Iterator[Entry] { + val iterTable = table; + var idx = table.length - 1; + var xs = iterTable(idx); + scan(); + def hasNext = !xs.isEmpty; + def next = { + val res = xs.head; + xs = xs.tail; + scan(); + res; + } + def scan(): Unit = if (xs.isEmpty && (idx > 0)) { + idx = idx - 1; + xs = iterTable(idx); + scan(); + } + } + + private def entryFor(key: A) = (e: Entry => elemEquals(entryKey(e), key)); + + protected def initTable(tb: Array[List[Entry]]): Unit = { + var i = tb.length - 1; + while (i >= 0) { + tb(i) = Nil; + i = i - 1; + } + } + + private def newThreshold(size: Int) = + (size * loadFactor).asInstanceOf[Int]; + + private def resize(newSize: Int) = { + val newTable: Array[List[Entry]] = new Array(newSize); + initTable(newTable); + var i = table.length - 1; + while (i >= 0) { + table(i).foreach { e => { + val idx = improve(elemHashCode(entryKey(e))) & (newSize - 1); + newTable(idx) = e :: newTable(idx); + }}; + i = i - 1; + } + table = newTable; + threshold = newThreshold(newSize); + } + + protected def elemEquals(key1: A, key2: A): Boolean = (key1 == key2); + + protected def elemHashCode(key: A) = key.hashCode(); + + protected final def improve(hcode: Int) = { + var h: Int = hcode + ~(hcode << 9); + h = h ^ (h >>> 14); + h = h + (h << 4); + h ^ (h >>> 10); + } + + protected final def index(hcode: Int) = improve(hcode) & (table.length - 1); +} diff --git a/src/library/scala/collection/mutable/History.scala b/src/library/scala/collection/mutable/History.scala new file mode 100644 index 0000000000..43be629602 --- /dev/null +++ b/src/library/scala/collection/mutable/History.scala @@ -0,0 +1,42 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** <code>History[A, B]</code> objects may subscribe to events of + * type <code>A</code> published by an object of type <code>B</code>. + * The history subscriber object records all published events + * up to maximum number of <code>maxHistory</code> events. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +class History[A, B] extends AnyRef with Subscriber[A, B] with Iterable[Pair[B, A]] { + + protected val log: Queue[Pair[B, A]] = new Queue[Pair[B, A]]; + + val maxHistory: Int = 1000; + + def notify(pub: B, event: A): Unit = { + if (log.length >= maxHistory) { + val old = log.dequeue; + } + log.enqueue(Pair(pub, event)); + } + + def elements: Iterator[Pair[B, A]] = log.elements; + + def events: Iterator[A] = log.elements.map { case Pair(_, e) => e } + + def size: Int = log.length; + + def clear: Unit = log.clear; +} diff --git a/src/library/scala/collection/mutable/ImmutableMapAdaptor.scala b/src/library/scala/collection/mutable/ImmutableMapAdaptor.scala new file mode 100644 index 0000000000..5167636b40 --- /dev/null +++ b/src/library/scala/collection/mutable/ImmutableMapAdaptor.scala @@ -0,0 +1,64 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class can be used as an adaptor to create mutable maps from + * immutable map implementations. Only method <code>empty</code> has + * to be redefined if the immutable map on which this mutable map is + * originally based is not empty. <code>empty</code> is supposed to + * return the representation of an empty map. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +[serializable] +class ImmutableMapAdaptor[A, B](m: scala.collection.immutable.Map[A, B]) extends Map[A, B] { + + protected var imap = m; + + def size: Int = imap.size; + + def get(key: A): Option[B] = imap.get(key); + + override def isEmpty: Boolean = imap.isEmpty; + + override def apply(key: A): B = imap.apply(key); + + override def contains(key: A): Boolean = imap.contains(key); + + override def isDefinedAt(key: A) = imap.isDefinedAt(key); + + override def keys: Iterator[A] = imap.keys; + + override def values: Iterator[B] = imap.values; + + def elements: Iterator[Pair[A, B]] = imap.elements; + + override def foreach(f: (A, B) => Unit) = imap.foreach(f); + + override def toList: List[Pair[A, B]] = imap.toList; + + override def toString() = imap.toString(); + + def update(key: A, value: B): Unit = { imap = imap.update(key, value); } + + def -=(key: A): Unit = { imap = imap - key; } + + override def clear: Unit = { imap = empty; } + + override def map(f: (A, B) => B): Unit = { imap = imap.map(f); } + + override def filter(p: (A, B) => Boolean): Unit = { imap = imap.filter(p); } + + override def mappingToString(p: Pair[A, B]) = imap.mappingToString(p); + + protected def empty: scala.collection.immutable.Map[A, B] = m; +} diff --git a/src/library/scala/collection/mutable/ImmutableSetAdaptor.scala b/src/library/scala/collection/mutable/ImmutableSetAdaptor.scala new file mode 100644 index 0000000000..623074b3d9 --- /dev/null +++ b/src/library/scala/collection/mutable/ImmutableSetAdaptor.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class can be used as an adaptor to create mutable sets from + * immutable set implementations. Only method <code>empty</code> has + * to be redefined if the immutable set on which this mutable set is + * originally based is not empty. <code>empty</code> is supposed to + * return the representation of an empty set. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +[serializable] +class ImmutableSetAdaptor[A](s: scala.collection.immutable.Set[A]) extends Set[A] { + + protected var set = s; + + def size: Int = set.size; + + override def isEmpty: Boolean = set.isEmpty; + + def contains(elem: A): Boolean = set.contains(elem); + + override def foreach(f: A => Unit): Unit = set.foreach(f); + + override def exists(p: A => Boolean): Boolean = set.exists(p); + + override def toList: List[A] = set.toList; + + override def toString() = set.toString(); + + def elements: Iterator[A] = set.elements; + + def +=(elem: A): Unit = { set = set + elem; } + + def -=(elem: A): Unit = { set = set - elem; } + + def clear: Unit = { set = empty; } + + protected def empty: scala.collection.immutable.Set[A] = s; +} diff --git a/src/library/scala/collection/mutable/JavaMapAdaptor.scala b/src/library/scala/collection/mutable/JavaMapAdaptor.scala new file mode 100644 index 0000000000..3679e787e8 --- /dev/null +++ b/src/library/scala/collection/mutable/JavaMapAdaptor.scala @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class can be used as an adaptor to create mutable maps from + * Java classes that implementat the <code>java.util.Map</code> interface. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +class JavaMapAdaptor[A, B](jmap: java.util.Map) extends Map[A, B] { + + def size: Int = jmap.size(); + + def get(key: A): Option[B] = + if (jmap.containsKey(key)) Some(jmap.get(key).asInstanceOf[B]) else None; + + override def isEmpty: Boolean = jmap.isEmpty(); + + override def apply(key: A): B = jmap.get(key).asInstanceOf[B]; + + override def contains(key: A): Boolean = jmap.containsKey(key); + + override def isDefinedAt(key: A) = jmap.containsKey(key); + + override def keys: Iterator[A] = new Iterator[A] { + val iter = jmap.keySet().iterator(); + def hasNext = iter.hasNext(); + def next = iter.next().asInstanceOf[A]; + } + + override def values: Iterator[B] = new Iterator[B] { + val iter = jmap.values().iterator(); + def hasNext = iter.hasNext(); + def next = iter.next().asInstanceOf[B]; + } + + def elements: Iterator[Pair[A, B]] = new Iterator[Pair[A, B]] { + val iter = jmap.keySet().iterator(); + def hasNext = iter.hasNext(); + def next = { + val key = iter.next().asInstanceOf[A]; + Pair(key, apply(key)) + } + } + + def update(key: A, value: B): Unit = { val x = jmap.put(key, value); } + + def -=(key: A): Unit = { val x = jmap.remove(key); } + + override def clear: Unit = jmap.clear(); + + override def clone(): Map[A, B] = { + val res = new HashMap[A, B]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/JavaSetAdaptor.scala b/src/library/scala/collection/mutable/JavaSetAdaptor.scala new file mode 100644 index 0000000000..3279e85a13 --- /dev/null +++ b/src/library/scala/collection/mutable/JavaSetAdaptor.scala @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class can be used as an adaptor to create mutable sets from + * Java classes that implement interface <code>java.util.Set</code>. + * + * @author Matthias Zenger + * @version 1.0, 19/09/2003 + */ +class JavaSetAdaptor[A](jset: java.util.Set) extends Set[A] { + + def size: Int = jset.size(); + + override def isEmpty: Boolean = jset.isEmpty(); + + def contains(elem: A): Boolean = jset.contains(elem); + + def elements: Iterator[A] = new Iterator[A] { + val iter = jset.iterator(); + def hasNext = iter.hasNext(); + def next = iter.next().asInstanceOf[A]; + } + + def +=(elem: A): Unit = { val x = jset.add(elem); } + + def -=(elem: A): Unit = { val x = jset.remove(elem); } + + def clear: Unit = jset.clear(); + + override def clone(): Set[A] = { + val res = new HashSet[A]; + res ++= this; + res; + } +} diff --git a/src/library/scala/collection/mutable/LinkedList.scala b/src/library/scala/collection/mutable/LinkedList.scala new file mode 100644 index 0000000000..c08928a04a --- /dev/null +++ b/src/library/scala/collection/mutable/LinkedList.scala @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** This class implements single linked lists where both the head (<code>elem</code>) + * and the tail (<code>next</code>) are mutable. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +class LinkedList[A](head: A, tail: LinkedList[A]) + extends SingleLinkedList[A, LinkedList[A]] +{ + elem = head; + next = tail; + + override def equals(obj: Any): Boolean = ( + obj.isInstanceOf[LinkedList[A]] + && toList.equals((obj.asInstanceOf[LinkedList[A]]).toList) + ); + + override protected def stringPrefix: String = "LinkedList"; +} + + + diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala new file mode 100644 index 0000000000..527741bbc9 --- /dev/null +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -0,0 +1,143 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** A list buffer uses a list internally to assemble sequences of elements + * incrementally by appending or prepending new elements. It is also + * possible to access and modify elements in a random access fashion + * via the index of the element in the sequence. + * + * @author Matthias Zenger + * @version 1.0, 15/03/2004 + */ +class ListBuffer[A] extends Buffer[A] with MutableList[A] { + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +(elem: A): Buffer[A] = { appendElem(elem); this } + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +:(elem: A): Buffer[A] = { prependElem(elem); this } + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert a new element at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + */ + def insertAll(n: Int, iter: Iterable[A]): Unit = { + val it = iter.elements; + while (it.hasNext) { + val newelem = it.next; + if (n == 0) + prepend(newelem); + else if (n >= len) + append(newelem); + else { + var elem = first; + var i = n; + while (i > 1) { + elem = elem.next; + if (elem == null) + error("cannot insert element " + n + " in ListBuffer"); + i = i - 1; + } + val old = elem.next; + elem.next = new LinkedList[A](newelem, old); + } + } + } + + /** Replace element at index <code>n</code> with the new element + * <code>newelem</code>. + * + * @param n the index of the element to replace. + * @param newelem the new element. + */ + def update(n: Int, newelem: A): Unit = { + var elem = first; + var i = n; + while (i > 0) { + elem = elem.next; + if (elem == null) + error("cannot update element " + n + " in Buffer"); + i = i - 1; + } + elem.elem = newelem; + } + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + def remove(n: Int): A = { + val old = apply(n); + if (n >= len) + error("cannot remove element " + n + " in Buffer"); + if ((n == 0) && (len == 1)) { + first = null; + last = null; + } else if (n == 0) { + first = first.next; + } else { + var elem = first; + var i = n; + while (i > 1) { + elem = elem.next; + i = i - 1; + } + elem.next = elem.next.next; + if (n == (len - 1)) { + last = elem.next; + } + } + len = len - 1; + old; + } + + /** Clears the buffer contents. + */ + def clear: Unit = reset; + + /** Return a clone of this buffer. + * + * @return an <code>ArrayBuffer</code> with the same elements. + */ + override def clone(): Buffer[A] = { + val res = new ListBuffer[A]; + res ++= this; + res + } + + /** Checks if two buffers are structurally identical. + * + * @return true, iff both buffers contain the same sequence of elements. + */ + override def equals(obj: Any): Boolean = obj match { + case that: ListBuffer[A] => + elements.zip(that.elements).forall { + case Pair(thiselem, thatelem) => thiselem == thatelem; + } + case _ => false + } + + /** Defines the prefix of the string representation. + */ + override protected def stringPrefix: String = "ListBuffer"; +} diff --git a/src/library/scala/collection/mutable/Location.scala b/src/library/scala/collection/mutable/Location.scala new file mode 100644 index 0000000000..30abd06781 --- /dev/null +++ b/src/library/scala/collection/mutable/Location.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** Class <code>Location</code> describes locations in messages implemented + * by class <code>Message</code>. + * + * @author Matthias Zenger + * @version 1.0, 10/05/2004 + */ +trait Location; + +case object NA extends Location; + +case object Start extends Location; + +case object End extends Location; + +case class Index(n: Int) extends Location; diff --git a/src/library/scala/collection/mutable/Map.scala b/src/library/scala/collection/mutable/Map.scala new file mode 100644 index 0000000000..75f544193e --- /dev/null +++ b/src/library/scala/collection/mutable/Map.scala @@ -0,0 +1,169 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** This trait represents mutable maps. Concrete map implementations + * just have to provide functionality for the abstract methods in + * <code>scala.collection.Map</code> as well as for <code>update</code>, + * and <code>remove</code>. + * + * @author Matthias Zenger + * @version 1.1, 09/05/2004 + */ +[cloneable] +trait Map[A, B] extends AnyRef with scala.collection.Map[A, B] with Scriptable[Message[Pair[A, B]]] { + + /** This method allows one to add a new mapping from <code>key</code> + * to <code>value</code> to the map. If the map already contains a + * mapping for <code>key</code>, it will be overridden by this + * function. + * + * @param key + * @param value + */ + def update(key: A, value: B): Unit; + + /** This method defines syntactic sugar for adding or modifying + * mappings. It is typically used in the following way: + * <pre> + * map += key -> value; + * </pre> + */ + def +=(key: A): MapTo = new MapTo(key); + + /** This method adds all the mappings provided by an iterator of + * parameter <code>map</code> to the map. + * + * @param map + */ + def ++=(map: Iterable[Pair[A, B]]): Unit = ++=(map.elements); + + /** This method adds all the mappings provided by an iterator of + * parameter <code>map</code> to the map. + * + * @param it + */ + def ++=(it: Iterator[Pair[A, B]]): Unit = it foreach { + case Pair(key, value) => update(key, value); + } + + /** <code>incl</code> can be used to add many mappings at the same time + * to the map. The method assumes that a mapping is represented + * by a <code>Pair</code> object who's first component denotes the + * key, and who's second component refers to the value. + * + * @param mappings + */ + def incl(mappings: Pair[A, B]*): Unit = ++=(mappings.elements); + + /** This method removes a mapping from the given <code>key</code>. + * If the map does not contain a mapping for the given key, the + * method does nothing. + */ + def -=(key: A): Unit; + + /** This method removes all the mappings for keys provided by an + * iterator over the elements of the <code>keys</code> object. + * + * @param keys + */ + def --=(keys: Iterable[A]): Unit = --=(keys.elements); + + /** This method removes all the mappings for keys provided by an + * iterator over the elements of the <code>keys</code> object. + * + * @param it + */ + def --=(it: Iterator[A]): Unit = it foreach -=; + + /** This method will remove all the mappings for the given sequence + * of keys from the map. + * + * @param keys + */ + def excl(keys: A*): Unit = --=(keys.elements); + + /** Removes all mappings from the map. After this operation is + * completed, the map is empty. + */ + def clear: Unit = keys foreach -=; + + /** This function transforms all the values of mappings contained + * in this map with function <code>f</code>. + * + * @param f + */ + def map(f: (A, B) => B): Unit = elements foreach { + case Pair(key, value) => update(key, f(key, value)); + } + + /** This method removes all the mappings for which the predicate + * <code>p</code> returns <code>false</code>. + * + * @param p + */ + def filter(p: (A, B) => Boolean): Unit = toList foreach { + case Pair(key, value) => if (!p(key, value)) -=(key); + } + + /** Send a message to this scriptable object. + * + * @param cmd the message to send. + */ + def <<(cmd: Message[Pair[A, B]]): Unit = cmd match { + case Include(Pair(k, v)) => update(k, v); + case Update(Pair(k, v)) => update(k, v); + case Remove(Pair(k, _)) => this -= k; + case Reset() => clear; + case s: Script[Pair[A, B]] => s.elements foreach <<; + case _ => error("message " + cmd + " not understood"); + } + + /** Return a clone of this map. + * + * @return an map with the same elements. + */ + override def clone(): Map[A, B] = super.clone().asInstanceOf[Map[A, B]]; + + /** The hashCode method always yields an error, since it is not + * safe to use mutable maps as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); + + /** Returns a string representation of this map which shows + * all the mappings. + */ + override def toString() = + if (size == 0) + "{}" + else + "{" + { + val iter = elements; + var res = mappingToString(iter.next); + while (iter.hasNext) { + res = res + ", " + mappingToString(iter.next); + } + res; + } + "}"; + + /** This method controls how a mapping is represented in the string + * representation provided by method <code>toString</code>. + * + * @param p + */ + def mappingToString(p: Pair[A, B]) = p._1.toString() + " -> " + p._2; + + class MapTo(key: A) { + def ->(value: B): Unit = update(key, value); + } + +} diff --git a/src/library/scala/collection/mutable/MapProxy.scala b/src/library/scala/collection/mutable/MapProxy.scala new file mode 100644 index 0000000000..8081e3a66a --- /dev/null +++ b/src/library/scala/collection/mutable/MapProxy.scala @@ -0,0 +1,53 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This is a simple wrapper class for <code>scala.collection.mutable.Map</code>. + * It is most useful for assembling customized map abstractions + * dynamically using object composition and forwarding. + * + * @author Matthias Zenger + * @version 1.0, 21/07/2003 + */ +trait MapProxy[A, B] extends Map[A, B] with scala.collection.MapProxy[A, B] { + + def self: Map[A, B]; + + def update(key: A, value: B): Unit = self.update(key, value); + + override def ++=(map: Iterable[Pair[A, B]]): Unit = self ++= map; + + override def ++=(it: Iterator[Pair[A, B]]): Unit = self ++= it; + + override def incl(mappings: Pair[A, B]*): Unit = self ++= mappings; + + def -=(key: A): Unit = self -= key; + + override def --=(keys: Iterable[A]): Unit = self --= keys; + + override def --=(it: Iterator[A]): Unit = self --= it; + + override def excl(keys: A*): Unit = self --= keys; + + override def clear: Unit = self.clear; + + override def map(f: (A, B) => B): Unit = self.map(f); + + override def filter(p: (A, B) => Boolean): Unit = self.filter(p); + + override def toString() = self.toString(); + + override def mappingToString(p: Pair[A, B]) = self.mappingToString(p); + + override def <<(cmd: Message[Pair[A, B]]): Unit = self << cmd; + + override def clone(): Map[A, B] = new MapProxy[A, B] { def self = MapProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/Message.scala b/src/library/scala/collection/mutable/Message.scala new file mode 100644 index 0000000000..bd805c5d6e --- /dev/null +++ b/src/library/scala/collection/mutable/Message.scala @@ -0,0 +1,77 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** Class <code>Message</code> represents messages that are issued by observable + * collection classes whenever a data structure is changed. Class <code>Message</code> + * has several subclasses for the various kinds of events: <code>Update</code> + * <code>Remove</code>, <code>Include</code>, <code>Reset</code>, and + * <code>Script</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait Message[+A]; + +/** This observable update refers to inclusion operations that add new elements + * to collection classes. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +case class Include[+I](elem: I) extends Message[I]; + +/** This observable update refers to destructive modification operations + * of elements from collection classes. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +case class Update[+A](elem: A) extends Message[A]; + +/** This observable update refers to removal operations of elements + * from collection classes. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +case class Remove[+A](elem: A) extends Message[A]; + +/** This command refers to reset operations. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +case class Reset[+A]() extends Message[A]; + +/** Objects of this class represent compound messages consisting + * of a sequence of other messages. + * + * @author Matthias Zenger + * @version 1.0, 10/05/2004 + */ +class Script[A] extends ArrayBuffer[Message[A]] with Message[A] { + + override def toString(): String = { + var res = "Script("; + var it = elements; + var i = 1; + while (it.hasNext) { + if (i > 1) + res = res + ", "; + res = res + "[" + i + "] " + it.next; + i = i + 1; + } + res + ")"; + } + + override def hashCode(): Int = error("scripts are not suitable as hash keys"); +} diff --git a/src/library/scala/collection/mutable/MultiMap.scala b/src/library/scala/collection/mutable/MultiMap.scala new file mode 100644 index 0000000000..0569d0b0c9 --- /dev/null +++ b/src/library/scala/collection/mutable/MultiMap.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + +/** This class is typically used as a mixin. It turns maps which map <code>A</code> + * to <code>Set[B]</code> objects into multi maps which map <code>A</code> to + * <code>B</code> objects. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait MultiMap[A, B] extends scala.collection.mutable.Map[A, scala.collection.mutable.Set[B]] { + protected def makeSet: scala.collection.mutable.Set[B] = new HashSet[B]; + + def add(key: A, value: B): Unit = get(key) match { + case None => val set = makeSet; + set += value; + this(key) = set; + case Some(set) => set += value; + } + + def remove(key: A, value: B) = get(key) match { + case None => + case Some(set) => set -= value; + } + + def entryExists(key: A, p: B => Boolean): Boolean = get(key) match { + case None => false + case Some(set) => set exists p; + } +} diff --git a/src/library/scala/collection/mutable/MutableList.scala b/src/library/scala/collection/mutable/MutableList.scala new file mode 100644 index 0000000000..37bb9ada63 --- /dev/null +++ b/src/library/scala/collection/mutable/MutableList.scala @@ -0,0 +1,77 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class is used internally to represent mutable lists. It is the + * basis for the implementation of the classes <code>Buffer</code>, + * <code>Stack</code>, and <code>Queue</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class MutableList[A] extends Seq[A] with PartialFunction[Int, A] { + + protected var first: LinkedList[A] = null; + protected var last: LinkedList[A] = null; + protected var len: Int = 0; + + /** Returns the length of this list. + */ + def length: Int = len; + + /** Returns the <code>n</code>th element of this list. This method + * yields an error if the element does not exist. + */ + def apply(n: Int): A = get(n) match { + case None => error("element not found") + case Some(value) => value + } + + /** Returns the <code>n</code>th element of this list or <code>None</code> + * if this element does not exist. + */ + def get(n: Int): Option[A] = first.get(n); + + protected def prependElem(elem: A): Unit = { + first = new LinkedList[A](elem, first); + if (len == 0) + last = first; + len = len + 1; + } + + protected def appendElem(elem: A): Unit = { + if (len == 0) + prependElem(elem); + else { + last.next = new LinkedList[A](elem, null); + last = last.next; + len = len + 1; + } + } + + protected def reset: Unit = { + first = null; + last = null; + len = 0; + } + + /** Returns an iterator over all elements of this list. + */ + def elements: Iterator[A] = + if (first == null) Nil.elements else first.elements; + + /** Returns an instance of <code>scala.List</code> containing the same + * sequence of elements. + */ + override def toList: List[A] = if (first == null) Nil else first.toList; + + override protected def stringPrefix: String = "MutableList"; +} diff --git a/src/library/scala/collection/mutable/ObservableBuffer.scala b/src/library/scala/collection/mutable/ObservableBuffer.scala new file mode 100644 index 0000000000..e3439b1dff --- /dev/null +++ b/src/library/scala/collection/mutable/ObservableBuffer.scala @@ -0,0 +1,74 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class is typically used as a mixin. It adds a subscription + * mechanism to the <code>Buffer</code> class into which this abstract + * class is mixed in. Class <code>ObservableBuffer</code> publishes + * events of the type <code>Message</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class ObservableBuffer[A, This <: ObservableBuffer[A, This]]: This + extends Buffer[A] + with Publisher[Message[Pair[Location, A]] with Undoable, This] { + + abstract override def +(element: A): Buffer[A] = { + super.+(element); + publish(new Include(Pair(End, element)) with Undoable { + def undo: Unit = trimEnd(1); + }); + this + } + + abstract override def +:(element: A): Buffer[A] = { + super.+:(element); + publish(new Include(Pair(Start, element)) with Undoable { + def undo: Unit = trimStart(1); + }); + this + } + + abstract override def insertAll(n: Int, iter: Iterable[A]): Unit = { + super.insertAll(n, iter); + var i = n; + val it = iter.elements; + while (it.hasNext) { + publish(new Include(Pair(Index(i), it.next)) with Undoable { + def undo: Unit = remove(i); + }); + i = i + 1; + } + } + + abstract override def update(n: Int, newelement: A): Unit = { + val oldelement = apply(n); + super.update(n, newelement); + publish(new Update(Pair(Index(n), newelement)) with Undoable { + def undo: Unit = update(n, oldelement); + }); + } + + abstract override def remove(n: Int): A = { + val oldelement = apply(n); + super.remove(n); + publish(new Remove(Pair(Index(n), oldelement)) with Undoable { + def undo: Unit = insert(n, oldelement); + }); + oldelement + } + + abstract override def clear: Unit = { + super.clear; + publish(new Reset with Undoable { def undo: Unit = error("cannot undo"); }); + } +} diff --git a/src/library/scala/collection/mutable/ObservableMap.scala b/src/library/scala/collection/mutable/ObservableMap.scala new file mode 100644 index 0000000000..207a294753 --- /dev/null +++ b/src/library/scala/collection/mutable/ObservableMap.scala @@ -0,0 +1,48 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class is typically used as a mixin. It adds a subscription + * mechanism to the <code>Map</code> class into which this abstract + * class is mixed in. Class <code>ObservableMap</code> publishes + * events of the type <code>Message</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class ObservableMap[A, B, This <: ObservableMap[A, B, This]]: This + extends scala.collection.mutable.Map[A, B] + with Publisher[Message[Pair[A, B]] with Undoable, This] { + + abstract override def update(key: A, value: B): Unit = get(key) match { + case None => super.update(key, value); + publish(new Include(Pair(key, value)) with Undoable { + def undo = -=(key); + }); + case Some(old) => super.update(key, value); + publish(new Update(Pair(key, value)) with Undoable { + def undo = update(key, old); + }); + } + + abstract override def -=(key: A): Unit = get(key) match { + case None => + case Some(old) => super.-=(key); + publish(new Remove(Pair(key, old)) with Undoable { + def undo = update(key, old); + }); + } + + abstract override def clear: Unit = { + super.clear; + publish(new Reset with Undoable { def undo: Unit = error("cannot undo"); }); + } +} diff --git a/src/library/scala/collection/mutable/ObservableSet.scala b/src/library/scala/collection/mutable/ObservableSet.scala new file mode 100644 index 0000000000..656545d78e --- /dev/null +++ b/src/library/scala/collection/mutable/ObservableSet.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class is typically used as a mixin. It adds a subscription + * mechanism to the <code>Set</code> class into which this abstract + * class is mixed in. Class <code>ObservableSet</code> publishes + * events of the type <code>Message</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class ObservableSet[A, This <: ObservableSet[A, This]]: This + extends scala.collection.mutable.Set[A] + with Publisher[Message[A] with Undoable, This] { + + abstract override def +=(elem: A): Unit = if (!contains(elem)) { + super.+=(elem); + publish(new Include(elem) with Undoable { def undo = -=(elem); }); + } + + abstract override def -=(elem: A): Unit = if (contains(elem)) { + super.-=(elem); + publish(new Remove(elem) with Undoable { def undo = +=(elem); }); + } + + abstract override def clear: Unit = { + super.clear; + publish(new Reset with Undoable { def undo: Unit = error("cannot undo"); }); + } +} diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala new file mode 100644 index 0000000000..83552417cc --- /dev/null +++ b/src/library/scala/collection/mutable/PriorityQueue.scala @@ -0,0 +1,180 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class implements priority queues using a heap. The + * elements of the queue have to be ordered in terms of the + * <code>Ordered[T]</code> trait. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + */ +//[serializable, cloneable] +class PriorityQueue[A <% Ordered[A]] extends ResizableArray[A] with java.io.Serializable { + size = size + 1; // we do not use array(0) + + protected def fixUp(as: Array[A], m: Int): Unit = { + var k: Int = m; + while ((k > 1) && (as(k / 2) < as(k))) { + swap(k, k / 2); + k = k / 2; + } + } + + protected def fixDown(as: Array[A], m: Int, n: Int): Unit = { + var k: Int = m; + var loop: Boolean = true; + while (loop && (n >= 2 * k)) { + var j = 2 * k; + if ((j < n) && (as(j) < as(j + 1))) + j = j + 1; + if (!(as(k) < as(j))) + loop = false; + else { + val h = as(k); + as(k) = as(j); + as(j) = h; + k = j; + } + } + } + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + def isEmpty: Boolean = size < 2; + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert + */ + def +=(elem: A): Unit = { + ensureSize(size+1); + array(size) = elem; + fixUp(array, size); + size = size + 1; + } + + /** Adds all elements provided by an <code>Iterable</code> object + * into the priority queue. + * + * @param iter an iterable object + */ + def ++=(iter: Iterable[A]): Unit = this ++= iter.elements; + + /** Adds all elements provided by an iterator into the priority queue. + * + * @param it an iterator + */ + def ++=(it: Iterator[A]): Unit = it foreach { e => this += e }; + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + def enqueue(elems: A*): Unit = (this ++= elems); + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + */ + def dequeue: A = { + if (size > 1) { + size = size - 1; + swap(1, size); + fixDown(array, 1, size - 1); + array(size) + } else + error("no element to remove from heap"); + } + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + def max: A = if (size > 1) array(1) else error("queue is empty"); + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + def clear: Unit = { + size = 1; + } + + /** Returns an iterator which yiels all the elements of the priority + * queue in descending priority order. + * + * @return an iterator over all elements sorted in descending order. + */ + override def elements: Iterator[A] = new Iterator[A] { + val as: Array[A] = new Array[A](size); + java.lang.System.arraycopy(array, 0, as, 0, size); + var i = size - 1; + def hasNext: Boolean = i > 0; + def next: A = { + val res = as(1); + as(1) = as(i); + i = i - 1; + fixDown(as, 1, i); + res + } + } + + /** Checks if two queues are structurally identical. + * + * @return true, iff both queues contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = ( + that.isInstanceOf[PriorityQueue[A]] && + { val other = that.asInstanceOf[PriorityQueue[A]]; + elements.zip(other.elements).forall { + case Pair(thiselem, thatelem) => thiselem == thatelem; + }} + ); + + /** The hashCode method always yields an error, since it is not + * safe to use mutable queues as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); + + /** Returns a regular queue containing the same elements. + */ + def toQueue: Queue[A] = { + val res = new Queue[A]; + res ++= this; + res + } + + /** Returns a list of all elements. + */ + def toList: List[A] = elements.toList; + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString() = toList.mkString("PriorityQueue(", ", ", ")"); + + /** This method clones the priority queue. + * + * @return a priority queue with the same elements. + */ + override def clone(): PriorityQueue[A] = { + val res = new PriorityQueue[A]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/PriorityQueueProxy.scala b/src/library/scala/collection/mutable/PriorityQueueProxy.scala new file mode 100644 index 0000000000..e78b98065f --- /dev/null +++ b/src/library/scala/collection/mutable/PriorityQueueProxy.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class implements priority queues using a heap. The + * elements of the queue have to be ordered in terms of the + * <code>Ordered[T]</code> trait. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + */ +abstract class PriorityQueueProxy[A <% Ordered[A]] extends PriorityQueue[A] with IterableProxy[A] { + + def self: PriorityQueue[A]; + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + override def elements: Iterator[A] = self.elements; + + /** Returns the length of this priority queue. + */ + override def length: Int = self.length; + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = self.isEmpty; + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): Unit = self += elem; + + /** Adds all elements provided by an <code>Iterable</code> object + * into the priority queue. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = self ++= iter; + + /** Adds all elements provided by an iterator into the priority queue. + * + * @param it an iterator + */ + override def ++=(it: Iterator[A]): Unit = self ++= it; + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = self ++= elems; + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + */ + override def dequeue: A = self.dequeue; + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + override def max: A = self.max; + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear: Unit = self.clear; + + /** Returns a regular queue containing the same elements. + */ + override def toQueue: Queue[A] = self.toQueue; + + /** This method clones the priority queue. + * + * @return a priority queue with the same elements. + */ + override def clone(): PriorityQueue[A] = new PriorityQueueProxy[A] { def self = PriorityQueueProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/Publisher.scala b/src/library/scala/collection/mutable/Publisher.scala new file mode 100644 index 0000000000..ee442eccd2 --- /dev/null +++ b/src/library/scala/collection/mutable/Publisher.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** <code>Publisher[A,This]</code> objects publish events of type <code>A</code> + * to all registered subscribers. When subscribing, a subscriber may specify + * a filter which can be used to constrain the number of events sent to the + * subscriber. Subscribers may suspend their subscription, or reactivate a + * suspended subscription. Class <code>Publisher</code> is typically used + * as a mixin. The type variable <code>This</code> models self types. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[_trait_] abstract class Publisher[A, This <: Publisher[A, This]]: This { + private val filters = new HashMap[Subscriber[A, This], + scala.collection.mutable.Set[A => Boolean]] + with MultiMap[Subscriber[A, This], A => Boolean]; + private val suspended = new HashSet[Subscriber[A, This]]; + + def subscribe(sub: Subscriber[A, This]): Unit = subscribe(sub, (event => true)); + + def subscribe(sub: Subscriber[A, This], filter: A => Boolean): Unit = + filters.add(sub, filter); + + def suspendSubscription(sub: Subscriber[A, This]): Unit = suspended += sub; + + def activateSubscription(sub: Subscriber[A, This]): Unit = suspended -= sub; + + def removeSubscription(sub: Subscriber[A, This]): Unit = filters -= sub; + + def removeSubscriptions: Unit = filters.clear; + + protected def publish(event: A): Unit = + filters.keys.foreach(sub => + if (filters.entryExists(sub, (p => p(event)))) sub.notify(this, event)); +} diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala new file mode 100644 index 0000000000..0d297f5e0d --- /dev/null +++ b/src/library/scala/collection/mutable/Queue.scala @@ -0,0 +1,194 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** <code>Queue</code> objects implement data structures that allow to + * insert and retrieve elements in a first-in-first-out (FIFO) manner. + * + * @author Matthias Zenger + * @version 1.1, 03/05/2004 + */ +[serializable, cloneable] +class Queue[A] extends MutableList[A] { + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + def isEmpty: Boolean = (first == null); + + /** Inserts a single element at the end of the queue. + * + * @param elem the element to insert + */ + def +=(elem: A): Unit = appendElem(elem); + + /** Adds all elements provided by an <code>Iterable</code> object + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + def ++=(iter: Iterable[A]): Unit = this ++= iter.elements; + + /** Adds all elements provided by an iterator + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param it an iterator + */ + def ++=(it: Iterator[A]): Unit = it foreach appendElem; + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + def enqueue(elems: A*): Unit = (this ++= elems); + + /** Returns the first element in the queue, and removes this element + * from the queue. + * + * @return the first element of the queue. + */ + def dequeue: A = { + if (first == null) + error("queue empty"); + else { + val res = first.elem; + first = first.next; + if (first == null) + last = null; + len = len - 1; + res; + } + } + + /** Returns the first element in the queue which satisfies the + * given predicate, and removes this element from the queue. + * + * @param p the predicate used for choosing the first element + * @return the first element of the queue for which p yields true + */ + def dequeueFirst(p: A => Boolean): Option[A] = { + if (first == null) + None + else if (p(first.elem)) { + val res: Option[A] = Some(first.elem); + first = first.next; + len = len - 1; + if (first == null) { + last = null; + } else if (first.next == null) { + last = first; + } + res + } else + extractFirst(first, p) match { + case None => None + case Some(cell) => Some(cell.elem) + } + } + + /** Returns all elements in the queue which satisfy the + * given predicate, and removes those elements from the queue. + * + * @param p the predicate used for choosing elements + * @return a sequence of all elements in the queue for which + * p yields true. + */ + def dequeueAll(p: A => Boolean): Seq[A] = { + val res = new ArrayBuffer[A]; + if (first == null) + res; + else { + while (p(first.elem)) { + res += first.elem; + first = first.next; + len = len - 1; + if (first == null) { + last = null; + } else if (first.next == null) { + last = first; + } + } + var cell: Option[LinkedList[A]] = extractFirst(first, p); + while (!cell.isEmpty) { + res += cell.get.elem; + cell = extractFirst(cell.get, p); + } + res + } + } + + private def extractFirst(start: LinkedList[A], p: A => Boolean): Option[LinkedList[A]] = { + var cell = start; + while ((cell.next != null) && !p(cell.next.elem)) { + cell = cell.next; + } + if (cell.next == null) + None + else { + val res: Option[LinkedList[A]] = Some(cell.next); + cell.next = cell.next.next; + if (cell.next == null) + last = cell; + len = len - 1; + res + } + } + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @return the first element. + */ + def front: A = first.elem; + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + def clear: Unit = reset; + + /** Checks if two queues are structurally identical. + * + * @return true, iff both queues contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = ( + that.isInstanceOf[Queue[A]] && + { val other = that.asInstanceOf[Queue[A]]; + elements.zip(other.elements).forall { + case Pair(thiselem, thatelem) => thiselem == thatelem; + }} + ); + + /** The hashCode method always yields an error, since it is not + * safe to use mutable queues as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString() = toList.mkString("Queue(", ", ", ")"); + + /** This method clones the queue. + * + * @return a queue with the same elements. + */ + override def clone(): Queue[A] = { + val res = new Queue[A]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/QueueProxy.scala b/src/library/scala/collection/mutable/QueueProxy.scala new file mode 100644 index 0000000000..65c426a9c2 --- /dev/null +++ b/src/library/scala/collection/mutable/QueueProxy.scala @@ -0,0 +1,97 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** <code>Queue</code> objects implement data structures that allow to + * insert and retrieve elements in a first-in-first-out (FIFO) manner. + * + * @author Matthias Zenger + * @version 1.1, 03/05/2004 + */ +trait QueueProxy[A] extends Queue[A] with SeqProxy[A] { + + def self: Queue[A]; + + /** Access element number <code>n</code>. + * + * @return the element at index <code>n</code>. + */ + override def apply(n: Int): A = self.apply(n); + + /** Returns the length of this queue. + */ + override def length: Int = self.length; + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = self.isEmpty; + + /** Inserts a single element at the end of the queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): Unit = self += elem; + + /** Adds all elements provided by an <code>Iterable</code> object + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = self ++= iter; + + /** Adds all elements provided by an iterator + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param iter an iterator + */ + override def ++=(it: Iterator[A]): Unit = self ++= it; + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = self ++= elems; + + /** Returns the first element in the queue, and removes this element + * from the queue. + * + * @return the first element of the queue. + */ + override def dequeue: A = self.dequeue; + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @return the first element. + */ + override def front: A = self.front; + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear: Unit = self.clear; + + /** Returns an iterator over all elements on the queue. + * + * @return an iterator over all queue elements. + */ + override def elements: Iterator[A] = self.elements; + + /** This method clones the queue. + * + * @return a queue with the same elements. + */ + override def clone(): Queue[A] = new QueueProxy[A] { def self = QueueProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/ResizableArray.scala b/src/library/scala/collection/mutable/ResizableArray.scala new file mode 100644 index 0000000000..5f1c5f053c --- /dev/null +++ b/src/library/scala/collection/mutable/ResizableArray.scala @@ -0,0 +1,64 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class is used internally to implement data structures that + * are based on resizable arrays. + * + * @author Matthias Zenger, Burak Emir + * @version 1.0, 03/05/2004 + */ +[serializable] +[_trait_] abstract class ResizableArray[A] extends AnyRef with Iterable[A] { + import scala.runtime.compat.Platform.arraycopy; + + protected val initialSize: Int = 16; + protected var array: Array[A] = new Array[A](initialSize); + protected var size: Int = 0; + + /** ensure that the internal array has at n cells */ + protected def ensureSize(n: Int): Unit = { + if (n > array.length) { + var newsize = array.length * 2; + while( n > newsize ) + newsize = newsize * 2; + val newar: Array[A] = new Array(newsize); + arraycopy(array, 0, newar, 0, size); + array = newar; + } + } + + /** Swap two elements of this array. + */ + protected def swap(a: Int, b: Int): Unit = { + val h = array(a); + array(a) = array(b); + array(b) = h; + } + + /** Move parts of the array. + */ + protected def copy(m: Int, n: Int, len: Int) = { + arraycopy(array, m, array, n, len); + } + + /** Returns the length of this resizable array. + */ + def length: Int = size; + + /** Returns a new iterator over all elements of this resizable array. + */ + def elements: Iterator[A] = new Iterator[A] { + var i = 0; + def hasNext: Boolean = i < size; + def next: A = { i = i + 1; array(i - 1) } + } +} diff --git a/src/library/scala/collection/mutable/RevertableHistory.scala b/src/library/scala/collection/mutable/RevertableHistory.scala new file mode 100644 index 0000000000..c9bb1ef6d1 --- /dev/null +++ b/src/library/scala/collection/mutable/RevertableHistory.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** A revertable history is a <code>History</code> object which supports + * an undo operation. Type variable <code>A</code> refers to the type + * of the published events, <code>B</code> denotes the publisher type. + * Type <code>B</code> is typically a subtype of <code>Publisher</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +class RevertableHistory[A <: Undoable, B] extends History[A, B] with Undoable { + + /** Rollback the full history. + */ + def undo: Unit = { + val old = log.toList.reverse; + clear; + old.foreach { case Pair(sub, event) => event.undo; } + } +} diff --git a/src/library/scala/collection/mutable/Scriptable.scala b/src/library/scala/collection/mutable/Scriptable.scala new file mode 100644 index 0000000000..448109bde3 --- /dev/null +++ b/src/library/scala/collection/mutable/Scriptable.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** Classes that implement the <code>Scriptable</code> trait allow + * messages to be sent to objects of that class. + * + * @author Matthias Zenger + * @version 1.0, 09/05/2004 + */ +trait Scriptable[A] { + + /** Send a message to this scriptable object. + */ + def <<(cmd: A): Unit; +} diff --git a/src/library/scala/collection/mutable/Set.scala b/src/library/scala/collection/mutable/Set.scala new file mode 100644 index 0000000000..d493ee1560 --- /dev/null +++ b/src/library/scala/collection/mutable/Set.scala @@ -0,0 +1,111 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This trait represents mutable sets. Concrete set implementations + * just have to provide functionality for the abstract methods in + * <code>scala.collection.Set</code> as well as for <code>add</code>, + * <code>remove</code>, and <code>clear</code>. + * + * @author Matthias Zenger + * @version 1.1, 09/05/2004 + */ +[cloneable] +trait Set[A] extends AnyRef with scala.collection.Set[A] with Scriptable[Message[A]] { + + /** This method allows one to add or remove an element <code>elem</code> + * from this set depending on the value of parameter <code>included</code>. + * Typically, one would use the following syntax: + * <pre>set(elem) = true</pre> + */ + def update(elem: A, included: Boolean): Unit = + if (included) +=(elem) else -=(elem); + + /** This method adds a new element to the set. + */ + def +=(elem: A): Unit; + + /** This method will add all the elements provided by an iterator + * of the iterable object <code>that</code> to the set. + */ + def ++=(that: Iterable[A]): Unit = ++=(that.elements); + + /** This method will add all the elements provided by an iterator + * of the iterable object <code>that</code> to the set. + */ + def ++=(it: Iterator[A]): Unit = it foreach +=; + + /** <code>incl</code> can be used to add many elements to the set + * at the same time. + */ + def incl(elems: A*): Unit = ++=(elems.elements); + + /** <code>-=</code> can be used to remove a single element from + * a set. + */ + def -=(elem: A): Unit; + + /** This method removes all the elements provided by the + * the iterable object <code>that</code> from the set. + */ + def --=(that: Iterable[A]): Unit = --=(that.elements); + + /** This method removes all the elements provided by an iterator + * <code>it</code> from the set. + */ + def --=(it: Iterator[A]): Unit = it foreach -=; + + /** <code>excl</code> removes many elements from the set. + */ + def excl(elems: A*): Unit = --=(elems.elements); + + /** This method computes an intersection with set <code>that</code>. + * It removes all the elements that are not present in <code>that</code>. + */ + def intersect(that: Set[A]): Unit = filter(that.contains); + + /** Method <code>filter</code> removes all elements from the set for + * which the predicate <code>p</code> yields the value <code>false</code>. + */ + def filter(p: A => Boolean): Unit = toList.foreach { + elem => if (!p(elem)) -=(elem); + } + + /** Removes all elements from the set. After this operation is completed, + * the set will be empty. + */ + def clear: Unit; + + /** Send a message to this scriptable object. + * + * @param cmd the message to send. + */ + def <<(cmd: Message[A]): Unit = cmd match { + case Include(elem) => this += elem; + case Remove(elem) => this -= elem; + case Reset() => clear; + case s: Script[A] => s.elements foreach <<; + case _ => error("message " + cmd + " not understood"); + } + + /** Return a clone of this set. + * + * @return a set with the same elements. + */ + override def clone(): Set[A] = super.clone().asInstanceOf[Set[A]]; + + /** The hashCode method always yields an error, since it is not + * safe to use mutable stacks as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); +} diff --git a/src/library/scala/collection/mutable/SetProxy.scala b/src/library/scala/collection/mutable/SetProxy.scala new file mode 100644 index 0000000000..4cd759a60d --- /dev/null +++ b/src/library/scala/collection/mutable/SetProxy.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This is a simple wrapper class for <code>scala.collection.mutable.Set</code>. + * It is most useful for assembling customized set abstractions + * dynamically using object composition and forwarding. + * + * @author Matthias Zenger + * @version 1.1, 09/05/2004 + */ +trait SetProxy[A] extends Set[A] with scala.collection.SetProxy[A] { + + def self: Set[A]; + + override def update(elem: A, included: Boolean): Unit = self(elem) = included; + + def +=(elem: A): Unit = self += elem; + + override def ++=(that: Iterable[A]): Unit = self ++= that; + + override def ++=(it: Iterator[A]): Unit = self ++= it; + + override def incl(elems: A*): Unit = self ++= elems; + + def -=(elem: A): Unit = self -= elem; + + override def --=(that: Iterable[A]): Unit = self --= that; + + override def --=(it: Iterator[A]): Unit = self --= it; + + override def excl(elems: A*): Unit = self --= elems; + + override def intersect(that: Set[A]): Unit = self.intersect(that); + + def clear: Unit = self.clear; + + override def filter(p: A => Boolean): Unit = self.filter(p); + + override def <<(cmd: Message[A]): Unit = self << cmd; + + override def clone(): Set[A] = new SetProxy[A] { def self = SetProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/SingleLinkedList.scala b/src/library/scala/collection/mutable/SingleLinkedList.scala new file mode 100644 index 0000000000..b60d32ba1b --- /dev/null +++ b/src/library/scala/collection/mutable/SingleLinkedList.scala @@ -0,0 +1,63 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This extensible class may be used as a basis for implementing linked + * list. Type variable <code>A</code> refers to the element type of the + * list, type variable <code>This</code> is used to model self types of + * linked lists. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +[serializable] +abstract class SingleLinkedList[A, This <: SingleLinkedList[A, This]]: This extends AnyRef with Seq[A] { + + var elem: A = _; + + var next: This = _; + + def length: Int = 1 + (if (next == null) 0 else next.length); + + def append(that: This): Unit = + if (next == null) { next = that; } else next.append(that); + + def insert(that: This): Unit = if (that != null) { + that.append(next); + next = that; + } + + def apply(n: Int): A = { + if (n == 0) elem + else if (next == null) error("unknown element") + else next.apply(n - 1); + } + + def get(n: Int): Option[A] = { + if (n == 0) Some(elem) + else if (next == null) None + else next.get(n - 1); + } + + def elements: Iterator[A] = new Iterator[A] { + var elems = SingleLinkedList.this; + def hasNext = (elems != null); + def next = { + val res = elems.elem; + elems = elems.next; + res; + } + } + + override def toList: List[A] = + if (next == null) (elem :: Nil) else (elem :: next.toList); + +} diff --git a/src/library/scala/collection/mutable/Stack.scala b/src/library/scala/collection/mutable/Stack.scala new file mode 100644 index 0000000000..0cf269dba7 --- /dev/null +++ b/src/library/scala/collection/mutable/Stack.scala @@ -0,0 +1,132 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** A stack implements a data structure which allows to store and retrieve + * objects in a last-in-first-out (LIFO) fashion. + * + * @author Matthias Zenger + * @version 1.1, 03/05/2004 + */ +[serializable, cloneable] +class Stack[A] extends MutableList[A] { + + /** Checks if the stack is empty. + * + * @return true, iff there is no element on the stack + */ + def isEmpty: Boolean = (first == null); + + /** Pushes a single element on top of the stack. + * + * @param elem the element to push onto the stack + */ + def +=(elem: A): Unit = prependElem(elem); + + /** Pushes all elements provided by an <code>Iterable</code> object + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + def ++=(iter: Iterable[A]): Unit = this ++= iter.elements; + + /** Pushes all elements provided by an iterator + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterator + */ + def ++=(it: Iterator[A]): Unit = it foreach { e => prependElem(e) }; + + /** Pushes a sequence of elements on top of the stack. The first element + * is pushed first, etc. + * + * @param elems a sequence of elements + */ + def push(elems: A*): Unit = (this ++= elems); + + /** Returns the top element of the stack. This method will not remove + * the element from the stack. An error is signaled if there is no + * element on the stack. + * + * @return the top element + */ + def top: A = if (first == null) error("stack empty"); else first.elem; + + /** Removes the top element from the stack. + */ + def pop: A = + if (first != null) { + val res = first.elem; + first = first.next; + len = len - 1; + res + } else + error("stack empty"); + + /** + * Removes all elements from the stack. After this operation completed, + * the stack will be empty. + */ + def clear: Unit = reset; + + /** Returns an iterator over all elements on the stack. This iterator + * is stable with respect to state changes in the stack object; i.e. + * such changes will not be reflected in the iterator. The iterator + * issues elements in the order they were inserted into the stack + * (FIFO order). + * + * @return an iterator over all stack elements. + */ + override def elements: Iterator[A] = toList.elements; + + /** Creates a list of all stack elements in FIFO order. + * + * @return the created list. + */ + override def toList: List[A] = super[MutableList].toList.reverse; + + /** Checks if two stacks are structurally identical. + * + * @return true, iff both stacks contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = ( + that.isInstanceOf[Stack[A]] && + { val other = that.asInstanceOf[Stack[A]]; + elements.zip(other.elements).forall { + case Pair(thiselem, thatelem) => thiselem == thatelem; + }} + ); + + /** The hashCode method always yields an error, since it is not + * safe to use mutable stacks as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = error("unsuitable as hash key"); + + /** Returns a textual representation of a stack as a string. + * + * @return the string representation of this stack. + */ + override def toString(): String = toList.mkString("Stack(", ", ", ")"); + + /** This method clones the stack. + * + * @return a stack with the same elements. + */ + override def clone(): Stack[A] = { + val res = new Stack[A]; + res ++= this; + res + } +} diff --git a/src/library/scala/collection/mutable/StackProxy.scala b/src/library/scala/collection/mutable/StackProxy.scala new file mode 100644 index 0000000000..cfca6ca56c --- /dev/null +++ b/src/library/scala/collection/mutable/StackProxy.scala @@ -0,0 +1,108 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** A stack implements a data structure which allows to store and retrieve + * objects in a last-in-first-out (LIFO) fashion. + * + * @author Matthias Zenger + * @version 1.0, 10/05/2004 + */ +trait StackProxy[A] extends Stack[A] with SeqProxy[A] { + + def self: Stack[A]; + + /** Access element number <code>n</code>. + * + * @return the element at index <code>n</code>. + */ + override def apply(n: Int): A = self.apply(n); + + /** Returns the length of this stack. + */ + override def length: Int = self.length; + + /** Checks if the stack is empty. + * + * @return true, iff there is no element on the stack + */ + override def isEmpty: Boolean = self.isEmpty; + + /** Pushes a single element on top of the stack. + * + * @param elem the element to push onto the stack + */ + override def +=(elem: A): Unit = self += elem; + + /** Pushes all elements provided by an <code>Iterable</code> object + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = self ++= iter; + + + /** Pushes all elements provided by an iterator + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterator + */ + override def ++=(it: Iterator[A]): Unit = self ++= it; + + /** Pushes a sequence of elements on top of the stack. The first element + * is pushed first, etc. + * + * @param elems a sequence of elements + */ + override def push(elems: A*): Unit = self ++= elems; + + /** Returns the top element of the stack. This method will not remove + * the element from the stack. An error is signaled if there is no + * element on the stack. + * + * @return the top element + */ + override def top: A = self.top; + + /** Removes the top element from the stack. + */ + override def pop: A = self.pop; + + /** + * Removes all elements from the stack. After this operation completed, + * the stack will be empty. + */ + override def clear: Unit = self.clear; + + /** Returns an iterator over all elements on the stack. This iterator + * is stable with respect to state changes in the stack object; i.e. + * such changes will not be reflected in the iterator. The iterator + * issues elements in the order they were inserted into the stack + * (FIFO order). + * + * @return an iterator over all stack elements. + */ + override def elements: Iterator[A] = self.elements; + + /** Creates a list of all stack elements in FIFO order. + * + * @return the created list. + */ + override def toList: List[A] = self.toList; + + /** This method clones the stack. + * + * @return a stack with the same elements. + */ + override def clone(): Stack[A] = new StackProxy[A] { def self = StackProxy.this.self.clone() } +} diff --git a/src/library/scala/collection/mutable/Subscriber.scala b/src/library/scala/collection/mutable/Subscriber.scala new file mode 100644 index 0000000000..30ca2d03e2 --- /dev/null +++ b/src/library/scala/collection/mutable/Subscriber.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** <code>Subscriber[A, B]</code> objects may subscribe to events of + * type <code>A</code> published by an object of type <code>B</code>. + * <code>B</code> is typically a subtype of <code>Publisher</code>. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait Subscriber[-A, -B] { + def notify(pub: B, event: A): Unit; +} diff --git a/src/library/scala/collection/mutable/SynchronizedBuffer.scala b/src/library/scala/collection/mutable/SynchronizedBuffer.scala new file mode 100644 index 0000000000..e15c59b4b4 --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedBuffer.scala @@ -0,0 +1,189 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This trait should be used as a mixin. It synchronizes the <code>Buffer</code> + * methods of the class into which it is mixed in. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait SynchronizedBuffer[A] extends Buffer[A] { + + abstract override def length: Int = synchronized { + super.length; + } + + abstract override def elements: Iterator[A] = synchronized { + super.elements; + } + + abstract override def apply(n: Int): A = synchronized { + super.apply(n); + } + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + abstract override def +(elem: A): Buffer[A] = synchronized { + super.+(elem); + } + + /** Append a single element to this buffer. + * + * @param elem the element to append. + */ + override def +=(elem: A): Unit = synchronized { + super.+=(elem); + } + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++(iter: Iterable[A]): Buffer[A] = synchronized { + super.++(iter); + } + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + override def ++=(iter: Iterable[A]): Unit = synchronized { + super.++=(iter); + } + + /** Appends a sequence of elements to this buffer. + * + * @param elems the elements to append. + */ + override def append(elems: A*): Unit = synchronized { + super.++=(elems); + } + + /** Appends a number of elements provided by an iterable object + * via its <code>elements</code> method. + * + * @param iter the iterable object. + */ + override def appendAll(iter: Iterable[A]): Unit = synchronized { + super.appendAll(iter); + } + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + abstract override def +:(elem: A): Buffer[A] = synchronized { + super.+:(elem); + } + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def ++:(iter: Iterable[A]): Buffer[A] = synchronized { + super.++:(iter); + } + + /** Prepend an element to this list. + * + * @param elem the element to prepend. + */ + override def prepend(elems: A*): Unit = synchronized { + super.prependAll(elems); + } + + /** Prepends a number of elements provided by an iterable object + * via its <code>elements</code> method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + override def prependAll(elems: Iterable[A]): Unit = synchronized { + super.prependAll(elems); + } + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert the new elements at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param elems the new elements to insert. + */ + override def insert(n: Int, elems: A*): Unit = synchronized { + super.insertAll(n, elems); + } + + /** Inserts new elements at the index <code>n</code>. Opposed to method + * <code>update</code>, this method will not replace an element with a + * one. Instead, it will insert a new element at index <code>n</code>. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + */ + abstract override def insertAll(n: Int, iter: Iterable[A]): Unit = synchronized { + super.insertAll(n, iter); + } + + /** Replace element at index <code>n</code> with the new element + * <code>newelem</code>. + * + * @param n the index of the element to replace. + * @param newelem the new element. + */ + abstract override def update(n: Int, newelem: A): Unit = synchronized { + super.update(n, newelem); + } + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + abstract override def remove(n: Int): A = synchronized { + super.remove(n); + } + + /** Clears the buffer contents. + */ + abstract override def clear: Unit = synchronized { + super.clear; + } + + override def <<(cmd: Message[Pair[Location, A]]): Unit = synchronized { + super.<<(cmd); + } + + /** Return a clone of this buffer. + * + * @return an <code>ArrayBuffer</code> with the same elements. + */ + override def clone(): Buffer[A] = synchronized { + super.clone(); + } + + /** The hashCode method always yields an error, since it is not + * safe to use buffers as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = synchronized { + super.hashCode(); + } +} diff --git a/src/library/scala/collection/mutable/SynchronizedMap.scala b/src/library/scala/collection/mutable/SynchronizedMap.scala new file mode 100644 index 0000000000..b0db7078de --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedMap.scala @@ -0,0 +1,116 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This trait should be used as a mixin. It synchronizes the <code>Map</code> + * functions of the class into which it is mixed in. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait SynchronizedMap[A, B] extends scala.collection.mutable.Map[A, B] { + + abstract override def size: Int = synchronized { + super.size; + } + + abstract override def get(key: A): Option[B] = synchronized { + super.get(key); + } + + override def isEmpty: Boolean = synchronized { + super.isEmpty; + } + + override def apply(key: A): B = synchronized { + super.apply(key); + } + + override def contains(key: A): Boolean = synchronized { + super.contains(key); + } + + override def isDefinedAt(key: A) = synchronized { + super.isDefinedAt(key); + } + + override def keys: Iterator[A] = synchronized { + super.keys; + } + + override def values: Iterator[B] = synchronized { + super.values; + } + + override def foreach(f: (A, B) => Unit) = synchronized { + super.foreach(f); + } + + override def toList: List[Pair[A, B]] = synchronized { + super.toList; + } + + abstract override def update(key: A, value: B): Unit = synchronized { + super.update(key, value); + } + + override def ++=(map: Iterable[Pair[A, B]]): Unit = synchronized { + super.++=(map); + } + + override def ++=(it: Iterator[Pair[A, B]]): Unit = synchronized { + super.++=(it); + } + + override def incl(mappings: Pair[A, B]*): Unit = synchronized { + super.++=(mappings); + } + + abstract override def -=(key: A): Unit = synchronized { + super.-=(key); + } + + override def --=(keys: Iterable[A]): Unit = synchronized { + super.--=(keys); + } + + override def --=(it: Iterator[A]): Unit = synchronized { + super.--=(it); + } + + override def excl(keys: A*): Unit = synchronized { + super.--=(keys); + } + + override def clear: Unit = synchronized { + super.clear; + } + + override def map(f: (A, B) => B): Unit = synchronized { + super.map(f); + } + + override def filter(p: (A, B) => Boolean): Unit = synchronized { + super.filter(p); + } + + override def toString() = synchronized { + super.toString(); + } + + override def <<(cmd: Message[Pair[A, B]]): Unit = synchronized { + super.<<(cmd); + } + + override def clone(): Map[A, B] = synchronized { + super.clone(); + } +} diff --git a/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala b/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala new file mode 100644 index 0000000000..8b3078e74b --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala @@ -0,0 +1,97 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This class implements synchronized priority queues using a heap. + * The elements of the queue have to be ordered in terms of the + * <code>Ordered[T]</code> trait. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + */ +class SynchronizedPriorityQueue[A <% Ordered[A]] extends PriorityQueue[A] { + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = synchronized { super.isEmpty; } + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): Unit = synchronized { super.+=(elem); } + + /** Adds all elements provided by an <code>Iterable</code> object + * into the priority queue. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = synchronized { super.++=(iter); } + + /** Adds all elements provided by an iterator into the priority queue. + * + * @param it an iterator + */ + override def ++=(it: Iterator[A]): Unit = synchronized { super.++=(it); } + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = synchronized { super.++=(elems); } + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + */ + override def dequeue: A = synchronized { super.dequeue; } + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + override def max: A = synchronized { super.max; } + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear: Unit = synchronized { super.clear; } + + /** Returns an iterator which yiels all the elements of the priority + * queue in descending priority order. + * + * @return an iterator over all elements sorted in descending order. + */ + override def elements: Iterator[A] = synchronized { super.elements; } + + /** Checks if two queues are structurally identical. + * + * @return true, iff both queues contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = synchronized { super.equals(that); } + + /** The hashCode method always yields an error, since it is not + * safe to use mutable queues as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = synchronized { super.hashCode(); } + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString(): String = synchronized { super.toString(); } +} diff --git a/src/library/scala/collection/mutable/SynchronizedQueue.scala b/src/library/scala/collection/mutable/SynchronizedQueue.scala new file mode 100644 index 0000000000..74fb9782b5 --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedQueue.scala @@ -0,0 +1,93 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This is a synchronized version of the <code>Queue[T]</code> class. It + * implements a data structure that allows one to insert and retrieve + * elements in a first-in-first-out (FIFO) manner. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + */ +class SynchronizedQueue[A] extends Queue[A] { + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = synchronized { super.isEmpty; } + + /** Inserts a single element at the end of the queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): Unit = synchronized { super.+=(elem); } + + /** Adds all elements provided by an <code>Iterable</code> object + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = synchronized { super.++=(iter); } + + /** Adds all elements provided by an iterator + * at the end of the queue. The elements are prepended in the order they + * are given out by the iterator. + * + * @param it an iterator + */ + override def ++=(it: Iterator[A]): Unit = synchronized { super.++=(it); } + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = synchronized { super.++=(elems); } + + /** Returns the first element in the queue, and removes this element + * from the queue. + * + * @return the first element of the queue. + */ + override def dequeue: A = synchronized { super.dequeue; } + + /** Returns the first element in the queue, or throws an error if there + * is no element contained in the queue. + * + * @return the first element. + */ + override def front: A = synchronized { super.front; } + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear: Unit = synchronized { super.clear; } + + /** Checks if two queues are structurally identical. + * + * @return true, iff both queues contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = synchronized { super.equals(that); } + + /** The hashCode method always yields an error, since it is not + * safe to use mutable queues as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = synchronized { super.hashCode(); } + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString() = synchronized { super.toString(); } +} diff --git a/src/library/scala/collection/mutable/SynchronizedSet.scala b/src/library/scala/collection/mutable/SynchronizedSet.scala new file mode 100644 index 0000000000..743a5a85c4 --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedSet.scala @@ -0,0 +1,104 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This trait should be used as a mixin. It synchronizes the <code>Set</code> + * functions of the class into which it is mixed in. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait SynchronizedSet[A] extends scala.collection.mutable.Set[A] { + + abstract override def size: Int = synchronized { + super.size + } + + override def isEmpty: Boolean = synchronized { + super.isEmpty + } + + abstract override def contains(elem: A) = synchronized { + super.contains(elem); + } + + abstract override def update(elem: A, included: Boolean): Unit = synchronized { + super.update(elem, included); + } + + abstract override def +=(elem: A): Unit = synchronized { + super.+=(elem); + } + + override def ++=(that: Iterable[A]) = synchronized { + super.++=(that); + } + + override def ++=(it: Iterator[A]) = synchronized { + super.++=(it); + } + + override def incl(elems: A*): Unit = synchronized { + super.++=(elems); + } + + abstract override def -=(elem: A): Unit = synchronized { + super.-=(elem); + } + + override def --=(that: Iterable[A]) = synchronized { + super.--=(that); + } + + override def --=(it: Iterator[A]) = synchronized { + super.--=(it); + } + + override def excl(elems: A*): Unit = synchronized { + super.--=(elems); + } + + override def intersect(that: Set[A]) = synchronized { + super.intersect(that); + } + + abstract override def clear: Unit = synchronized { + super.clear; + } + + override def subsetOf(that: scala.collection.Set[A]) = synchronized { + super.subsetOf(that); + } + + override def foreach(f: A => Unit) = synchronized { + super.foreach(f); + } + + override def filter(p: A => Boolean) = synchronized { + super.filter(p); + } + + override def toList: List[A] = synchronized { + super.toList; + } + + override def toString() = synchronized { + super.toString(); + } + + override def <<(cmd: Message[A]): Unit = synchronized { + super.<<(cmd); + } + + override def clone(): Set[A] = synchronized { + super.clone(); + } +} diff --git a/src/library/scala/collection/mutable/SynchronizedStack.scala b/src/library/scala/collection/mutable/SynchronizedStack.scala new file mode 100644 index 0000000000..c21a17b22d --- /dev/null +++ b/src/library/scala/collection/mutable/SynchronizedStack.scala @@ -0,0 +1,109 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** This is a synchronized version of the <code>Stack[T]</code> class. It + * implements a data structure which allows to store and retrieve + * objects in a last-in-first-out (LIFO) fashion. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + */ +class SynchronizedStack[A] extends Stack[A] { + + /** Checks if the stack is empty. + * + * @return true, iff there is no element on the stack + */ + override def isEmpty: Boolean = synchronized { super.isEmpty } + + /** Pushes a single element on top of the stack. + * + * @param elem the element to push onto the stack + */ + override def +=(elem: A): Unit = synchronized { super.+=(elem); } + + /** Pushes all elements provided by an <code>Iterable</code> object + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterable object + */ + override def ++=(iter: Iterable[A]): Unit = synchronized { super.++=(iter); } + + /** Pushes all elements provided by an iterator + * on top of the stack. The elements are pushed in the order they + * are given out by the iterator. + * + * @param iter an iterator + */ + override def ++=(it: Iterator[A]): Unit = synchronized { super.++=(it); } + + /** Pushes a sequence of elements on top of the stack. The first element + * is pushed first, etc. + * + * @param elems a sequence of elements + */ + override def push(elems: A*): Unit = synchronized { super.++=(elems); } + + /** Returns the top element of the stack. This method will not remove + * the element from the stack. An error is signaled if there is no + * element on the stack. + * + * @return the top element + */ + override def top: A = synchronized { super.top; } + + /** Removes the top element from the stack. + */ + override def pop: A = synchronized { super.pop; } + + /** + * Removes all elements from the stack. After this operation completed, + * the stack will be empty. + */ + override def clear: Unit = synchronized { super.clear; } + + /** Returns an iterator over all elements on the stack. This iterator + * is stable with respect to state changes in the stack object; i.e. + * such changes will not be reflected in the iterator. The iterator + * issues elements in the order they were inserted into the stack + * (FIFO order). + * + * @return an iterator over all stack elements. + */ + override def elements: Iterator[A] = synchronized { super.elements; } + + /** Creates a list of all stack elements in FIFO order. + * + * @return the created list. + */ + override def toList: List[A] = synchronized { super.toList; } + + /** Checks if two stacks are structurally identical. + * + * @return true, iff both stacks contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = synchronized { super.equals(that); } + + /** The hashCode method always yields an error, since it is not + * safe to use mutable stacks as keys in hash tables. + * + * @return never. + */ + override def hashCode(): Int = synchronized { super.hashCode(); } + + /** Returns a textual representation of a stack as a string. + * + * @return the string representation of this stack. + */ + override def toString() = synchronized { super.toString(); } +} diff --git a/src/library/scala/collection/mutable/Undoable.scala b/src/library/scala/collection/mutable/Undoable.scala new file mode 100644 index 0000000000..62e7335411 --- /dev/null +++ b/src/library/scala/collection/mutable/Undoable.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.collection.mutable; + + +/** Classes that implement the <code>Undoable</code> trait provide an operation + * <code>undo</code> which can be used to undo the last operation. + * + * @author Matthias Zenger + * @version 1.0, 08/07/2003 + */ +trait Undoable { + + /** Undo the last operation. + */ + def undo: Unit; +} diff --git a/src/library/scala/concurrent/Actor.scala b/src/library/scala/concurrent/Actor.scala new file mode 100644 index 0000000000..4ba4ab3078 --- /dev/null +++ b/src/library/scala/concurrent/Actor.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.concurrent; + +abstract class Actor extends Thread { + private val in = new MailBox; + + def send(msg: in.Message) = + in.send(msg); + + def receive[a](f: PartialFunction[in.Message, a]): a = + if (Thread.currentThread() == this) in.receive(f); + else error("receive called not on own process"); + + def receiveWithin[a](msec: long)(f: PartialFunction[in.Message, a]): a = + if (Thread.currentThread() == this) in.receiveWithin(msec)(f); + else error("receiveWithin called not on own process"); +} + + + diff --git a/src/library/scala/concurrent/Channel.scala b/src/library/scala/concurrent/Channel.scala new file mode 100644 index 0000000000..a57fd7f530 --- /dev/null +++ b/src/library/scala/concurrent/Channel.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.concurrent; + +class Channel[a] { + class LinkedList[a] { + var elem: a = _; + var next: LinkedList[a] = null; + } + private var written = new LinkedList[a]; // FIFO buffer, realized through + private var lastWritten = written; // aliasing of a linked list + private var nreaders = 0; + + def write(x: a) = synchronized { + lastWritten.elem = x; + lastWritten.next = new LinkedList[a]; + lastWritten = lastWritten.next; + if (nreaders > 0) notify(); + } + + def read: a = synchronized { + if (written.next == null) { + nreaders = nreaders + 1; wait(); nreaders = nreaders - 1; + } + val x = written.elem; + written = written.next; + x + } +} diff --git a/src/library/scala/concurrent/Lock.scala b/src/library/scala/concurrent/Lock.scala new file mode 100644 index 0000000000..a473c358c8 --- /dev/null +++ b/src/library/scala/concurrent/Lock.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + +class Lock { + var available = true; + def acquire = synchronized { + if (!available) wait(); + available = false + } + def release = synchronized { + available = true; + notify() + } +} diff --git a/src/library/scala/concurrent/MailBox.scala b/src/library/scala/concurrent/MailBox.scala new file mode 100644 index 0000000000..427d424b28 --- /dev/null +++ b/src/library/scala/concurrent/MailBox.scala @@ -0,0 +1,170 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +//class MailBox with Monitor with LinkedListQueueCreator { +class MailBox extends AnyRef with ListQueueCreator { + + type Message = AnyRef; + + private abstract class PreReceiver { + var msg: Message = null; + def isDefinedAt(msg: Message): boolean; + } + + private class Receiver[a](receiver: PartialFunction[Message, a]) extends PreReceiver { + + def isDefinedAt(msg: Message) = receiver.isDefinedAt(msg); + + def receive(): a = synchronized { + if (msg == null) wait(); + receiver(msg) + } + + def receiveWithin(msec: long): a = synchronized { + if (msg == null) wait(msec); + receiver(if (msg != null) msg else TIMEOUT()) + } + } + + private val messageQueue = queueCreate[Message]; + private val receiverQueue = queueCreate[PreReceiver]; + + /** Unconsumed messages. */ + private var sent = messageQueue.make; + + /** Pending receivers. */ + private var receivers = receiverQueue.make; + + /** + * Check whether the receiver can be applied to an unconsumed message. + * If yes, the message is extracted and associated with the receiver. + * Otherwise the receiver is appended to the list of pending receivers. + */ + private def scanSentMsgs[a](receiver: Receiver[a]): unit = synchronized { + messageQueue.extractFirst(sent, msg => receiver.isDefinedAt(msg)) match { + case None => receivers = receiverQueue.append(receivers, receiver) + case Some(Pair(msg, withoutMsg)) => { + sent = withoutMsg; + receiver.msg = msg + } + } + } + + /** + * First check whether a pending receiver is applicable to the sent + * message. If yes, the receiver is notified. Otherwise the message + * is appended to the linked list of sent messages. + */ + def send(msg: Message): unit = synchronized { + receiverQueue.extractFirst(receivers, r => r.isDefinedAt(msg)) match { + case None => sent = messageQueue.append(sent, msg) + case Some(Pair(receiver, withoutReceiver)) => { + receivers = withoutReceiver; + receiver.msg = msg; + receiver synchronized { receiver.notify() }; + } + } + } + + /** + * Block until there is a message in the mailbox for which the processor + * <code>f</code> is defined. + */ + def receive[a](f: PartialFunction[Message, a]): a = { + val r = new Receiver(f); + scanSentMsgs(r); + r.receive() + } + + /** + * Block until there is a message in the mailbox for which the processor + * <code>f</code> is defined or the timeout is over. + */ + def receiveWithin[a](msec: long)(f: PartialFunction[Message, a]): a = { + val r = new Receiver(f); + scanSentMsgs(r); + r.receiveWithin(msec) + } + +} + +///////////////////////////////////////////////////////////////// + +/** +* Module for dealing with queues. +*/ +trait QueueModule[a] { + /** Type of queues. */ + type t; + /** Create an empty queue. */ + def make: t; + /** Append an element to a queue. */ + def append(l: t, x: a): t; + /** Extract an element satisfying a predicate from a queue. */ + def extractFirst(l: t, p: a => boolean): Option[Pair[a, t]]; +} + +/** Inefficient but simple queue module creator. */ +trait ListQueueCreator { + def queueCreate[a]: QueueModule[a] = new QueueModule[a] { + type t = List[a]; + def make: t = Nil; + def append(l: t, x: a): t = l ::: x :: Nil; + def extractFirst(l: t, p: a => boolean): Option[Pair[a, t]] = + l match { + case Nil => None + case head :: tail => + if (p(head)) + Some(Pair(head, tail)) + else + extractFirst(tail, p) match { + case None => None + case Some(Pair(x, without_x)) => Some(Pair(x, head :: without_x)) + } + } + } +} + +/** Efficient queue module creator based on linked lists. */ +trait LinkedListQueueCreator { + import scala.collection.mutable.LinkedList; + def queueCreate[a <: AnyRef]: QueueModule[a] = new QueueModule[a] { + type t = Pair[LinkedList[a], LinkedList[a]]; // fst = the list, snd = last elem + def make: t = { + val l = new LinkedList[a](null, null); + Pair(l, l) + } + def append(l: t, x: a): t = { + val atTail = new LinkedList(x, null); + l._2 append atTail; + Pair(l._1, atTail) + } + def extractFirst(l: t, p: a => boolean): Option[Pair[a, t]] = { + var xs = l._1; + var xs1 = xs.next; + while (xs1 != null && !p(xs1.elem)) { + xs = xs1; + xs1 = xs1.next; + } + if (xs1 != null) { + xs.next = xs1.next; + if (xs.next == null) + Some(Pair(xs1.elem, Pair(l._1, xs))) + else + Some(Pair(xs1.elem, l)) + } + else + None + } + } +} + diff --git a/src/library/scala/concurrent/NameServer.scala b/src/library/scala/concurrent/NameServer.scala new file mode 100644 index 0000000000..b7646e6deb --- /dev/null +++ b/src/library/scala/concurrent/NameServer.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +object NameServer { + + val names = new scala.collection.mutable.HashMap[Symbol, Process]; + + def register(name: Symbol, proc: Process) = { + if (names.contains(name)) error("Name:" + name + " already registred"); + names += name -> proc; + } + + def unregister(name: Symbol) = { + if (names.contains(name)) + names -= name; + else + error("Name:" + name + " not registred"); + } + + def whereis(name: Symbol): Option[Process] = + names.get(name); + + def send(name: Symbol, msg: MailBox#Message) = + names(name).send(msg); + +} diff --git a/src/library/scala/concurrent/Process.scala b/src/library/scala/concurrent/Process.scala new file mode 100644 index 0000000000..950220da35 --- /dev/null +++ b/src/library/scala/concurrent/Process.scala @@ -0,0 +1,78 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +object Process { + def spawn(body: => Unit): Process = { + val p = new Process(body); + p.start(); + p; + } + def spawn_link(body: => Unit): Process = { + self.spawn_link(body); + } + + def send(p: Process,msg: MailBox#Message) = + p.send(msg); + def receive[a](f: PartialFunction[MailBox#Message, a]): a = + self.receive(f); + + def receiveWithin[a](msec: long)(f: PartialFunction[MailBox#Message, a]): a = + self.receiveWithin(msec)(f); + + def self: Process = { + if (Thread.currentThread().isInstanceOf[Process]) + Thread.currentThread().asInstanceOf[Process] + else error("Self called outside a process"); + } + + def exit(p: Process, reason: AnyRef) = + p.exit(reason); +} + +class Process(body: => Unit) extends Actor() { + private var exitReason: AnyRef = null; + private var links: List[Process] = Nil; + override def run() = { + try {body} + catch { + case _: java.lang.InterruptedException => + signal(exitReason); + case (exitSignal) => + signal(exitSignal); + } + } + + private def signal(s: MailBox#Message) = { + links.foreach((p:Process) => p.send(Tuple3('EXIT,this,s))); + } + + def !(msg: MailBox#Message) = + send(msg); + + def link(p: Process) = { + links = p::links; + } + + def spawn_link(body: => Unit) = { + val p = new Process(body); + p.link(this); + p.start(); + p + } + + def self = this; + + def exit(reason: AnyRef): Unit = { + exitReason = reason; + interrupt(); + } +} diff --git a/src/library/scala/concurrent/SyncChannel.scala b/src/library/scala/concurrent/SyncChannel.scala new file mode 100644 index 0000000000..6cab7b2ad6 --- /dev/null +++ b/src/library/scala/concurrent/SyncChannel.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +class SyncChannel[a] { + private var data: a = _; + private var reading = false; + private var writing = false; + + def await(cond: => Boolean) = while (!cond) { wait() } + + def write(x: a) = synchronized { + await(!writing); + data = x; + writing = true; + if (reading) notifyAll(); + else await(reading) + } + + def read: a = synchronized { + await(!reading); + reading = true; + await(writing); + val x = data; + writing = false; + reading = false; + notifyAll(); + x + } +} diff --git a/src/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala new file mode 100644 index 0000000000..0e1b9fd38c --- /dev/null +++ b/src/library/scala/concurrent/SyncVar.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +class SyncVar[a] { + + private var isDefined: Boolean = false; + private var value: a = _; + + def get = synchronized { + if (!isDefined) wait(); + value + } + + def set(x: a) = synchronized { + value = x; + isDefined = true; + notifyAll() + } + + def isSet: Boolean = synchronized { + isDefined; + } + + def unset = synchronized { + isDefined = false + } + +} diff --git a/src/library/scala/concurrent/TIMEOUT.scala b/src/library/scala/concurrent/TIMEOUT.scala new file mode 100644 index 0000000000..d665fdf649 --- /dev/null +++ b/src/library/scala/concurrent/TIMEOUT.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + +case class TIMEOUT(); diff --git a/src/library/scala/concurrent/jolib.scala b/src/library/scala/concurrent/jolib.scala new file mode 100644 index 0000000000..08611be513 --- /dev/null +++ b/src/library/scala/concurrent/jolib.scala @@ -0,0 +1,75 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +/** +* Library for using join-calculus concurrent primitives in Scala. +*/ +object jolib { + + type Pattern = List[Signal]; + + type Rule = PartialFunction[List[Any], unit]; + + /////////////////// JOIN DEFINITION ///////////////////////// + + class Join { + + private var ruls: List[Pair[Pattern, Rule]] = null; + + def canMatch(p: Pattern) = + p forall { s => !s.queue.isEmpty }; + + def values(p: Pattern): List[Any] = + p map { s => s.queue.dequeue: Any }; + + def rules(rs: Pair[Pattern, Rule]*) = + ruls = rs.asInstanceOf[List[Pair[Pattern, Rule]]]; + + def tryMatch = + (ruls find { case Pair(p, _) => canMatch(p) }) match { + case None => () => (); + case Some(Pair(p, r)) => { + val args = values(p); + () => concurrent.ops.spawn(r(args)) + } + } + + } + + /////////////////// SIGNALS ///////////////////////// + + abstract class Signal(join: Join) { + type C; + val queue = new collection.mutable.Queue[C]; + def tryReduction(x: C): unit = { + val continuation = join synchronized { + queue.enqueue(x); + join.tryMatch + }; + continuation() + } + } + + abstract class Asynchr(join: Join) extends Signal(join) { + def apply(x: C): unit = tryReduction(x); + } + + abstract class Synchr[a](join: Join) extends Signal(join) { + type C <: SyncVar[a]; + def apply(x: C): a = { + tryReduction(x); + x.get + } + } + +} + diff --git a/src/library/scala/concurrent/ops.scala b/src/library/scala/concurrent/ops.scala new file mode 100644 index 0000000000..04bee676ec --- /dev/null +++ b/src/library/scala/concurrent/ops.scala @@ -0,0 +1,52 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-04, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.concurrent; + + +object ops { + + def spawn(p: => Unit) = { + val t = new Thread() { override def run() = p; } + t.start() + } + + def future[a](p: => a): () => a = { + val result = new SyncVar[a]; + spawn { result set p } + () => result.get + } + + def par[a, b](xp: => a, yp: => b): Pair[a, b] = { + val y = new SyncVar[b]; + spawn { y set yp } + Pair(xp, y.get) + } + + def replicate(start: Int, end: Int)(p: Int => Unit): Unit = { + if (start == end) + () + else if (start + 1 == end) + p(start) + else { + val mid = (start + end) / 2; + spawn { replicate(start, mid)(p) } + replicate(mid, end)(p) + } + } + +/* + def parMap[a,b](f: a => b, xs: Array[a]): Array[b] = { + val results = new Array[b](xs.length); + replicate(0, xs.length) { i => results(i) = f(xs(i)) } + results + } +*/ + +} diff --git a/src/library/scala/concurrent/pilib.scala b/src/library/scala/concurrent/pilib.scala new file mode 100644 index 0000000000..6ce7a560af --- /dev/null +++ b/src/library/scala/concurrent/pilib.scala @@ -0,0 +1,189 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.concurrent; + +/** +* Library for using Pi-calculus concurrent primitives in Scala. As an example, +the definition of a two-place buffer using the <b>pilib</b> library looks like: +*<pre> +*def Buffer[a](put: Chan[a], get: Chan[a]): unit = { +* def B0: unit = choice ( put * { x => B1(x) } ); +* def B1(x: a): unit = choice ( get(x) * B0, put * { y => B2(x, y) } ); +* def B2(x: a, y: a): unit = choice ( get(x) * B1(y) ); +* B0 +*} +*</pre> +* +* @see <a href="http://scala.epfl.ch/docu/related.html">PiLib: A Hosted Language for Pi-Calculus Style Concurrency</a> +* @author Vincent Cremet, Martin Odersky +*/ +object pilib { + + /////////////////////////// SPAWN ////////////////////////////// + + /** + * Run several processes in parallel using the following syntax: + * spawn < p_1 | ... | p_n > + */ + trait Spawn { + def <(p: => unit): Spawn; + def |(p: => unit): Spawn; + def > : unit; + } + val spawn = new Spawn { + //object spawn extends Spawn { // BUG ! + def <(p: => unit): Spawn = { scala.concurrent.ops.spawn(p); this } + def |(p: => unit): Spawn = { scala.concurrent.ops.spawn(p); this } + def > : unit = () + } + + //////////////////////// GUARDED PROCESSES ///////////////////////// + + /** Untyped channel. */ + class UChan { + /** Default log function. */ + var log = (x: Any) => (); + } + + /** An untyped guarded process. + * @param n channel name + * @param polarity input (true) or output (false) + * @param v transmitted value + * @param c continuation + */ + case class UGP(n: UChan, polarity: boolean, v: Any, c: Any => Any); + + /** Typed guarded process. */ + class GP[a](n: UChan, polarity: boolean, v: Any, c: Any => a) { + val untyped = UGP(n, polarity, v, c); + } + + ////////////////////////// CHANNELS ////////////////////////////// + + /** + * Name on which one can emit, receive or that can be emitted or received + * during a communication. + */ + class Chan[a] extends UChan with Function1[a, Product[a]] { + + var defaultValue: a = _; + + /** Creates an input guarded process. */ + def input[b](c: a => b) = + new GP(this, true, (), x => c(x.asInstanceOf[a])); + + /** Creates an input guarded process. */ + def output[b](v: a, c: () => b) = + new GP(this, false, v, x => c()); + + /** Blocking read. */ + def read = { + var res: a = defaultValue; + choice ( input(x => res = x) ); + res + } + + /** Blocking write. */ + def write(x: a) = + choice ( output(x, () => ()) ); + + /** Syntactic sugar for input. */ + def *[b](f: a => b) = + input(f); + + /** Syntactic sugar for output. */ + def apply(v: a) = + new Product(this, v); + + /** Attach a function to be evaluated at each communication event + * on this channel. Replace previous attached function. + */ + def attach(f: a => unit) = + log = x => f(x.asInstanceOf[a]); + } + + class Product[a](c: Chan[a], v: a) { + def *[b](f: => b) = c.output(v, () => f); + } + + //////////////////// SUM OF GUARDED PROCESSES ////////////////////// + + case class Sum(gs: List[UGP]) { + + /** Continuation of the sum. */ + var cont: () => Any = _; + + var initialized = false; + + /** + * Block if not initialized otherwise continue with the + * continuation. + */ + def continue = synchronized { + if (!initialized) wait(); + cont() + } + + /** Set the values of parameters and awake the sleeping sum. */ + def set(f: () => Any) = synchronized { + cont = f; + initialized = true; + notify() + } + } + + /////////////////////////// COMMUNICATION ////////////////////////// + + private var sums: List[Sum] = Nil; + + /** Test if two lists of guarded processes can communicate. */ + private def matches(gs1: List[UGP], gs2: List[UGP]): Option[Triple[() => unit, () => Any, () => Any]] = + Pair(gs1, gs2) match { + case Pair(Nil, _) => None + case Pair(_, Nil) => None + case Pair(UGP(a1, d1, v1, c1) :: rest1, UGP(a2, d2, v2, c2) :: rest2) => + if (a1 == a2 && d1 == !d2) + Some(Triple(() => if (d1) a1.log(v2) else a1.log(v1), () => c1(v2), () => c2(v1))) + else matches(gs1, rest2) match { + case None => matches(rest1, gs2) + case Some(t) => Some(t) + } + } + + /** + * Test if the given sum can react with one of the pending sums. + * If yes then do the reaction otherwise append the sum at the end + * of the pending sums. + */ + private def compare(s1: Sum, ss: List[Sum]): List[Sum] = + ss match { + case Nil => ss ::: List(s1) + case s2 :: rest => matches(s1.gs, s2.gs) match { + case None => s2 :: compare(s1, rest) + case Some(Triple(log, c1, c2)) => { + log(); + s1.set(c1); + s2.set(c2); + rest + } + } + } + + /** Pi-calculus non-deterministic choice. */ + def choice[a](s: GP[a]*): a = { + val sum = Sum(s.asInstanceOf[List[GP[a]]] map { x => x.untyped }); + synchronized { sums = compare(sum, sums) }; + (sum.continue).asInstanceOf[a] + } + + +} + diff --git a/src/library/scala/dbc/DataType.scala b/src/library/scala/dbc/DataType.scala new file mode 100644 index 0000000000..b984169350 --- /dev/null +++ b/src/library/scala/dbc/DataType.scala @@ -0,0 +1,66 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +/** An ISO-9075:2003 (SQL) data type. Mappings between SQL types and + * database specific types should be provided by the database driver. + */ +abstract class DataType { + + /** Tests whether this datatype is equivalent to another. Usually, two + * types are defined as equivalent if they are equal. Two types can be + * equivalent without being equal if values of those types will be + * encoded in the same native Scala type. + */ + def isEquivalent(datatype: DataType): Boolean; + + /** Tests whether this datatype is equivalent or a subtype of another + * datatype. Type <code>A</code> is said to be subtype of type + * <code>B</code> if any value of type <code>A</code> can be + * represented as a value of type <code>B</code>. + */ + def isSubtypeOf(datatype: DataType): Boolean; + + /** The native Scala type in which values of this SQL type will be + * encoded. + */ + type NativeType <: Any; + + /** The native Scala type in which values of this SQL type will be + * encoded. This must point to the same type as <code>NativeType</code>. + */ + def nativeTypeId: DataType.Id; + + /** Whether the value can take the null value, None when this property is + * unknown. + */ + def nullable: Option[Boolean] = None; + + /** The SQL name of the type */ + def sqlString: String = "UNDEFINED DATA TYPE" + +} + +object DataType { + + type Id = Int; + + val OBJECT : Id = 10; + val BOOLEAN : Id = 20; + val BYTE : Id = 30; + val SHORT : Id = 31; + val INT : Id = 32; + val LONG : Id = 33; + val BIG_INTEGER: Id = 34; + val BIG_DECIMAL: Id = 35; + val FLOAT : Id = 40; + val DOUBLE : Id = 41; + val STRING : Id = 50; + +} diff --git a/src/library/scala/dbc/Database.scala b/src/library/scala/dbc/Database.scala new file mode 100644 index 0000000000..28270184b5 --- /dev/null +++ b/src/library/scala/dbc/Database.scala @@ -0,0 +1,186 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +import java.sql._; + +/** A link to a database. The <code>Database</code> abstract class must + * be specialised for every different DBMS. + * + * @author Gilles Dubochet + */ +case class Database(dbms: Vendor) { + + class Closed extends Exception; + + /** A lock used for operations that need to be atomic for this database + * instance. */ + private val lock: scala.concurrent.Lock = new scala.concurrent.Lock(); + + /** The vendor of the DBMS that contains this database. */ + private val vendor: Vendor = dbms; + + /** The Database connections available to use. */ + private var availableConnections: List[Connection] = Nil; + + /** The connections that are currently in use. */ + private var usedConnections: List[Connection] = Nil; + + /** Whether the database no longer accepts new connections. */ + private var closing: Boolean = false; + + /** Retrieves a connection from the available connection pool or creates + * a new one. + * + * @returns A connection that can be used to access the database. + */ + private def getConnection: Connection = { + if (closing) { + throw new Closed; + } else { + availableConnections match { + case Nil => { + lock.acquire; + val connection = vendor.getConnection; + usedConnections = connection :: usedConnections; + lock.release; + connection + } + case connection :: cs => { + lock.acquire; + availableConnections = cs; + usedConnections = connection :: usedConnections; + lock.release; + connection; + } + } + } + } + + /** Closes a connection to this database. A closed connection might + * also return to the available connection pool if the latter is depleted. + * + * @param connection The connection that should be closed. + */ + private def closeConnection(connection: Connection): Unit = { + if (closing) { + connection.close(); + } else { + lock.acquire; + usedConnections = usedConnections.remove(e => (e.equals(connection))); + if (availableConnections.length < vendor.retainedConnections) + availableConnections = connection :: availableConnections + else + connection.close(); + lock.release; + } + } + + /** .. + */ + def close: Unit = { + closing = true; + for (val conn <- availableConnections) conn.close(); + } + + /** Executes a statement that returns a relation on this database. + * + * @param relationStatement The statement to execute. + * @return The relation returned by the database for this statement. + */ + def executeStatement(relationStatement: statement.Relation): result.Relation = + executeStatement(relationStatement, false); + + /** Executes a statement that returns a relation on this database. + * + * @param relationStatement The statement to execute. + * @param debug Whether debugging information should be printed on the console. + * @return The relation returned by the database for this statement. + */ + def executeStatement(relationStatement: statement.Relation, + debug: Boolean): result.Relation = + new scala.dbc.result.Relation { + val statement = relationStatement; + if (debug) Console.println("## " + statement.sqlString); + private val connection = getConnection; + val sqlResult = connection.createStatement().executeQuery(statement.sqlString); + closeConnection(connection); + statement.typeCheck(this); + } + + /** Executes a statement that updates the state of the database. + * + * @param statusStatement The statement to execute. + * @return The status of the database after the statement has been executed. + */ + def executeStatement(statusStatement: statement.Status): result.Status[Unit] = + executeStatement(statusStatement, false); + + /** Executes a statement that updates the state of the database. + * + * @param statusStatement The statement to execute. + * @param debug Whether debugging information should be printed on the console. + * @return The status of the database after the statement has been executed. + */ + def executeStatement(statusStatement: statement.Status, + debug: Boolean): result.Status[Unit] = + new scala.dbc.result.Status[Unit] { + val statement = statusStatement; + if (debug) Console.println("## " + statement.sqlString); + def result = (); + private val connection = getConnection; + val jdbcStatement: java.sql.Statement = connection.createStatement(); + jdbcStatement.execute(statement.sqlString); + val touchedCount = Some(jdbcStatement.getUpdateCount()); + closeConnection(connection); + } + + /** Executes a list of statements or other operations inside a transaction. + * Only statements are protected in a transaction, other Scala code is not. + * + * @param transactionStatement The transaction to execute as a closure. + * @return The status of the database after the transaction has been executed. + */ + def executeStatement[ResultType](transactionStatement: statement.Transaction[ResultType]): result.Status[ResultType] = + executeStatement(transactionStatement, false); + + /** Executes a list of statements or other operations inside a transaction. + * Only statements are protected in a transaction, other Scala code is not. + * + * @param transactionStatement The transaction to execute as a closure. + * @param debug Whether debugging information should be printed on the console. + * @return The status of the database after the transaction has been executed. + */ + def executeStatement[ResultType](transactionStatement: statement.Transaction[ResultType], debug: Boolean): result.Status[ResultType] = { + new scala.dbc.result.Status[ResultType] { + val touchedCount = None; + val statement = transactionStatement; + private val connection = getConnection; + connection.setAutoCommit(false); + val jdbcStatement: java.sql.Statement = connection.createStatement(); + if (debug) Console.println("## " + transactionStatement.sqlStartString); + jdbcStatement.execute(transactionStatement.sqlStartString); + val result: ResultType = try { + val buffer = transactionStatement.transactionBody(Database.this); + if (debug) Console.println("## " + transactionStatement.sqlCommitString); + jdbcStatement.execute(transactionStatement.sqlCommitString); + buffer + } catch { + case e: Throwable => { + if (debug) Console.println("## " + transactionStatement.sqlAbortString); + jdbcStatement.execute(transactionStatement.sqlAbortString); + throw e; + } + } + connection.setAutoCommit(true); + closeConnection(connection); + } + } + +} diff --git a/src/library/scala/dbc/Syntax.scala b/src/library/scala/dbc/Syntax.scala new file mode 100644 index 0000000000..cde8112979 --- /dev/null +++ b/src/library/scala/dbc/Syntax.scala @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +import java.math.{BigDecimal, BigInteger}; + + +/** This class .. + * + */ +object Syntax { + + import syntax.DataTypeUtil; + + /* Data types */ + def boolean = DataTypeUtil.boolean; + def tinyint = DataTypeUtil.tinyint; + def smallint = DataTypeUtil.smallint; + def integer = DataTypeUtil.integer; + def bigint = DataTypeUtil.bigint; + def real = DataTypeUtil.real; + + def numeric(precision: Int) = DataTypeUtil.numeric(precision); + def numeric(precision: Int, scale: Int) = DataTypeUtil.numeric(precision, scale); + + def doublePrecision = DataTypeUtil.doublePrecision; + def character(length: Int) = DataTypeUtil.character(length); + def characterVarying(length: Int) = DataTypeUtil.characterVarying(length); + def characterLargeObject = DataTypeUtil.characterLargeObject; + + /* Statements */ + //def select + + /* Other stuff */ + def database (server: String, username: String, password: String): dbc.Database = + syntax.Database.database(server, username, password); + +} diff --git a/src/library/scala/dbc/Utilities.scala b/src/library/scala/dbc/Utilities.scala new file mode 100644 index 0000000000..46ca27a019 --- /dev/null +++ b/src/library/scala/dbc/Utilities.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +/** An object offering transformation methods (views) on various values. + * This object's members must be visible in an expression to use value + * auto-conversion. + */ +object Utilities { + + def view (obj: statement.expression.Constant): Value = + obj.constantValue; + + def view (obj: Value): statement.expression.Constant = + new statement.expression.Constant { + val constantValue = obj; + } + +} diff --git a/src/library/scala/dbc/Value.scala b/src/library/scala/dbc/Value.scala new file mode 100644 index 0000000000..3ab2d0cbe5 --- /dev/null +++ b/src/library/scala/dbc/Value.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +/** A SQL-99 value of any type. */ +abstract class Value { + + /** The SQL-99 type of the value. */ + val dataType: DataType; + + type NativeType = dataType.type#NativeType; + + val nativeValue: NativeType; + + /** A SQL-99 compliant string representation of the value. */ + def sqlString: String; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/Vendor.scala b/src/library/scala/dbc/Vendor.scala new file mode 100644 index 0000000000..db44ddec42 --- /dev/null +++ b/src/library/scala/dbc/Vendor.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc; + +import java.sql.{Connection, Driver}; + + +/** This class .. + */ +abstract class Vendor { + + def nativeDriverClass: Class; + def uri: java.net.URI; + def user: String; + def pass: String; + def nativeProperties: java.util.Properties = { + val properties = new java.util.Properties(); + properties.setProperty("user", user); + properties.setProperty("password", pass); + properties + } + + def retainedConnections: Int; + + def getConnection: Connection = { + val driver = nativeDriverClass.newInstance().asInstanceOf[Driver]; + driver.connect(uri.toString(),nativeProperties) + } + + def urlProtocolString: String; + +} diff --git a/src/library/scala/dbc/datatype/ApproximateNumeric.scala b/src/library/scala/dbc/datatype/ApproximateNumeric.scala new file mode 100644 index 0000000000..d75da1eb50 --- /dev/null +++ b/src/library/scala/dbc/datatype/ApproximateNumeric.scala @@ -0,0 +1,53 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store varying-precision + * numbers. + */ +abstract class ApproximateNumeric[Type] ( + override val nativeTypeId: DataType.Id +) extends datatype.Numeric[Type](nativeTypeId) { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: ApproximateNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision == dt.precision && + signed == dt.signed) + case _ => + false + } + + def isSubtypeOf (datatype:DataType) = datatype match { + case dt:ApproximateNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision <= dt.precision && + signed == dt.signed) + case _ => + false + } + + /** A SQL-99 compliant string representation of the type. + * <h3>Compatibility notice</h3> This method assumes that a real + * uses 32 bits and a double 64. This is not defined in the + * standard but is usually the case. + */ + override def sqlString: java.lang.String = Tuple2(precisionRadix,precision) match { + case Tuple2(2,64) => "REAL" + case Tuple2(2,128) => "DOUBLE PRECISION" + case Tuple2(2,p) => + throw exception.UnsupportedFeature("SQL-99 does not support an approximate numeric type with a binary defined precision other than 16, 32 and 64 bits"); + case Tuple2(10,p) => "FLOAT (" + p.toString() + ")" + case Tuple2(pr,_) => + throw exception.UnsupportedFeature("SQL-99 does not support the precision of an approximate numeric type to be defined in a radix other than 2 or 10"); + } + +} diff --git a/src/library/scala/dbc/datatype/Boolean.scala b/src/library/scala/dbc/datatype/Boolean.scala new file mode 100644 index 0000000000..7c68ab9c7d --- /dev/null +++ b/src/library/scala/dbc/datatype/Boolean.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** The SQL type for a truth value. */ +class Boolean extends DataType { + + def isEquivalent (datatype:DataType) = datatype match { + case dt:Boolean => true + case _ => false + } + + def isSubtypeOf (datatype:DataType) = isEquivalent(datatype); + + type NativeType = scala.Boolean; + val nativeTypeId = DataType.BOOLEAN; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "BOOLEAN"; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/datatype/Character.scala b/src/library/scala/dbc/datatype/Character.scala new file mode 100644 index 0000000000..ded08c5602 --- /dev/null +++ b/src/library/scala/dbc/datatype/Character.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A SQL type for a string of characters of arbitrary length with + * arbitrary character set. + */ +abstract class Character extends CharacterString { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: Character => + length == dt.length && encoding == dt.encoding + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: Character => + length >= dt.length && encoding == dt.encoding + case _ => + false + } + + /** The length of the string defined in characters. */ + def length: Int; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "CHARACTER (" + length.toString() + ")"; + +} diff --git a/src/library/scala/dbc/datatype/CharacterLargeObject.scala b/src/library/scala/dbc/datatype/CharacterLargeObject.scala new file mode 100644 index 0000000000..adbd979f6e --- /dev/null +++ b/src/library/scala/dbc/datatype/CharacterLargeObject.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A SQL type for an unbounded length string of characters with arbitrary + * character set. */ +class CharacterLargeObject extends CharacterString { + + def isEquivalent (datatype:DataType) = datatype match { + case dt:CharacterLargeObject => { + encoding == dt.encoding + } + case _ => false + } + + def isSubtypeOf (datatype:DataType) = isEquivalent(datatype); + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = "CHARACTER LARGE OBJECT"; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/datatype/CharacterString.scala b/src/library/scala/dbc/datatype/CharacterString.scala new file mode 100644 index 0000000000..e937d10b9e --- /dev/null +++ b/src/library/scala/dbc/datatype/CharacterString.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store strings of characters. */ +abstract class CharacterString extends String { + + type NativeType = java.lang.String; + val nativeTypeId = DataType.STRING; + + /** The name of the character set in which the string is encoded. */ + def encoding: Option[java.lang.String] = None; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/datatype/CharacterVarying.scala b/src/library/scala/dbc/datatype/CharacterVarying.scala new file mode 100644 index 0000000000..b7f743ce0b --- /dev/null +++ b/src/library/scala/dbc/datatype/CharacterVarying.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A SQL type for a varying length string of characters with arbitrary + * maximal length and arbitrary character set. + */ +abstract class CharacterVarying extends CharacterString { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: CharacterVarying => + length == dt.length && encoding == dt.encoding + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: CharacterVarying => + length >= dt.length && encoding == dt.encoding + case _ => + false + } + + /** The maximal length of the string defined in characters. */ + def length: Int; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = + "CHARACTER VARYING (" + length.toString() + ")"; + +} diff --git a/src/library/scala/dbc/datatype/ExactNumeric.scala b/src/library/scala/dbc/datatype/ExactNumeric.scala new file mode 100644 index 0000000000..9152a7e928 --- /dev/null +++ b/src/library/scala/dbc/datatype/ExactNumeric.scala @@ -0,0 +1,61 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store constant-precision + * numbers. + */ +abstract class ExactNumeric[Type]( + override val nativeTypeId: DataType.Id +) extends datatype.Numeric[Type](nativeTypeId) { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: ExactNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision == dt.precision && + scale == dt.scale && + signed == dt.signed) + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = datatype match { + case dt: ExactNumeric[Type] => + (nativeTypeId == dt.nativeTypeId && + precisionRadix == dt.precisionRadix && + precision <= dt.precision && + scale <= dt.scale && + signed == dt.signed) + case _ => + false + } + + /** The number of digits used after the decimal point. */ + def scale: Int; + + /** A SQL-99 compliant string representation of the type. + * <h3>Compatibility notice</h3> This method assumes that an integer + * uses 32 bits, a small 16 and a big 64. This is not defined in the + * standard but is usually the case. + */ + override def sqlString: java.lang.String = Tuple3(precisionRadix,precision,scale) match { + case Tuple3(2,16,0) => "SMALLINT" + case Tuple3(2,32,0) => "INTEGER" + case Tuple3(2,64,0) => "BIGINT" + case Tuple3(2,java.lang.Integer.MAX_VALUE,0) => "BIGINT" + case Tuple3(2,p,s) => + throw exception.UnsupportedFeature("SQL-99 does not support an exact numeric type with a binary defined precision other than 16, 32 and 64 bits"); + case Tuple3(10,p,0) => "NUMERIC (" + p.toString() + ")" + case Tuple3(10,p,s) => "NUMERIC (" + p.toString() + ", " + s.toString() + ")" + case Tuple3(pr,_,_) => + throw exception.UnsupportedFeature("SQL-99 does not support the precision of an exact numeric type to be defined in a radix other than 2 or 10"); + } + +} diff --git a/src/library/scala/dbc/datatype/Factory.scala b/src/library/scala/dbc/datatype/Factory.scala new file mode 100644 index 0000000000..13d9cd35e2 --- /dev/null +++ b/src/library/scala/dbc/datatype/Factory.scala @@ -0,0 +1,246 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +import java.sql.Types._; +import java.math.BigInteger; +import java.math.BigDecimal; + +object Factory { + + final val java_lang_Integer_SIZE = 32; + final val java_lang_Long_SIZE = 64; + + /** Returns a mullable property formated as a boolean option */ + def isNullable (metadata:java.sql.ResultSetMetaData, index:Int): Option[scala.Boolean] = + metadata.isNullable(index) match { + case java.sql.ResultSetMetaData.columnNoNulls => Some(false); + case java.sql.ResultSetMetaData.columnNullable => Some(true); + case java.sql.ResultSetMetaData.columnNoNulls => None; + } + + /** Returns the binary precision for an integer field. This should only be + * used to find precision for integer numbers. It assumes that + * bytes cannot be used partially (result % 8 = 0). */ + def bytePrecision (precision:Int, signed:scala.Boolean, safe:scala.Boolean): Int = { + val decimalPrecision = precision + (if (safe) 1 else 0); + Pair(signed,decimalPrecision) match { + case Pair(_,0) => java.lang.Integer.MAX_VALUE // That's a bit of a hack. + case Pair(_,dp) if (dp <= 3) => 8 + case Pair(_,dp) if (dp <= 5) => 16 + case Pair(true,dp) if (dp <= 7) => 24 + case Pair(false,dp) if (dp <= 8) => 24 + case Pair(_,dp) if (dp <= 10) => 32 + case Pair(true,dp) if (dp <= 12) => 40 + case Pair(false,dp) if (dp <= 13) => 40 + case Pair(_,dp) if (dp <= 15) => 48 + case Pair(_,dp) if (dp <= 17) => 56 + case Pair(true,dp) if (dp <= 19) => 64 + case Pair(false,dp) if (dp <= 20) => 64 + case Pair(_,dp) if (dp <= 22) => 72 + case Pair(true,dp) if (dp <= 24) => 80 + case Pair(false,dp) if (dp <= 25) => 80 + case Pair(_,dp) if (dp <= 27) => 88 + case Pair(_,dp) if (dp <= 29) => 96 + case Pair(_,dp) if (dp <= 32) => 104 + case Pair(_,dp) if (dp <= 34) => 112 + case Pair(true,dp) if (dp <= 36) => 120 + case Pair(false,dp) if (dp <= 37) => 120 + case Pair(_,dp) if (dp <= 39) => 128 + case _ => java.lang.Integer.MAX_VALUE + } + } + + def create (metadata:java.sql.ResultSetMetaData, index:Int): DataType = { + metadata.getColumnType(index) match { + /* Boolean data types. */ + case BOOLEAN => new datatype.Boolean { + override val nullable = isNullable(metadata,index); + } + case BIT => new datatype.Boolean { + override val nullable = isNullable(metadata,index); + } + /* Fixed precision numeric data types. */ + case DECIMAL => { + Pair(bytePrecision(metadata.getPrecision(index),metadata.isSigned(index),true),metadata.getScale(index) == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + } + } + case NUMERIC => { + Pair(bytePrecision(metadata.getPrecision(index),metadata.isSigned(index),true),metadata.getScale(index) == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 10; + val precision = metadata.getPrecision(index); + val signed = metadata.isSigned(index); + val scale = metadata.getScale(index); + } + } + } + /* Fixed precision integer data types. */ + case BIGINT => + new datatype.ExactNumeric[Long](DataType.LONG) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 64; + val signed = metadata.isSigned(index); + val scale = 0; + } + case INTEGER => + new datatype.ExactNumeric[Int](DataType.INT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 32; + val signed = metadata.isSigned(index); + val scale = 0; + } + case SMALLINT => + new datatype.ExactNumeric[Short](DataType.SHORT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 16; + val signed = metadata.isSigned(index); + val scale = 0; + } + case TINYINT => + new datatype.ExactNumeric[Byte](DataType.BYTE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 8; + val signed = metadata.isSigned(index); + val scale = 0; + } + /* Floating point numeric data types. */ + case REAL => + new datatype.ApproximateNumeric[Float](DataType.FLOAT) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 64; + val signed = metadata.isSigned(index); + } + case DOUBLE => + new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 128; + val signed = metadata.isSigned(index); + } + case FLOAT => + new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + override val nullable = isNullable(metadata,index); + val precisionRadix = 2; + val precision = 128; + val signed = metadata.isSigned(index); + } + /* Character string data types. */ + case CHAR => new datatype.Character { + override val nullable = isNullable(metadata,index); + val length = metadata.getColumnDisplaySize(index); + } + case CLOB => new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + case LONGVARCHAR => { + if (metadata.getColumnDisplaySize(index) >= 0) + new datatype.CharacterVarying { + override val nullable = isNullable(metadata,index); + def length = metadata.getColumnDisplaySize(index); + } + else // A PostgreSQL Hack + new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + } + case VARCHAR => { + if (metadata.getColumnDisplaySize(index) >= 0) + new datatype.CharacterVarying { + override val nullable = isNullable(metadata,index); + def length = metadata.getColumnDisplaySize(index); + } + else // A PostgreSQL Hack + new datatype.CharacterLargeObject { + override val nullable = isNullable(metadata,index); + } + } + /* Undefined cases. */ + case OTHER => new datatype.Unknown { + override val nullable = isNullable(metadata, index); + } + /* Unsupported data types. */ + case REF | ARRAY | STRUCT => + error ("I don't support composite data types yet."); + case DATALINK | DISTINCT | JAVA_OBJECT | NULL => + error ("I won't support strange data types."); + /* Unsupported binary string data types. */ + case BINARY | BLOB | LONGVARBINARY | VARBINARY => + error ("I don't support binary string data types yet."); + /* Unsupported date and time data types. */ + case DATE | TIME | TIMESTAMP => + error ("I don't support date and time data types yet."); + /* Default case */ + case x => error ("I don't know about this ("+metadata.getColumnTypeName(index)+") JDBC type.") + } + } +} diff --git a/src/library/scala/dbc/datatype/Numeric.scala b/src/library/scala/dbc/datatype/Numeric.scala new file mode 100644 index 0000000000..ef3eee1f32 --- /dev/null +++ b/src/library/scala/dbc/datatype/Numeric.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store numbers. */ +abstract class Numeric[Type](_nativeTypeId: DataType.Id) extends DataType { + + type NativeType = Type; + val nativeTypeId = _nativeTypeId; + + /** The radix in which the precision (and scale when appliable) is defined. + * ISO-9075 only allows 2 and 10 for this value. + */ + def precisionRadix: Int; + + /** The number of significant digits for that number. */ + def precision: Int; + + /** Whether the number is signed or not. */ + def signed: scala.Boolean; + +} diff --git a/src/library/scala/dbc/datatype/String.scala b/src/library/scala/dbc/datatype/String.scala new file mode 100644 index 0000000000..7d409e6c2d --- /dev/null +++ b/src/library/scala/dbc/datatype/String.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** A type category for all SQL types that store strings of elements. + */ +abstract class String extends DataType { + + /** The maximal possible length of the string defined in characters. + * This is an implementation-specific value. + */ + def maxLength: Option[Int] = None; + +} diff --git a/src/library/scala/dbc/datatype/Unknown.scala b/src/library/scala/dbc/datatype/Unknown.scala new file mode 100644 index 0000000000..1e7895eb46 --- /dev/null +++ b/src/library/scala/dbc/datatype/Unknown.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.datatype; + +/** The SQL type for a truth value. */ +class Unknown extends DataType { + + def isEquivalent(datatype: DataType) = datatype match { + case dt: Unknown => + nativeTypeId == dt.nativeTypeId + case _ => + false + } + + def isSubtypeOf(datatype: DataType) = true; + + type NativeType = Object; + val nativeTypeId = DataType.OBJECT; + + /** A SQL-99 compliant string representation of the type. */ + override def sqlString: java.lang.String = + error("The 'UNKNOWN' data type cannot be represented."); + +} diff --git a/src/library/scala/dbc/exception/IncompatibleSchema.scala b/src/library/scala/dbc/exception/IncompatibleSchema.scala new file mode 100644 index 0000000000..6095fbec82 --- /dev/null +++ b/src/library/scala/dbc/exception/IncompatibleSchema.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.exception; + +/** A type category for all SQL types that store constant-precision numbers. */ +case class IncompatibleSchema ( + expectedSchema: List[DataType], + foundSchema: List[DataType] +) extends Exception;
\ No newline at end of file diff --git a/src/library/scala/dbc/exception/UnsupportedFeature.scala b/src/library/scala/dbc/exception/UnsupportedFeature.scala new file mode 100644 index 0000000000..9fbaed81d7 --- /dev/null +++ b/src/library/scala/dbc/exception/UnsupportedFeature.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.exception; + +/** A type category for all SQL types that store constant-precision numbers. */ +case class UnsupportedFeature (msg: String) extends Exception;
\ No newline at end of file diff --git a/src/library/scala/dbc/result/Field.scala b/src/library/scala/dbc/result/Field.scala new file mode 100644 index 0000000000..af129d18b7 --- /dev/null +++ b/src/library/scala/dbc/result/Field.scala @@ -0,0 +1,60 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +import scala.dbc.datatype._; +import scala.dbc.value._; + +/** An ISO-9075:2003 (SQL) table field. */ +abstract class Field { + + /** The content (value) of the field. The type of this value is undefined, + * transformation into a useful type will be done by an automatic view + * function defined in the field object. */ + def content: Value; + + final def value[Type <: Value]: Type = + content.asInstanceOf[Type]; + + final def exactNumericValue[NativeType] = + content.asInstanceOf[dbc.value.ExactNumeric[NativeType]]; + + final def approximateNumericValue[NativeType] = + content.asInstanceOf[dbc.value.ApproximateNumeric[NativeType]]; + + final def booleanValue = + content.asInstanceOf[dbc.value.Boolean]; + + final def characterValue = + content.asInstanceOf[dbc.value.Character]; + + final def characterLargeObjectValue = + content.asInstanceOf[dbc.value.CharacterLargeObject]; + + final def characterVaryingValue = + content.asInstanceOf[dbc.value.CharacterVarying]; + + final def unknownValue = + content.asInstanceOf[dbc.value.Unknown]; + + /** The tuple that contains this field. */ + def originatingTuple: Tuple; + + /** The field metadata attached to this field. */ + def metadata: FieldMetadata; + +} + +object Field { + + implicit def view (field:Field): Value = field.content; + + + +} diff --git a/src/library/scala/dbc/result/FieldMetadata.scala b/src/library/scala/dbc/result/FieldMetadata.scala new file mode 100644 index 0000000000..97c5bdb3a3 --- /dev/null +++ b/src/library/scala/dbc/result/FieldMetadata.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +/** Informations attached to a field about its content and its relationship to the originating database. */ +abstract class FieldMetadata { + + /** The name of the field. */ + def name: String; + + /** The index of the field in the tuple. */ + def index: Int; + + /** The expected type of the field. This information is used for automatic transformation of the field value into a usable type. */ + def datatype: DataType; + + /** The name of the catalog in the database from which the field originates */ + def catalog: String; + + /** The name of the schema in the database from which the field originates */ + def schema: String; + + /** The name of the table in the database from which the field originates */ + def table: String; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/result/Relation.scala b/src/library/scala/dbc/result/Relation.scala new file mode 100644 index 0000000000..006b6dbf07 --- /dev/null +++ b/src/library/scala/dbc/result/Relation.scala @@ -0,0 +1,68 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +/** An ISO-9075:2003 (SQL) table. This is equivalent to a relation in the + * relational model. */ +abstract class Relation extends Object with Iterable[Tuple] { + + /** The statement that generated this relation. */ + def statement: scala.dbc.statement.Relation; + + /** A JDBC result containing this relation. */ + protected def sqlResult: java.sql.ResultSet; + + /** A JDBC metadata object attached to the relation. */ + protected def sqlMetadata: java.sql.ResultSetMetaData = sqlResult.getMetaData(); + + /** Metadata about all fields in a tuple of the relation. */ + def metadata: List[FieldMetadata] = + for (val count <- List.range(1, sqlMetadata.getColumnCount()+1)) yield + new FieldMetadata { + val name: String = sqlMetadata.getColumnName(count); + val index: Int = count; + val datatype: DataType = dbc.datatype.Factory.create(sqlMetadata,count); + val catalog: String = sqlMetadata.getCatalogName(count); + val schema: String = sqlMetadata.getSchemaName(count); + val table: String = sqlMetadata.getTableName(count); + } + + /** Metadata about the field at the given index. If there is no such + * field <code>None</code> is returned instead. */ + def metadataFor (index:Int): Option[FieldMetadata] = + try {Some(metadata(index))} catch {case e => None} + + /** Metadata about the field with the given column name. If there is no + * such field, <code>None</code> is returned instead. */ + def metadataFor (name:String): Option[FieldMetadata] = + metadata.find(f=>(f.name==name)); + + /** An iterator on the tuples of the relation. + * <h3>Caution</h3> A Relation only has one single iterator, due to limitations + * in DBMS. This means that if this method is called multiple times, all returned + * iterators will share the same state. */ + def elements: Iterator[Tuple] = new Iterator[Tuple] { + protected val result: java.sql.ResultSet = Relation.this.sqlResult; + def hasNext: Boolean = !(result.isLast()); + def next: Tuple = { + if (result.next()) { + new Tuple { + val me = this; + val originatingRelation = Relation.this; + val fields: List[Field] = for (val fieldMetadata <- metadata) yield + new Field { + val metadata = fieldMetadata; + val content = dbc.value.Factory.create(result,metadata.index,metadata.datatype); + val originatingTuple = me; + } + } + } else error("next on empty iterator") + } + } +} diff --git a/src/library/scala/dbc/result/Status.scala b/src/library/scala/dbc/result/Status.scala new file mode 100644 index 0000000000..56d83d4ae6 --- /dev/null +++ b/src/library/scala/dbc/result/Status.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +import scala.dbc.datatype._; + +/** An object containing the status of a query */ +abstract class Status[ResultType] { + + /** The statement that generated this status result. */ + def statement: scala.dbc.statement.Statement; + + /** The number of elements modified or added by this statement. */ + def touchedCount: Option[Int]; + + def result: ResultType; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/result/Tuple.scala b/src/library/scala/dbc/result/Tuple.scala new file mode 100644 index 0000000000..41c2f1f6d1 --- /dev/null +++ b/src/library/scala/dbc/result/Tuple.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.result; + +/** An ISO-9075:2003 (SQL) table row. This is equivalent to a tuple in the relational model. */ +abstract class Tuple { + + /** All the fields contained in the tuple. */ + def fields: List[Field]; + + /** The relation that contains the tuple. */ + def originatingRelation: Relation; + + /** The field at the given index. If there is no such field (that is the index is out of bounds), <code>None</code> is returned instead. */ + def apply (index:Int): Field = + try { + fields(index) + } catch { + case e => + throw new java.lang.IndexOutOfBoundsException("Field at index "+index+" does not exist in relation"); + } + + /** The field with the given column name. If there is no such field, <code>None</code> is returned instead. */ + def apply (name:String): Field = { + def findField (fields: List[Field], name:String): Field = fields match { + case Nil => throw new java.lang.IndexOutOfBoundsException("Field '"+name+"' does not exist in relation") + case field :: _ if (field.metadata.name == name) => field + case field :: fields => findField (fields, name) + } + findField (fields, name); + } +} diff --git a/src/library/scala/dbc/statement/AccessMode.scala b/src/library/scala/dbc/statement/AccessMode.scala new file mode 100644 index 0000000000..815f8b3c37 --- /dev/null +++ b/src/library/scala/dbc/statement/AccessMode.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +abstract class AccessMode { + def sqlString: String; +} + +object AccessMode { + case object ReadOnly extends AccessMode { + def sqlString = "READ ONLY"; + } + case object ReadWrite extends AccessMode { + def sqlString = "READ WRITE" + } +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/DerivedColumn.scala b/src/library/scala/dbc/statement/DerivedColumn.scala new file mode 100644 index 0000000000..020304f979 --- /dev/null +++ b/src/library/scala/dbc/statement/DerivedColumn.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +abstract class DerivedColumn { + + /** The value for the column. This value can be of any type but must be + * calculated from fields that appear in a relation that takes part + * in the query. */ + def valueExpression: Expression; + + /** A new name for this field. This name must be unique for the query in + * which the column takes part. */ + def asClause: Option[String]; + + /** A SQL-99 compliant string representation of the derived column + * sub-statement. This only has a meaning inside a select statement. */ + def sqlString: String = ( + valueExpression.sqlInnerString + + (asClause match { + case None => "" + case Some(ac) => " AS " + ac + }) + ); + +} diff --git a/src/library/scala/dbc/statement/Expression.scala b/src/library/scala/dbc/statement/Expression.scala new file mode 100644 index 0000000000..51cada90bb --- /dev/null +++ b/src/library/scala/dbc/statement/Expression.scala @@ -0,0 +1,25 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** An expression that calculates some value from fields. */ +abstract class Expression extends Relation { + + def fieldTypes: List[DataType] = Nil; + + /** A SQL-99 compliant string representation of the expression. */ + def sqlString: String = { + "SELECT " + sqlInnerString + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String; + +} diff --git a/src/library/scala/dbc/statement/Insert.scala b/src/library/scala/dbc/statement/Insert.scala new file mode 100644 index 0000000000..23f93ec4a0 --- /dev/null +++ b/src/library/scala/dbc/statement/Insert.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +import scala.dbc.statement.expression._; + +/** An insertion of values into a table. */ +case class Insert ( + insertionTarget: String, + insertionData: InsertionData +) extends Status { + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "INSERT INTO " + + insertionTarget + + " " + insertionData.sqlString + ); + + /** The name of the table where the data should be added. */ + //def insertionTarget: String; + + /** The data that will be added tot he table. */ + //def insertionData: InsertionData; + +} diff --git a/src/library/scala/dbc/statement/InsertionData.scala b/src/library/scala/dbc/statement/InsertionData.scala new file mode 100644 index 0000000000..2d6f370319 --- /dev/null +++ b/src/library/scala/dbc/statement/InsertionData.scala @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +import scala.dbc.statement.expression._; + +/** Data to be inserted into a table in an <code>Insert</code>. */ +abstract class InsertionData { + def sqlString: String; +} + +object InsertionData { + /** Insertion of data resulting from a query on the database. */ + case class Subquery (query:Relation) extends InsertionData { + def sqlString = query.sqlString; + } + /** Insertion of data as explicitly defined values. */ + case class Constructor ( + columnNames:Option[List[String]], + columnValues:List[Expression] + ) extends InsertionData { + def sqlString = ( + (columnNames match { + case None => "" + case Some(cn) => cn.mkString(" (",", ",")") + }) + + " VALUES" + + columnValues.map(e=>e.sqlInnerString).mkString(" (",", ",")") + ) + } +} diff --git a/src/library/scala/dbc/statement/IsolationLevel.scala b/src/library/scala/dbc/statement/IsolationLevel.scala new file mode 100644 index 0000000000..f63ebe39e8 --- /dev/null +++ b/src/library/scala/dbc/statement/IsolationLevel.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +abstract class IsolationLevel { + def sqlString: String; +} + +object IsolationLevel { + case object ReadUncommitted extends IsolationLevel { + def sqlString = "ISOLATION LEVEL READ UNCOMMITTED" + } + case object ReadCommitted extends IsolationLevel { + def sqlString = "ISOLATION LEVEL READ COMMITTED" + } + case object RepeatableRead extends IsolationLevel { + def sqlString = "ISOLATION LEVEL REPEATABLE READ" + } + case object Serializable extends IsolationLevel { + def sqlString = "ISOLATION LEVEL SERIALIZABLE" + } +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/JoinType.scala b/src/library/scala/dbc/statement/JoinType.scala new file mode 100644 index 0000000000..60d14cf968 --- /dev/null +++ b/src/library/scala/dbc/statement/JoinType.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A join behaviour in a <code>Jointure</code>. */ +abstract class JoinType { + /** A SQL-99 string representation of the join behaviour. */ + def sqlString: String; +} + +object JoinType { + /** A join behaviour where a joined tuple is created only when a + * corresponding tuple exists in both original relations. */ + case object Inner extends JoinType { + val sqlString = "INNER JOIN" + } + /** A join behaviour family where a joined tuple is created even when a + * tuple has no corresponding tuple in the other relation. The fields + * populated by values of the other tuple will receive the NULL value. + */ + abstract class Outer extends JoinType; + object Outer { + /** An outer join behaviour where there will be at least on tuple for + * every tuple in the left relation. */ + case object Left extends Outer { + val sqlString = "LEFT OUTER JOIN" + } + /** An outer join behaviour where there will be at least on tuple for + * every tuple in the right relation. */ + case object Right extends Outer { + val sqlString = "RIGHT OUTER JOIN" + } + /** An outer join behaviour where there will be at least on tuple for + * every tuple in both right and left relations. */ + case object Full extends Outer { + val sqlString = "FULL OUTER JOIN" + } + } +} diff --git a/src/library/scala/dbc/statement/Jointure.scala b/src/library/scala/dbc/statement/Jointure.scala new file mode 100644 index 0000000000..b2ea55d5c9 --- /dev/null +++ b/src/library/scala/dbc/statement/Jointure.scala @@ -0,0 +1,43 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A jointure between two relations. */ +abstract class Jointure extends Relation { + + /** The relation on the left part of the join. */ + def leftRelation: Relation; + + /** The relation on the right part of the join. */ + def rightRelation: Relation; + + /** The type of the jointure. */ + def joinType: JoinType; + + /** The condition on which the jointure needs be done. */ + def joinCondition: Option[Expression]; + + /** A SQL-99 compliant string representation of the relation statement. */ + def sqlString: String = { + "SELECT * FROM " + sqlInnerString + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = ( + leftRelation.sqlInnerString + " " + + joinType.sqlString + " " + + rightRelation.sqlInnerString + + (joinCondition match { + case Some(jc) => jc.sqlString + case None => "" + }) + ) + +} diff --git a/src/library/scala/dbc/statement/Relation.scala b/src/library/scala/dbc/statement/Relation.scala new file mode 100644 index 0000000000..a6e1a283c9 --- /dev/null +++ b/src/library/scala/dbc/statement/Relation.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A statement that returns a relation. */ +abstract class Relation extends Statement { + + def isCompatibleType: (DataType,DataType)=>Boolean = + ((dt,wdt)=>dt.isSubtypeOf(wdt)); + + def typeCheck (relation: result.Relation): Unit = { + val sameType: Boolean = ( + relation.metadata.length == fieldTypes.length && + (relation.metadata.zip(fieldTypes).forall({case Pair(field,expectedType) => + isCompatibleType(field.datatype, expectedType)})) + ); + if (!sameType) + throw new exception.IncompatibleSchema(fieldTypes,relation.metadata.map(field=>field.datatype)); + } + + def fieldTypes: List[DataType]; + + def sqlTypeString: String = + if (fieldTypes.isEmpty) + "UNTYPED" + else + fieldTypes.map(dt=>dt.sqlString).mkString("RELATION (",", ",")"); + + /** A SQL-99 compliant string representation of the statement. */ + def sqlString: String; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String; + + /** Executes the statement on the given database. */ + def execute (database: scala.dbc.Database): scala.dbc.result.Relation = { + database.executeStatement(this); + } + + def execute (database:scala.dbc.Database, debug:Boolean): scala.dbc.result.Relation = { + database.executeStatement(this,debug); + } + +} diff --git a/src/library/scala/dbc/statement/Select.scala b/src/library/scala/dbc/statement/Select.scala new file mode 100644 index 0000000000..47c449d0e1 --- /dev/null +++ b/src/library/scala/dbc/statement/Select.scala @@ -0,0 +1,91 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A statement that when executed on a database will return a relation. + * The returned relation will be a subset of a table in the database or + * a jointure between such subsets. */ +abstract class Select extends Relation { + + /** Defines if duplicated tuples should be removed from the returned + * relation. <h3>Compatibility notice</h3> Some DBMS (PostgreSQL) allow + * uniqueness constrains on an arbitrary field instead of the entire + * tuple. */ + def setQuantifier: Option[SetQuantifier]; + + /** Defines the output fields that a tuple in the returned relation will + * contain, and their content with respect to the tables in the + * database. If the fields are not specified (that is the list is + * empty), all possible input fields will be returned. <h3>Compatibility + * notice</h3> SQL's qualified asterisk select sublist is not + * available. */ + def selectList: List[DerivedColumn]; + + /** Defines the relations from which the query will obtain its data.*/ + def fromClause: List[Relation]; + + /** Defines condition that must be true in the returned relation's tuples. + * This value expression must return a boolean or boolean-compatible + * value. This condition is applied before any GROUP BY clause. */ + def whereClause: Option[Expression]; + + /** Defines the grouping of the returned relation's tuples. One tuple is + * returned for every group. The value of <code>selectList</code> must + * use aggregate functions for calculation. */ + def groupByClause: Option[List[Expression]]; + + /** Defines conditions that must be true in the returned relation's tuples. + * The value expression must return a boolean can only refer to fields + * that are grouped or to any field from inside an aggregate function. */ + def havingClause: Option[Expression]; + + /* def windowClause: Option[_]; */ + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "SELECT" + + (setQuantifier match { + case None => "" + case Some(sq) => " " + sq.sqlString + }) + + (selectList match { + case Nil => " *" + case _ => (" " + selectList.tail.foldLeft(selectList.head.sqlString) + ((name:String, dc:DerivedColumn) => name + ", " + dc.sqlString)) + }) + + (fromClause match { + case Nil => error("Empty from clause is not allowed") + case _ => (" FROM " + fromClause.tail.foldLeft(fromClause.head.sqlInnerString) + ((name:String, rel:Relation) => name + ", " + rel.sqlInnerString)) + }) + + (whereClause match { + case None => "" + case Some(expr) => " WHERE " + expr.sqlInnerString + }) + + (groupByClause match { + case None => "" + case Some(gbl) => gbl match { + case Nil => error("Empty group by clause is not allowed") + case _ => + (" GROUP BY " + + gbl.tail.foldLeft(gbl.head.sqlInnerString) + ((name:String, gb) => name + ", " + gb.sqlInnerString)) + } + }) + + (havingClause match { + case None => "" + case Some(expr) => " HAVING " + expr.sqlString + }) + ); + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = "("+sqlString+")"; + +} diff --git a/src/library/scala/dbc/statement/SetClause.scala b/src/library/scala/dbc/statement/SetClause.scala new file mode 100644 index 0000000000..292879d3d0 --- /dev/null +++ b/src/library/scala/dbc/statement/SetClause.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +import scala.dbc.statement.expression._; + +/** Data to be inserted into a table in an <code>Insert</code>. */ +case class SetClause (name:String, expr:Expression) { + val value: Pair[String,Expression] = Pair(name,expr); + def sqlString: String = + value._1 + " = " + value._2.sqlInnerString; +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/SetQuantifier.scala b/src/library/scala/dbc/statement/SetQuantifier.scala new file mode 100644 index 0000000000..a2fbbb854a --- /dev/null +++ b/src/library/scala/dbc/statement/SetQuantifier.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A set quantifier that defines the collection type of a relation. */ +abstract class SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String; +} + +object SetQuantifier { + /** A set quantifier that defines a relation as being a bag. That means + * that duplicates are allowed. */ + case object AllTuples extends SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String = "ALL"; + } + /** A set quantifier that defines a relation as being a set. That means + * that duplicates are not allowed and will be pruned. */ + case object DistinctTuples extends SetQuantifier { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String = "DISTINCT"; + } +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/Statement.scala b/src/library/scala/dbc/statement/Statement.scala new file mode 100644 index 0000000000..64f0967483 --- /dev/null +++ b/src/library/scala/dbc/statement/Statement.scala @@ -0,0 +1,14 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** An ISO-9075:2003 (SQL) statement. */ +abstract class Statement { + +} diff --git a/src/library/scala/dbc/statement/Status.scala b/src/library/scala/dbc/statement/Status.scala new file mode 100644 index 0000000000..61ab479199 --- /dev/null +++ b/src/library/scala/dbc/statement/Status.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A statement that changes the status of the database. */ +abstract class Status extends Statement { + + /** A SQL-99 compliant string representation of the statement. */ + def sqlString: String; + + /** Executes the statement on the given database. */ + def execute (database: scala.dbc.Database): scala.dbc.result.Status[Unit] = { + database.executeStatement(this); + } + + def execute (database: scala.dbc.Database, debug: Boolean): scala.dbc.result.Status[Unit] = { + database.executeStatement(this,debug); + } + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/Table.scala b/src/library/scala/dbc/statement/Table.scala new file mode 100644 index 0000000000..18a065e221 --- /dev/null +++ b/src/library/scala/dbc/statement/Table.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A reference to a table in the database. + * @author Gilles Dubochet + * @version 1.0 */ +abstract class Table extends Relation { + + /** The name of the table in the database. */ + def tableName: String; + + /** The name that the table will be called in the enclosing statement. */ + def tableRename: Option[String]; + + /** A SQL-99 compliant string representation of the relation statement. */ + def sqlString: String = { + "SELECT * FROM " + tableName + } + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside a query. */ + def sqlInnerString: String = ( + tableName + + (tableRename match { + case None => "" + case Some(rename) => " AS " + rename + }) + ) +} diff --git a/src/library/scala/dbc/statement/Transaction.scala b/src/library/scala/dbc/statement/Transaction.scala new file mode 100644 index 0000000000..b87c0ccc35 --- /dev/null +++ b/src/library/scala/dbc/statement/Transaction.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +/** A statement that changes the status of the database. */ +case class Transaction [ResultType] ( + transactionBody: (scala.dbc.Database=>ResultType), + accessMode: Option[AccessMode], + isolationLevel: Option[IsolationLevel] +) extends Statement { + + /** A SQL-99 compliant string representation of the statement. */ + def sqlStartString: String = ( + "START TRANSACTION" + + (Pair(accessMode,isolationLevel) match { + case Pair(None,None) => "" + case Pair(Some(am),None) => " " + am.sqlString + case Pair(None,Some(il)) => " " + il.sqlString + case Pair(Some(am),Some(il)) => " " + am.sqlString + ", " + il.sqlString + }) + ); + + def sqlCommitString: String = { + "COMMIT" + } + + def sqlAbortString: String = { + "ROLLBACK" + } + + //def transactionBody: (()=>Unit); + + //def accessMode: Option[AccessMode]; + + //def isolationLevel: Option[IsolationLevel]; + + def execute (database: scala.dbc.Database): scala.dbc.result.Status[ResultType] = { + database.executeStatement(this); + } + + def execute (database: scala.dbc.Database, debug: Boolean): scala.dbc.result.Status[ResultType] = { + database.executeStatement(this,debug); + } + +} diff --git a/src/library/scala/dbc/statement/Update.scala b/src/library/scala/dbc/statement/Update.scala new file mode 100644 index 0000000000..bc541200c4 --- /dev/null +++ b/src/library/scala/dbc/statement/Update.scala @@ -0,0 +1,43 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement; + +import scala.dbc.statement.expression._; + +/** An update of the state of a table. */ +case class Update ( + updateTarget: String, + setClauses: List[SetClause], + whereClause: Option[Expression] +) extends Status { + + + /** A SQL-99 compliant string representation of the select statement. */ + def sqlString: String = ( + "UPDATE " + + updateTarget + + " SET " + setClauses.map(sc=>sc.sqlString).mkString("",", ","") + + (whereClause match { + case None => "" + case Some(expr) => " WHERE " + expr.sqlString + }) + ); + + /** The name of the table that should be updated. */ + //def updateTarget: String; + + /** The data that will be added tot he table. */ + //def setClauses: List[SetClause]; + + /** Defines condition that must be true in the tuples that will be updated. + * This value expression must return a boolean or boolean-compatible + * value. */ + //def whereClause: Option[scala.dbc.statement.expression.Expression]; + +} diff --git a/src/library/scala/dbc/statement/expression/Aggregate.scala b/src/library/scala/dbc/statement/expression/Aggregate.scala new file mode 100644 index 0000000000..d0dfd6a133 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/Aggregate.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class Aggregate extends Expression { + + def aggregateName: String; + + def setFunction: SetFunction; + + def filterClause: Option[Expression]; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = ( + aggregateName + + "(" + setFunction.sqlString + ")" + + (filterClause match { + case None => "" + case Some(fc) => " FILTER (WHERE " + fc.sqlString + ")" + }) + ) + +} diff --git a/src/library/scala/dbc/statement/expression/BinaryOperator.scala b/src/library/scala/dbc/statement/expression/BinaryOperator.scala new file mode 100644 index 0000000000..a0d964a798 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/BinaryOperator.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class BinaryOperator extends Expression { + + /** The name of the operator. */ + def operator: String; + + /** The expression applied on the left of the operator. */ + def leftOperand: Expression; + + /** The expression applied on the right of the operator. */ + def rightOperand: Expression; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + leftOperand.sqlInnerString + " " + operator + " " + rightOperand.sqlInnerString + } + +} diff --git a/src/library/scala/dbc/statement/expression/Constant.scala b/src/library/scala/dbc/statement/expression/Constant.scala new file mode 100644 index 0000000000..12a089ea48 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/Constant.scala @@ -0,0 +1,18 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class Constant extends Expression { + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = constantValue.sqlString; + + /** The value of the constant. */ + def constantValue: Value; +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/expression/Default.scala b/src/library/scala/dbc/statement/expression/Default.scala new file mode 100644 index 0000000000..2b84a6ce4f --- /dev/null +++ b/src/library/scala/dbc/statement/expression/Default.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +case object Default extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = "DEFAULT"; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/expression/Field.scala b/src/library/scala/dbc/statement/expression/Field.scala new file mode 100644 index 0000000000..55b7c96053 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/Field.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class Field extends Expression { + + /** The name of the schema in the database where the field is located. */ + def schemaName: Option[String] = None; + + /** The name of the table in the database where the field is located. */ + def tableName: Option[String]; + + /** The name of the field in the database. */ + def fieldName: String; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = ( + (schemaName match { + case None => "" + case Some(sn) => sn + "." + }) + + (tableName match { + case None => "" + case Some(tn) => tn + "." + }) + fieldName + ) + +} diff --git a/src/library/scala/dbc/statement/expression/FunctionCall.scala b/src/library/scala/dbc/statement/expression/FunctionCall.scala new file mode 100644 index 0000000000..030b05812b --- /dev/null +++ b/src/library/scala/dbc/statement/expression/FunctionCall.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +case class FunctionCall ( + functionName: String, + arguments: List[Expression] +) extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + functionName + "(" + arguments.mkString("",", ","") + ")" + } + + /** The name of the function to call. */ + //def functionName: String; + + /** A list of all argument expressions to pass to the function, in order. */ + //def arguments: List[Expression]; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/expression/Select.scala b/src/library/scala/dbc/statement/expression/Select.scala new file mode 100644 index 0000000000..bce9a5d002 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/Select.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class Select extends Expression { + + /** The actual select statement */ + def selectStatement: statement.Select; + + /** A SQL-99 compliant string representation of the expression. */ + override def sqlString: String = selectStatement.sqlString; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = "("+selectStatement.sqlString+")"; + +} diff --git a/src/library/scala/dbc/statement/expression/SetFunction.scala b/src/library/scala/dbc/statement/expression/SetFunction.scala new file mode 100644 index 0000000000..6a3ba37d2d --- /dev/null +++ b/src/library/scala/dbc/statement/expression/SetFunction.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class SetFunction { + /** A SQL-99 compliant string representation of the set quantifier. */ + def sqlString: String; +} + +object SetFunction { + abstract class Asterisk extends SetFunction { + def sqlString = "(*)"; + } + abstract class General extends SetFunction { + def setQuantifier: Option[SetQuantifier]; + def valueExpression: Expression; + def sqlString = ( + "(" + + (setQuantifier match { + case None => "" + case Some(sq) => sq.sqlString + " " + }) + + valueExpression.sqlString + ")" + ); + } + abstract class Binary extends SetFunction { + def sqlString = error("Binary set function is not supported yet."); + } +} diff --git a/src/library/scala/dbc/statement/expression/TypeCast.scala b/src/library/scala/dbc/statement/expression/TypeCast.scala new file mode 100644 index 0000000000..25d1874381 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/TypeCast.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +case class TypeCast ( + expression: Expression, + castType: DataType +) extends Expression { + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = { + "CAST (" + expression.sqlInnerString + " AS " + castType.sqlString + ")"; + } + + /** The expression that will be casted. */ + //def expression: Expression; + + /** The type to which to cast. */ + //def castType: scala.dbc.datatype.DataType; +}
\ No newline at end of file diff --git a/src/library/scala/dbc/statement/expression/UnaryOperator.scala b/src/library/scala/dbc/statement/expression/UnaryOperator.scala new file mode 100644 index 0000000000..1e7ab028f7 --- /dev/null +++ b/src/library/scala/dbc/statement/expression/UnaryOperator.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.statement.expression; + +abstract class UnaryOperator extends Expression { + + /** The name of the operator */ + def operator: String; + + /** Whether the operator comes before the operand or not. */ + def operatorIsLeft: Boolean; + + /** The operand applied to the operator. */ + def operand: Expression; + + /** A SQL-99 compliant string representation of the relation sub- + * statement. This only has a meaning inside another statement. */ + def sqlInnerString: String = operatorIsLeft match { + case true => operator + " " + operand.sqlInnerString; + case false => operand.sqlInnerString + " " + operator; + } +} diff --git a/src/library/scala/dbc/syntax/DataTypeUtil.scala b/src/library/scala/dbc/syntax/DataTypeUtil.scala new file mode 100644 index 0000000000..1df3d20317 --- /dev/null +++ b/src/library/scala/dbc/syntax/DataTypeUtil.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.syntax; + +import java.math.BigDecimal; +import java.math.BigInteger; + +object DataTypeUtil { + + final val java_lang_Integer_SIZE = 32; + final val java_lang_Long_SIZE = 64; + + def boolean = new datatype.Boolean; + def tinyint = new datatype.ExactNumeric[Byte](dbc.DataType.BYTE) { + val precisionRadix = 2; + val precision = 8; + val signed = true; + val scale = 0; + } + def smallint = new datatype.ExactNumeric[Short](dbc.DataType.SHORT) { + val precisionRadix = 2; + val precision = 16; + val signed = true; + val scale = 0; + } + def integer = new datatype.ExactNumeric[Int](dbc.DataType.INT) { + val precisionRadix = 2; + val precision = 32; + val signed = true; + val scale = 0; + } + def bigint = new datatype.ExactNumeric[Long](dbc.DataType.LONG) { + val precisionRadix = 2; + val precision = 64; + val signed = true; + val scale = 0; + } + def numeric (_precision:Int): DataType = numeric(_precision,0); + def numeric (_precision:Int, _scale:Int): DataType = + Pair(datatype.Factory.bytePrecision(_precision,true,true),_scale == 0) match { + case Pair(bp,true) if (bp <= java_lang_Integer_SIZE) => + new datatype.ExactNumeric[Int](DataType.INT) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(bp,true) if (bp <= java_lang_Long_SIZE) => + new datatype.ExactNumeric[Long](DataType.LONG) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(_,true) => + new datatype.ExactNumeric[BigInteger](DataType.BIG_INTEGER) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = 0; + } + case Pair(_,false) => + new datatype.ExactNumeric[BigDecimal](DataType.BIG_DECIMAL) { + val precisionRadix = 10; + val precision = _precision; + val signed = true; + val scale = _scale; + } + } + def real = new datatype.ApproximateNumeric[Float](DataType.FLOAT) { + val precisionRadix = 2; + val precision = 64; + val signed = true; + } + def doublePrecision = new datatype.ApproximateNumeric[Double](DataType.DOUBLE) { + val precisionRadix = 2; + val precision = 128; + val signed = true; + } + def character (_length: Int) = new datatype.Character { + val length = _length; + } + def characterVarying (_length: Int) = new datatype.CharacterVarying { + def length = _length; + } + def characterLargeObject = new datatype.CharacterLargeObject; + +} diff --git a/src/library/scala/dbc/syntax/Database.scala b/src/library/scala/dbc/syntax/Database.scala new file mode 100644 index 0000000000..d819931e95 --- /dev/null +++ b/src/library/scala/dbc/syntax/Database.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API tests ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.syntax; + +import java.net.URI; + +object Database { + + def database (server:String, username:String, password:String): dbc.Database = { + val uri = new URI(server); + // Java 1.5 if (uri.toString().contains("postgres")) { + if (uri.toString().indexOf("postgres") != -1) { + new dbc.Database(new vendor.PostgreSQL { + val uri = new URI(server); + val user = username; + val pass = password; + }) + } else { + throw new Exception("No DBMS vendor support could be found for the given URI"); + } + } + +} diff --git a/src/library/scala/dbc/syntax/Statement.scala b/src/library/scala/dbc/syntax/Statement.scala new file mode 100644 index 0000000000..c4dd2e7434 --- /dev/null +++ b/src/library/scala/dbc/syntax/Statement.scala @@ -0,0 +1,270 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.syntax; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import StatementExpression._; + +/* + +ASSUMPTIONS: + +IMPROVABLE: +For type safety, all types must be defined. If one is missing, none is taken into account. +It is possible to redefine many types or renamings for a field, in that case, + only the last one is taken into account ("a" as "b" as "c" of boolean as "e" of integer + is equivalent to "a" as "e" of integer). + +FIXED: + +*/ + +object Statement { + + // SELECT ZYGOTE ... + + def select: SelectZygote = new SelectZygote { + val setQuantifier = None; + } + def selectBag: SelectZygote = new SelectZygote { + val setQuantifier = Some(statement.SetQuantifier.AllTuples); + } + def selectSet: SelectZygote = new SelectZygote { + val setQuantifier = Some(statement.SetQuantifier.DistinctTuples); + } + + abstract class SelectZygote { + def setQuantifier: Option[statement.SetQuantifier]; + def fields (sdc:SelectDerivedColumns): SelectOf = new SelectOf { + val setQuantifier = SelectZygote.this.setQuantifier; + val selectList = sdc.selectList; + val selectTypes = sdc.selectTypes; + } + } + + abstract class SelectDerivedField { + def fieldValue: StatementField; + def fieldRename: Option[String] = {val x = None; x} + def fieldType: Option[dbc.DataType] = {val x = None; x} + def as (rename:String): SelectDerivedField = new SelectDerivedField { + val fieldValue = SelectDerivedField.this.fieldValue; + override val fieldRename = Some(rename); + override val fieldType = SelectDerivedField.this.fieldType; + } + def of (datatype:dbc.DataType): SelectDerivedField = new SelectDerivedField { + val fieldValue = SelectDerivedField.this.fieldValue; + override val fieldRename = SelectDerivedField.this.fieldRename; + override val fieldType = Some(datatype); + } + } + + implicit def view (fv:StatementField): SelectDerivedField = new SelectDerivedField { + val fieldValue = fv; + } + + implicit def view (fv:String): SelectDerivedField = new SelectDerivedField { + val fieldValue: StatementField = StatementExpression.view(fv); + } + + abstract class SelectDerivedColumns { + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def and (sdc:SelectDerivedColumns): SelectDerivedColumns = new SelectDerivedColumns { + val selectList = SelectDerivedColumns.this.selectList ::: sdc.selectList; + val selectTypes = + if (SelectDerivedColumns.this.selectTypes.isEmpty | sdc.selectTypes.isEmpty) + Nil + else + SelectDerivedColumns.this.selectTypes ::: sdc.selectTypes; + } + } + + implicit def view (sdf:SelectDerivedField): SelectDerivedColumns = new SelectDerivedColumns { + val selectList = List(new statement.DerivedColumn { + val valueExpression = sdf.fieldValue.toStatement; + val asClause = sdf.fieldRename; + }); + val selectTypes = if (sdf.fieldType.isEmpty) Nil else List(sdf.fieldType.get); + } + + implicit def view (sdfs:String): SelectDerivedColumns = { + val sdf: SelectDerivedField = sdfs; + view(sdf); + } + + // SELECT OF ... + + abstract class SelectOf { + def setQuantifier: Option[statement.SetQuantifier]; + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def from (sst:SelectSourceTables): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectOf.this.setQuantifier; + val selectList = SelectOf.this.selectList; + val selectTypes = SelectOf.this.selectTypes; + val fromClause = sst.fromClause; + val whereClause = None; + val groupByClause = None; + val havingClause = None; + } + } + + abstract class SelectSourceTable { + def fromRelation: statement.Relation; + def innerJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Inner; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def leftOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Left; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def rightOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Right; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + def fullOuterJoin (sst: SelectSourceTable): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Jointure { + val leftRelation = SelectSourceTable.this.fromRelation; + val rightRelation = sst.fromRelation; + val joinType = statement.JoinType.Outer.Full; + val joinCondition = None; + val fieldTypes = leftRelation.fieldTypes ::: rightRelation.fieldTypes; + } + } + } + + implicit def view (sct:String): SelectSourceTable = new SelectSourceTable { + val fromRelation = new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + } + } + + implicit def view (sct:statement.Select): SelectSourceTable = new SelectSourceTable { + val fromRelation = sct; + } + + abstract class SelectSourceTables { + def fromClause: List[statement.Relation]; + def join (sct:SelectSourceTable): SelectSourceTables = new SelectSourceTables { + val fromClause = SelectSourceTables.this.fromClause ::: List(sct.fromRelation); + } + } + + implicit def view (sct:String): SelectSourceTables = new SelectSourceTables { + val fromClause = List(new statement.Table { + val tableName = sct; + val tableRename = None; + val fieldTypes = Nil; + }); + } + + implicit def view (sct:statement.Select): SelectSourceTables = new SelectSourceTables { + val fromClause = List(sct); + } + + implicit def view (sct:SelectSourceTable): SelectSourceTables = new SelectSourceTables { + val fromClause = List(sct.fromRelation); + } + + // SELECT BEYOND ... + + abstract class SelectBeyond { + def setQuantifier: Option[statement.SetQuantifier]; + def selectList: List[statement.DerivedColumn]; + def selectTypes: List[DataType]; + def fromClause: List[statement.Relation]; + def whereClause: Option[statement.Expression]; + def groupByClause: Option[List[statement.Expression]]; + def havingClause: Option[statement.Expression]; + def where (se:StatementExpression): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = Some(se.toStatement); + val groupByClause = SelectBeyond.this.groupByClause; + val havingClause = SelectBeyond.this.havingClause; + } + def groupBy (sgb:SelectGroupBy): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = SelectBeyond.this.whereClause; + val groupByClause = Some(sgb.groupByClause); + val havingClause = SelectBeyond.this.havingClause; + } + def having (se:StatementExpression): SelectBeyond = new SelectBeyond { + val setQuantifier = SelectBeyond.this.setQuantifier; + val selectList = SelectBeyond.this.selectList; + val selectTypes = SelectBeyond.this.selectTypes; + val fromClause = SelectBeyond.this.fromClause; + val whereClause = SelectBeyond.this.whereClause; + val groupByClause = SelectBeyond.this.groupByClause; + val havingClause = Some(se.toStatement); + } + } + + implicit def view (sb:SelectBeyond): statement.Select = new statement.Select { + val setQuantifier = sb.setQuantifier; + val selectList = sb.selectList; + val fromClause = sb.fromClause; + val whereClause = sb.whereClause; + val groupByClause = sb.groupByClause; + val havingClause = sb.havingClause; + val fieldTypes = sb.selectTypes; + } + + abstract class SelectGroupBy { + def groupByClause: List[statement.Expression]; + def then (se:StatementExpression): SelectGroupBy = new SelectGroupBy { + val groupByClause = + SelectGroupBy.this.groupByClause ::: List(se.toStatement); + } + def then (se:String): SelectGroupBy = new SelectGroupBy { + val groupByClause = + SelectGroupBy.this.groupByClause ::: List(new statement.expression.Field { + val tableName = None; + val fieldName = se; + }); + } + } + + implicit def view (se:StatementExpression): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(se.toStatement); + } + + implicit def view (se:String): SelectGroupBy = new SelectGroupBy { + val groupByClause = List(new statement.expression.Field { + val tableName = None; + val fieldName = se; + }); + } + +} diff --git a/src/library/scala/dbc/syntax/StatementExpression.scala b/src/library/scala/dbc/syntax/StatementExpression.scala new file mode 100644 index 0000000000..e03be2a476 --- /dev/null +++ b/src/library/scala/dbc/syntax/StatementExpression.scala @@ -0,0 +1,217 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.syntax; + +import java.math.BigDecimal; +import java.math.BigInteger; + +abstract class StatementExpression { + + def toStatement: statement.Expression; + + def and (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "AND"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def or (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "OR"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def == (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def < (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def > (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = ">"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def <= (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def >= (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = ">="; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def <> (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "<>"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def isNull: StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "IS NULL"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def isNotNull: StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "IS NOT NULL"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def + (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "+"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def - (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "-"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def * (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "*"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def / (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "/"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def % (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "%"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def ^ (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "^"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def not : StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "!"; + val operatorIsLeft = false; + val operand = StatementExpression.this.toStatement; + } + } + def || (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "||"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def like (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "LIKE"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def similar (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "SIMILAR"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = se.toStatement; + } + } + def in (se:statement.Select): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.BinaryOperator { + val operator = "IN"; + val leftOperand = StatementExpression.this.toStatement; + val rightOperand = new statement.expression.Select { + val selectStatement = se; + }; + } + } + +} + +object StatementExpression { + + def not (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "NOT"; + val operatorIsLeft = true; + val operand = se.toStatement; + } + } + def abs (se:StatementExpression): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "@"; + val operatorIsLeft = true; + val operand = se.toStatement; + } + } + def exists (se:statement.Select): StatementExpression = new StatementExpression { + val toStatement = new statement.expression.UnaryOperator { + val operator = "EXISTS"; + val operatorIsLeft = true; + val operand = new statement.expression.Select { + val selectStatement = se; + }; + } + } + + abstract class StatementField extends StatementExpression { + def fieldName: String; + def tableName: Option[String] = None; + def in (tn:String): StatementField = new StatementField { + val fieldName = StatementField.this.fieldName; + override val tableName = Some(tn); + } + def toStatement: statement.expression.Field = new statement.expression.Field { + override val schemaName = None; + val tableName = StatementField.this.tableName; + val fieldName = StatementField.this.fieldName; + } + } + + implicit def view (ef:String): StatementField = new StatementField { + val fieldName = ef; + } + + + + +} diff --git a/src/library/scala/dbc/value/ApproximateNumeric.scala b/src/library/scala/dbc/value/ApproximateNumeric.scala new file mode 100644 index 0000000000..4b0c495f54 --- /dev/null +++ b/src/library/scala/dbc/value/ApproximateNumeric.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +abstract class ApproximateNumeric [Type] extends Value { + + val dataType: datatype.ApproximateNumeric[Type]; + + def sqlString = nativeValue.toString(); + + } + +object ApproximateNumeric { + + def view (obj:value.ApproximateNumeric[Float]): Float = obj.nativeValue; + def view (obj:value.ApproximateNumeric[Double]): Double = obj.nativeValue; + +} diff --git a/src/library/scala/dbc/value/Boolean.scala b/src/library/scala/dbc/value/Boolean.scala new file mode 100644 index 0000000000..fa20a066af --- /dev/null +++ b/src/library/scala/dbc/value/Boolean.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +abstract class Boolean extends Value { + + val dataType: datatype.Boolean; + + def sqlString = if (nativeValue) "TRUE" else "FALSE"; + +} + +object Boolean { + + def view (obj:value.Boolean): scala.Boolean = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/Character.scala b/src/library/scala/dbc/value/Character.scala new file mode 100644 index 0000000000..1d5433b991 --- /dev/null +++ b/src/library/scala/dbc/value/Character.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +/** A SQL-99 value of type character string. */ +abstract class Character extends Value { + + override val dataType: datatype.Character; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object Character { + + /** A character string value as a native string. */ + def view (obj:value.Character): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/CharacterLargeObject.scala b/src/library/scala/dbc/value/CharacterLargeObject.scala new file mode 100644 index 0000000000..c4fb7097e0 --- /dev/null +++ b/src/library/scala/dbc/value/CharacterLargeObject.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +/** A SQL-99 value of type character large object. */ +abstract class CharacterLargeObject extends Value { + + override val dataType: datatype.CharacterLargeObject; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object CharacterLargeObject { + + /** A character large object value as a native string. */ + def view (obj:value.CharacterLargeObject): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/CharacterVarying.scala b/src/library/scala/dbc/value/CharacterVarying.scala new file mode 100644 index 0000000000..61142e536c --- /dev/null +++ b/src/library/scala/dbc/value/CharacterVarying.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +/** A SQL-99 value of type character varying string. */ +abstract class CharacterVarying extends Value { + + override val dataType: datatype.CharacterVarying; + + /** An SQL-99 compliant string representation of the value. */ + def sqlString: String = { + "'" + nativeValue + "'" + } + +} + +/** An object offering transformation methods (views) on the value. + * This object must be visible in an expression to use value auto- + * conversion. */ +object CharacterVarying { + + /** A character varying string value as a native string. */ + def view (obj:value.CharacterVarying): String = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/Conversion.scala b/src/library/scala/dbc/value/Conversion.scala new file mode 100644 index 0000000000..5c5dda4220 --- /dev/null +++ b/src/library/scala/dbc/value/Conversion.scala @@ -0,0 +1,153 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +import java.math._; + +object Conversion { + + class Illegal (msg:String) extends Exception(msg); + + implicit def view (value:Value): Byte = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to byte: "+value) + } + } + + implicit def view (value:Value): Short = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to short: "+value) + } + } + + implicit def view (value:Value): Int = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to int: "+value) + } + } + + implicit def view (value:Value): Long = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to long: "+value) + } + } + + implicit def view (value:Value): BigInteger = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + new BigInteger(v.nativeValue.toString(),10) + } else if (value.dataType.nativeTypeId == DataType.BIG_INTEGER) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigInteger]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to big integer: "+value) + } + } + + implicit def view (value:Value): BigDecimal = { + if (value.dataType.nativeTypeId == DataType.BYTE) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Byte]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.SHORT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Short]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.INT) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Int]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.LONG) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[Long]]; + new BigDecimal(v.nativeValue.toString()) + } else if (value.dataType.nativeTypeId == DataType.BIG_INTEGER) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigInteger]]; + new BigDecimal(v.nativeValue) + } else if (value.dataType.nativeTypeId == DataType.BIG_DECIMAL) { + val v = value.asInstanceOf[dbc.value.ExactNumeric[BigDecimal]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to big decimal: "+value) + } + } + + implicit def view (value:Value): Float = { + if (value.dataType.nativeTypeId == DataType.FLOAT) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Float]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to float: "+value) + } + } + + implicit def view (value:Value): Double = { + if (value.dataType.nativeTypeId == DataType.FLOAT) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Float]]; + v.nativeValue.coerce + } else if (value.dataType.nativeTypeId == DataType.DOUBLE) { + val v = value.asInstanceOf[dbc.value.ApproximateNumeric[Double]]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to double: "+value) + } + } + + implicit def view (value:Value): scala.Boolean = { + if (value.dataType.nativeTypeId == DataType.BOOLEAN) { + val v = value.asInstanceOf[dbc.value.Boolean]; + v.nativeValue + } else { + throw new Illegal("Cannot convert value to boolean: "+value) + } + } + + implicit def view (value:Value): String = value match { + case v:dbc.value.Character => v.nativeValue; + case v:dbc.value.CharacterLargeObject => v.nativeValue; + case v:dbc.value.CharacterVarying => v.nativeValue; + case _ => throw new Illegal("Cannot convert value to string") + } + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/ExactNumeric.scala b/src/library/scala/dbc/value/ExactNumeric.scala new file mode 100644 index 0000000000..f3e1a54aac --- /dev/null +++ b/src/library/scala/dbc/value/ExactNumeric.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +import java.math.BigInteger; +import java.math.BigDecimal; + +abstract class ExactNumeric [Type] extends Value { + + val dataType: datatype.ExactNumeric[Type]; + + def sqlString = nativeValue.toString(); + +} + +object ExactNumeric { + + def view (obj:value.ExactNumeric[Byte]): Byte = obj.nativeValue; + def view (obj:value.ExactNumeric[Short]): Short = obj.nativeValue; + def view (obj:value.ExactNumeric[Int]): Int = obj.nativeValue; + def view (obj:value.ExactNumeric[Long]): Long = obj.nativeValue; + def view (obj:value.ExactNumeric[BigInteger]): BigInteger = obj.nativeValue; + def view (obj:value.ExactNumeric[BigDecimal]): BigDecimal = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/value/Factory.scala b/src/library/scala/dbc/value/Factory.scala new file mode 100644 index 0000000000..b24f985354 --- /dev/null +++ b/src/library/scala/dbc/value/Factory.scala @@ -0,0 +1,91 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +import java.math.BigInteger; +import java.math.BigDecimal; + +object Factory { + + def create (result: java.sql.ResultSet, index: Int, expectedDataType: DataType): Value = { + expectedDataType.nativeTypeId match { + case DataType.OBJECT => + new value.Unknown { + val dataType = expectedDataType.asInstanceOf[datatype.Unknown]; + val nativeValue: Object = result.getObject(index); + } + case DataType.STRING => { + expectedDataType match { + case t:datatype.Character => + new value.Character { + val dataType = t; + val nativeValue: String = result.getString(index); + } + case t:datatype.CharacterVarying => + new value.CharacterVarying { + val dataType = t; + val nativeValue: String = result.getString(index); + } + case t:datatype.CharacterLargeObject => + new value.CharacterLargeObject { + val dataType = t; + val nativeValue: String = result.getString(index); + } + } + } + case DataType.BOOLEAN => + new value.Boolean { + val dataType = expectedDataType.asInstanceOf[datatype.Boolean]; + val nativeValue: scala.Boolean = result.getBoolean(index); + } + case DataType.FLOAT => + new value.ApproximateNumeric[Float] { + val dataType = expectedDataType.asInstanceOf[datatype.ApproximateNumeric[Float]]; + val nativeValue: Float = result.getFloat(index); + } + case DataType.DOUBLE => + new value.ApproximateNumeric[Double] { + val dataType = expectedDataType.asInstanceOf[datatype.ApproximateNumeric[Double]]; + val nativeValue: Double = result.getDouble(index); + } + case DataType.BYTE => + new value.ExactNumeric[Byte] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Byte]]; + val nativeValue: Byte = result.getByte(index); + } + case DataType.SHORT => + new value.ExactNumeric[Short] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Short]]; + val nativeValue: Short = result.getShort(index); + } + case DataType.INT => + new value.ExactNumeric[Int] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Int]]; + val nativeValue: Int = result.getInt(index); + } + case DataType.LONG => + new value.ExactNumeric[Long] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[Long]]; + val nativeValue:Long = result.getLong(index); + } + case DataType.BIG_INTEGER => + new value.ExactNumeric[BigInteger] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[BigInteger]]; + val nativeValue: BigInteger = result.getBigDecimal(index).unscaledValue(); + } + case DataType.BIG_DECIMAL => + new value.ExactNumeric[BigDecimal] { + val dataType = expectedDataType.asInstanceOf[datatype.ExactNumeric[BigDecimal]]; + val nativeValue: BigDecimal = result.getBigDecimal(index); + } + + } + } + +} diff --git a/src/library/scala/dbc/value/Unknown.scala b/src/library/scala/dbc/value/Unknown.scala new file mode 100644 index 0000000000..b40df69011 --- /dev/null +++ b/src/library/scala/dbc/value/Unknown.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.value; + +abstract class Unknown extends Value { + + val dataType: datatype.Unknown; + + def sqlString = error ("An 'ANY' value cannot be represented."); + +} + +object UnknownType { + + def view (obj:value.Unknown): Object = obj.nativeValue; + +}
\ No newline at end of file diff --git a/src/library/scala/dbc/vendor/PostgreSQL.scala b/src/library/scala/dbc/vendor/PostgreSQL.scala new file mode 100644 index 0000000000..2653ed714a --- /dev/null +++ b/src/library/scala/dbc/vendor/PostgreSQL.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.dbc.vendor; + +abstract class PostgreSQL extends Vendor { + + def uri:java.net.URI; + def user:String; + def pass:String; + + val retainedConnections = 5; + + val nativeDriverClass = Class.forName("org.postgresql.Driver"); + + val urlProtocolString = "jdbc:postgresql:" + +}
\ No newline at end of file diff --git a/src/library/scala/io/Position.scala b/src/library/scala/io/Position.scala new file mode 100644 index 0000000000..ef61e01317 --- /dev/null +++ b/src/library/scala/io/Position.scala @@ -0,0 +1,92 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.io; + +/** convenience methods to encode line and column number in one + * single integer. The encode line (column) + * numbers range from 0 to LINE_MASK (COLUMN_MASK), where 0 indicates + * that the line (column) is the undefined and 1 represents the first + * line (column). Line (Column) numbers greater than LINE_MASK + * (COLUMN_MASK) are replaced by LINE_MASK (COLUMN_MASK). Furthermore, + * if the encoded line number is LINE_MASK, the column number is + * always set to 0. + + * The following properties hold: + * - the undefined position is 0: encode(0,0) == 0 + * - encodings are non-negative : encode(line,column) >= 0 + * - position order is preserved: + * (line1 < line2) || (line1 == line2 && column1 < column2) + * implies + * encode(line1,column1) <= encode(line2,column2) + * @author Burak Emir (translated from work by Matthias Zengers and others) + */ +object Position { + + /** Number of bits used to encode the line number */ + final val LINE_BITS = 20; + /** Number of bits used to encode the column number */ + final val COLUMN_BITS = 31 - LINE_BITS; // no negatives => 31 + + /** Mask to decode the line number */ + final val LINE_MASK = (1 << LINE_BITS) - 1; + /** Mask to decode the column number */ + final val COLUMN_MASK = (1 << COLUMN_BITS) - 1; + + /** The undefined position */ + final val NOPOS = 0; + + /** The first position in a source file */ + final val FIRSTPOS = encode(1, 1); + + //######################################################################## + // Public Functions + + /** Encodes a position into a single integer. */ + final def encode(line: Int, column: Int): Int = { + var line1, column1 = 0; + if( line < 0 ) + error(line+" < 0"); + if(( line == 0 )&&(column != 0)) + error(line+","+column+" not allowed"); + if( column < 0 ) + error(line+","+column+" not allowed"); + + {if (line >= LINE_MASK) { + line1 = LINE_MASK; + column1 = 0; + } else { + line1 = line; + if (column > COLUMN_MASK) + column1 = COLUMN_MASK; + else + column1 = column + }} + {(line1 << COLUMN_BITS) | column1;} + } + + /** Returns the line number of the encoded position. */ + final def line(pos: Int): Int = { + (pos >> COLUMN_BITS) & LINE_MASK; + } + + /** Returns the column number of the encoded position. */ + final def column(pos: Int): Int = { + pos & COLUMN_MASK; + } + + /** Returns a string representation of the encoded position. */ + def toString(pos: Int): String = { + val sb = new StringBuffer(); + sb.append(line(pos)); + sb.append(':'); + sb.append(column(pos)); + sb.toString(); + } +} diff --git a/src/library/scala/io/Source.scala b/src/library/scala/io/Source.scala new file mode 100644 index 0000000000..a22657e4be --- /dev/null +++ b/src/library/scala/io/Source.scala @@ -0,0 +1,252 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.io; + +import java.io.{ File, FileInputStream, PrintStream }; + +/** convenience methods to create an iterable representation of a source + * file + * @author buraq + */ +object Source { + + /** creates Source from array of bytes, with empty description + */ + def fromBytes(bytes: Array[Byte]): Source = + fromString(new String(bytes)); + + /** creates Source from array of bytes with given encoding, with empty description + */ + def fromBytes(bytes: Array[Byte], enc: String): Source = + fromString(new String(bytes, enc)); + + /** creates Source from a single char */ + def fromChar(c: Char): Source = { + val it = Iterator.single(c); + new Source { + def reset = fromChar(c); + val iter = it; + } + } + /** creates Source from array of characters, with empty description + */ + def fromChars(chars: Array[Char]): Source = { + val it = Iterator.fromArray(chars); + new Source { + def reset = fromChars(chars); + val iter = it; + } + } + /** creates Source from string, with empty description + */ + def fromString(s: String): Source = { + val it = Iterator.fromString(s); + new Source { + def reset = fromString(s); + val iter = it; + } + } + + /** creates Source from file with given name, setting its description to + * filename. + */ + def fromFile(name: String): Source = + fromFile( new File( name )); + + /** creates Source from file with given name, using given encoding, setting + * its description to filename. + */ + def fromFile(name: String, enc: String): Source = + fromFile( new File( name ), enc); + + /** creates Source from file with given file: URI + */ + def fromFile(uri: java.net.URI): Source = + fromFile(new File(uri)); + + /** creates Source from file, using default character encoding, setting its + * description to filename. + */ + def fromFile(file: java.io.File): Source = { + val arr: Array[Byte] = new Array[Byte]( file.length().asInstanceOf[Int] ); + val is = new FileInputStream( file ); + is.read( arr ); + val s = fromBytes(arr); + return setFileDescriptor(file,s); + } + + /** creates Source from file, using given character encoding, setting its + * description to filename. + */ + def fromFile(file: java.io.File, enc: String): Source = { + val arr: Array[Byte] = new Array[Byte]( file.length().asInstanceOf[Int] ); + val is = new FileInputStream( file ); + is.read( arr ); + val s = fromBytes(arr, enc); + s.descr = file.getName(); + return setFileDescriptor(file,s); + } + + def setFileDescriptor(file: File, s: Source): Source = { + s.descr = new StringBuffer() + .append( "file:" ) + .append( file.getAbsolutePath() ) + .toString(); + s + } + + def fromURL(s:String): Source = + fromURL(new java.net.URL(s)); + + def fromURL(url: java.net.URL): Source = { + val it = new Iterator[Char] { + var data: Int = _; + def hasNext = {data != -1}; + def next = {val x = data.asInstanceOf[char]; data = bufIn.read(); x} + val in = url.openStream(); + val bufIn = new java.io.BufferedInputStream(in); + data = bufIn.read() + } + val s = new Source { + def reset = fromURL(url); + val iter = it; + }; + s.descr = url.toString(); + s + } + +} + +/** an iterable representation of source files. + * calling method reset returns an identical, resetted source + * + * @author buraq + */ +abstract class Source extends Iterator[Char] { + + + // ------ protected values + + /** the actual iterator */ + protected val iter: Iterator[Char]; + + protected var cline = 1; + protected var ccol = 1; + + // ------ public values + + /** position of last character returned by next*/ + var pos = 0; + + /** the last character returned by next. + * the value before the first call to next is undefined. + */ + var ch: Char = _; + + /** description of this source, default empty */ + var descr: String = ""; + + var nerrors = 0; + var nwarnings = 0; + + /** default col increment for tabs '\t', set to 4 initially + */ + var tabinc = 4; + + // + // -- methods + // + + /** convenience method, returns given line (not including newline) + * from Source + */ + def getLine(line: Int): String = { + val buf = new StringBuffer(); + val it = reset; + var i = 0; + + while( it.hasNext && i < (line-1)) + if('\n' == it.next) + i = i + 1; + + if(!it.hasNext) { // this should not happen + throw new java.lang.IllegalArgumentException( + "line "+line+" does not exist?!" + ); + } + var ch = it.next; + while(it.hasNext && '\n' != ch) { + buf.append( ch ); + ch = it.next; + } + val res = buf.toString(); + buf.setLength( 0 ); + res + } + + /** returns true if this source has more characters + */ + def hasNext = iter.hasNext; + + /** returns next character and has the following side-effects: updates + * position (ccol and cline) and assigns the character to ch + */ + def next = { + ch = iter.next; + pos = Position.encode(cline,ccol); + ch match { + case '\n' => + ccol = 1; + cline = cline + 1; + case '\t' => + ccol = ccol + tabinc; + case _ => + ccol = ccol + 1; + } + ch + }; + + + /** reports an error message to console */ + def reportError(pos: Int, msg: String): Unit = { + report(pos, msg, java.lang.System.out); + } + + def reportError(pos: Int, msg: String, out: PrintStream): Unit = { + nerrors = nerrors + 1; + report(pos, msg, java.lang.System.out); + } + + def report(pos: Int, msg: String, out: PrintStream): Unit = { + val line = Position.line(pos); + val col = Position.column(pos); + Console.println(descr+":"+line+":"+col+": "+msg); + Console.println(getLine(line)); + var i = 1; + while( i < col ) { + Console.print(' '); + i = i + 1; + } + Console.println('^'); + } + + /** reports a warning message to java.lang.System.out */ + def reportWarning(pos: Int, msg: String): Unit = + reportWarning(pos, msg, java.lang.System.out); + + def reportWarning(pos: Int, msg: String, out: PrintStream): Unit = { + nwarnings = nwarnings + 1; + report(pos, "warning! "+msg, out); + } + + /** the actual reset method */ + def reset: Source; + +} diff --git a/src/library/scala/mobile/Code.scala b/src/library/scala/mobile/Code.scala new file mode 100644 index 0000000000..1ce9997a3d --- /dev/null +++ b/src/library/scala/mobile/Code.scala @@ -0,0 +1,234 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.mobile; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + + +/** The class <code>Code</code> provides <code>apply</code> methods + * with different arities (actually up to 9 parameters) to invoke + * a function simply by specifying its name and argument types.<p/> + * + * Example:<pre> + * <b>val</b> url = <b>new</b> URL("http://scala.epfl.ch/classes/examples.jar"); + * <b>val</b> obj = <b>new</b> Location(url) create "examples.sort"; + * <b>val</b> ar = Array(6, 2, 8, 5, 1); + * obj[Array[Int], Unit]("println")(ar); + * obj[Array[Int], Unit]("sort")(ar); + * obj[Array[Int], Unit]("println")(ar);</pre> + * + * @see <a href="Location-class.html">Location</a> + * + * @author Stephane Micheloud + * @version 1.0, 04/05/2004 + */ +class Code(clazz: java.lang.Class) { + + private type JObject = java.lang.Object; + + private var instance: JObject = _; + + ///////////////////////////// apply methods /////////////////////////////// + + def apply[R](funName: String) = + () => { + val args = Predef.Array[JObject](); + val types = Predef.Array[Class](); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, R](funName: String) = + (_0: A0) => { + val p = boxValue(_0); + val args = Predef.Array(p._1); + val types = Predef.Array(p._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, R](funName: String) = + (_0: A0, _1: A1) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val args = Predef.Array(p0._1, p1._1); + val types = Predef.Array(p0._2, p1._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, R](funName: String) = + (_0: A0, _1: A1, _2: A2) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val args = Predef.Array(p0._1, p1._1, p2._1); + val types = Predef.Array(p0._2, p1._2, p2._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, A4, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3, _4: A4) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val p4 = boxValue(_4); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1, p4._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2, p4._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, A4, A5, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3, _4: A4, _5: A5) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val p4 = boxValue(_4); + val p5 = boxValue(_5); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1, p4._1, p5._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2, p4._2, p5._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, A4, A5, A6, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3, _4: A4, _5: A5, _6: A6) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val p4 = boxValue(_4); + val p5 = boxValue(_5); + val p6 = boxValue(_6); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1, p4._1, p5._1, p6._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2, p4._2, p5._2, p6._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, A4, A5, A6, A7, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3, _4: A4, _5: A5, _6: A6, _7: A7) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val p4 = boxValue(_4); + val p5 = boxValue(_5); + val p6 = boxValue(_6); + val p7 = boxValue(_7); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1, p4._1, p5._1, p6._1, p7._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2, p4._2, p5._2, p6._2, p7._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + def apply[A0, A1, A2, A3, A4, A5, A6, A7, A8, R](funName: String) = + (_0: A0, _1: A1, _2: A2, _3: A3, _4: A4, _5: A5, _6: A6, _7: A7, _8: A8) => { + val p0 = boxValue(_0); + val p1 = boxValue(_1); + val p2 = boxValue(_2); + val p3 = boxValue(_3); + val p4 = boxValue(_4); + val p5 = boxValue(_5); + val p6 = boxValue(_6); + val p7 = boxValue(_7); + val p8 = boxValue(_8); + val args = Predef.Array(p0._1, p1._1, p2._1, p3._1, p4._1, p5._1, p6._1, p7._1, p8._1); + val types = Predef.Array(p0._2, p1._2, p2._2, p3._2, p4._2, p5._2, p6._2, p7._2, p8._2); + applyFun(funName, args, types).asInstanceOf[R]; + }; + + ////////////////////// private functions /////////////////////// + + private def boxValue(value: Any) = value match { + case x: Byte => Pair(new java.lang.Byte(x), java.lang.Byte.TYPE) + case x: Boolean => Pair(new java.lang.Boolean(x), java.lang.Boolean.TYPE) + case x: Char => Pair(new java.lang.Character(x), java.lang.Character.TYPE) + case x: Short => Pair(new java.lang.Short(x), java.lang.Short.TYPE) + case x: Int => Pair(new java.lang.Integer(x), java.lang.Integer.TYPE) + case x: Long => Pair(new java.lang.Long(x), java.lang.Long.TYPE) + case x: Float => Pair(new java.lang.Float(x), java.lang.Float.TYPE) + case x: Double => Pair(new java.lang.Double(x), java.lang.Double.TYPE) + case _ => + val x = value.asInstanceOf[JObject]; + Pair(x, x.getClass()) + } + + private def isConstructorName(methName: String) = { + var className = clazz.getName(); + val classInx = className.lastIndexOf("."); + val methInx = methName.lastIndexOf("."); + if (classInx > 0 && methInx < 0) + className = className.substring(classInx + 1, className.length()); + methName.equals(className) + } + + private def applyFun(methName: String, args: Array[JObject], argTypes: Array[Class]): JObject = { + try { + val method = clazz.getMethod(methName, argTypes); + var obj: JObject = null; + if (! Modifier.isStatic(method.getModifiers())) { + if (instance == null) { + instance = try { + clazz.newInstance(); + } catch { case _ => + val cs = clazz.getConstructors(); +//Console.println("cs.length=" + cs.length); + if (cs.length > 0) { + cs(0).newInstance(Predef.Array("")) + } else { + error("class " + clazz.getName() + " has no public constructor"); + null + } + } + } + obj = instance; + } + val result = method.invoke(obj, args); + if (result == null) ().asInstanceOf[JObject] else result + } + catch { + case me: NoSuchMethodException => + if (isConstructorName(methName)) { + try { + val cstr = clazz.getConstructor(argTypes); + instance = cstr.newInstance(args); + instance + } + catch { + case e: Exception => + Console.println(e.getMessage()); + e.printStackTrace(); + } + } + else { + Console.println(me.getMessage()); + me.printStackTrace(); + } + null + case e: Exception => + Console.println(e.getMessage()); + e.printStackTrace(); + null + } + } + +} + diff --git a/src/library/scala/mobile/Location.scala b/src/library/scala/mobile/Location.scala new file mode 100644 index 0000000000..c6f7d57648 --- /dev/null +++ b/src/library/scala/mobile/Location.scala @@ -0,0 +1,96 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.mobile; + +import java.net._; + +import scala.collection.mutable._; + + +/** The class <code>Location</code> provides a <code>create</code> + * method to instantiate objects from a network location by + * specifying the URL address of the jar/class file.<p/> + * + * An update of the jar/class file should not break your code as far + * as the used class names and method signatures are the same.<p/> + * + * Example:<pre> + * <b>val</b> url = <b>new</b> URL("http://scala.epfl.ch/classes/examples.jar"); + * <b>val</b> obj = <b>new</b> Location(url) create "examples.sort";</pre> + * + * @see <a href="Code-class.html">Code</a> + * + * @author Stephane Micheloud + * @version 1.0, 04/05/2004 + */ +class Location(url: URL) { + + /** A cache containing all class loaders of this location. + */ + private var lcache: Map[URL, ClassLoader] = new HashMap; + + /** The class loader associated with this location. + */ + private val loader = if (url == null) + ClassLoader.getSystemClassLoader() + else + lcache.get(url) match { + case Some(cl) => + cl + case _ => + val cl = new URLClassLoader(Predef.Array(url)); + lcache(url) = cl; + cl + }; + + /** A cache containing all classes of this location. + */ + private var ccache: Map[String, java.lang.Class] = new HashMap; + + /** Return the code description for the string <code>className</code> + * at this location. + * + * @param the name of the class + * @return the code description corresponding to <code>className</code> + */ + def create(className: String) = new Code( + ccache.get(className) match { + case Some(clazz) => + clazz + case _ => + val clazz = if (loader.loadClass(className).isInterface()) { + // Scala source: class A { ... }; + // Java bytecode: interface A.class + class A$class.class + loader.loadClass(className + "$class"); + } + else { + // Scala source: object A { ... }; + // Java bytecode: interface A.class + class A$.class + loader.loadClass(className + "$"); + } + ccache(className) = clazz; + clazz + } + ); + +} + +/** The object <code>Location</code> can be used to instantiate + * objects on the same Java VM. It is just provided to illustrate + * the special case where resources are available locally.<p/> + * + * Example:<pre> + * <b>val</b> obj = Location.create("xcode.Math"); + * <b>val</b> x = obj[Int, Int]("square")(5);</pre> + * + * @author Stephane Micheloud + * @version 1.0, 04/05/2004 + */ +object Location extends Location(null); diff --git a/src/library/scala/reflect/Code.scala b/src/library/scala/reflect/Code.scala new file mode 100644 index 0000000000..b354bd753f --- /dev/null +++ b/src/library/scala/reflect/Code.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.reflect; + +abstract class Code; + +case class Ident(sym: Symbol) extends Code; +case class Select(qual: Code, sym: Symbol) extends Code; +case class Literal(value: Any) extends Code; +case class Apply(fun: Code, args: List[Code]) extends Code; +case class TypeApply(fun: Code, args: List[Type]) extends Code; +case class Function(params: List[Symbol], body: Code) extends Code; +case class This(sym: Symbol) extends Code; diff --git a/src/library/scala/reflect/Print.scala b/src/library/scala/reflect/Print.scala new file mode 100644 index 0000000000..decfe933cc --- /dev/null +++ b/src/library/scala/reflect/Print.scala @@ -0,0 +1,113 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.reflect; + +object Print extends Function1[Any, String] { + + def apply (any: Any): String = { + if (any.isInstanceOf[TypedCode[Any]]) + apply(any.asInstanceOf[TypedCode[Any]]) + else if (any.isInstanceOf[Code]) + apply(any.asInstanceOf[Code]) + else if (any.isInstanceOf[Symbol]) + apply(any.asInstanceOf[Symbol]) + else if (any.isInstanceOf[Type]) + apply(any.asInstanceOf[Type]) + else "UnknownAny" + } + + def apply (typedCode: TypedCode[Any]): String = + Print(typedCode.code); + + def apply (code: Code): String = code match { + case reflect.Ident(sym) => + "Ident (" + Print(sym) + ")" + case reflect.Select(qual, sym) => + "Select (" + Print(qual) + " from " + Print(sym) + ")" + case reflect.Literal(value) => + "Literal (" + value + ")" + case reflect.Apply(fun, args) => + ("Apply (" + Print(fun) + " on " + + ((args match { + case Nil => "nothing " + case _ :: _ => args.map(Print).mkString("", ", ", "") + }):String) + ")") + case reflect.TypeApply(fun, args) => + ("TypeApply (" + Print(fun) + " on " + + ((args match { + case Nil => "nothing" + case _ :: _ => args.map(Print).mkString("", ", ", "") + }):String) + ")") + case reflect.Function(params, body) => + ("Function (" + + ((params match { + case Nil => "nothing" + case _ :: _ => params.map(Print).mkString("", ", ", "") + }):String) + " is " + Print(body) + ")") + case reflect.This(sym) => + "This (" + Print(sym) + ")" + case _ => "UnknownCode" + } + + def apply (symbol: Symbol): String = symbol match { + case reflect.Class(name) => + "Class (" + name + ")" + case reflect.Method(name, datatype) => + "Method (" + name + " of " + Print(datatype) + ")" + case reflect.Field(name, datatype) => + "Field (" + name + " of " + Print(datatype) + ")" + case reflect.TypeField(name, datatype) => + "TypeField (" + name + " of " + Print(datatype) + ")" + case reflect.LocalValue(owner, name, datatype) => + ("LocalValue (" + name + " owned by " + Print(owner) + + " of " + Print(datatype) + ")") + case reflect.LocalMethod(owner, name, datatype) => + ("LocalMethod (" + name + " owned by " + Print(owner) + + " of " + Print(datatype) + ")") + case reflect.NoSymbol => "NoSymbol" + case reflect.RootSymbol => "RootSymbol" + case _ => "UnknownSymbol" + } + + def apply (datatype: Type): String = datatype match { + case reflect.NoPrefix => "NoPrefix" + case reflect.NoType => "NoType" + case reflect.NamedType(name) => + "NamedType (" + name + ")" + case reflect.PrefixedType(prefix, symbol) => + "PrefixedType (" + Print(symbol) + " in " + Print(prefix) + ")" + case reflect.SingleType(prefix, symbol) => + "SingleType (" + Print(symbol) + " in " + Print(prefix) + ")" + case reflect.ThisType(clazz) => + "ThisType (" + Print(clazz) + ")" + case reflect.AppliedType(datatype, args) => + ("AppliedType (" + Print(datatype) + " on " + + ((args match { + case Nil => "nothing" + case _ :: _ => args.map(Print).mkString("", ", ", "") + }):String) + ")") + case reflect.TypeBounds(lo, hi) => + "TypeBounds (" + Print(lo) + " to " + Print(hi) + ")" + case reflect.MethodType(formals, resultType) => + ("MethodType (" + + ((formals match { + case Nil => "nothing" + case _ :: _ => formals.map(Print).mkString("", ", ", "") + }):String) + " is " + Print(resultType) + ")") + case reflect.PolyType(typeParams, typeBounds, resultType) => + ("PolyType (" + (typeParams zip typeBounds).map{ + case Pair(typeParam, Pair(lo, hi)) => + Print(lo) + " < " + Print(typeParam) + " < " + Print(hi) + }.mkString("", ", ", "") + " to " + Print(resultType) + ")") + case _ => "UnknownType" + } + +} diff --git a/src/library/scala/reflect/Symbol.scala b/src/library/scala/reflect/Symbol.scala new file mode 100644 index 0000000000..14be8e9317 --- /dev/null +++ b/src/library/scala/reflect/Symbol.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.reflect; + +abstract class Symbol { + val owner: Symbol; + val name: String; + val tpe: Type; +} + +abstract class GlobalSymbol(val fullname: String) extends Symbol { + private val pointIndex = fullname.lastIndexOf('.'); + val owner: Symbol = + if (pointIndex < 0) RootSymbol else Class(fullname.substring(0, pointIndex)); + val name: String = + if (pointIndex < 0) fullname else fullname.substring(pointIndex, fullname.length()); +} + +abstract class LocalSymbol extends Symbol {} + +case class Class(override val fullname: String) extends GlobalSymbol(fullname) { + val tpe = NamedType(fullname); +} + +case class Method(override val fullname: String, tpe: Type) extends GlobalSymbol(fullname); + +case class Field(override val fullname: String, tpe: Type) extends GlobalSymbol(fullname); + +case class TypeField(override val fullname: String, tpe: Type) extends GlobalSymbol(fullname); + +case class LocalValue(owner: Symbol, name: String, tpe: Type) extends LocalSymbol; + +case class LocalMethod(owner: Symbol, name: String, tpe: Type) extends LocalSymbol; + +case object NoSymbol extends Symbol { + val owner = null; + val name = null; + val tpe = NoType +} + +case object RootSymbol extends Symbol { + val owner = NoSymbol; + val name = "<root>"; + val tpe = NoPrefix +} diff --git a/src/library/scala/reflect/Type.scala b/src/library/scala/reflect/Type.scala new file mode 100644 index 0000000000..c0b86162f6 --- /dev/null +++ b/src/library/scala/reflect/Type.scala @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.reflect; + +abstract class Type; + +case object NoPrefix extends Type; +case object NoType extends Type; + +/** fullname */ +case class NamedType(fullname: String) extends Type; + +/** pre # sym */ +case class PrefixedType(pre: Type, sym: Symbol) extends Type; + +/** pre.type # sym == pre.sym */ +case class SingleType(pre: Type, sym: Symbol) extends Type; + +/** clazz.this */ +case class ThisType(clazz: Symbol) extends Type; + +/** clazz.super[superClazz] */ +/** tpe[args1, ..., argsn] */ +case class AppliedType(tpe: Type, args: List[Type]) extends Type; + +/** [a <: lo >: hi] */ +case class TypeBounds(lo: Type, hi: Type) extends Type; + +/** (formals1 ... formalsn) restpe */ +case class MethodType(formals: List[Type], restpe: Type) extends Type; + +/** */ +case class PolyType(typeParams: List[Symbol], typeBounds: List[Pair[Type, Type]], resultType: Type) extends Type; + +/** */ +class ImplicitMethodType(formals: List[Type], restpe: Type) extends MethodType(formals, restpe); diff --git a/src/library/scala/reflect/TypedCode.scala b/src/library/scala/reflect/TypedCode.scala new file mode 100644 index 0000000000..38ce0512c7 --- /dev/null +++ b/src/library/scala/reflect/TypedCode.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.reflect; + +class TypedCode[T](val code: Code) { +} + diff --git a/src/library/scala/remote.scala b/src/library/scala/remote.scala new file mode 100644 index 0000000000..825cfbdc44 --- /dev/null +++ b/src/library/scala/remote.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:$ +*/ + +package scala; + +class remote extends Attribute {} diff --git a/src/library/scala/runtime/AtomicReference.java b/src/library/scala/runtime/AtomicReference.java new file mode 100644 index 0000000000..7eb4abdb0e --- /dev/null +++ b/src/library/scala/runtime/AtomicReference.java @@ -0,0 +1,54 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +/** + * Blocking (i.e. non-atomic) placeholder for Java 1.5's + * <code>java.util.concurrent.atomic.AtomicReference</code> class. + */ + +public class AtomicReference implements java.io.Serializable { + private Object value; + + public AtomicReference(Object value) { + this.value = value; + } + + public Object get() { + return value; + } + + public Object getAndSet(Object update) { + Object previousValue = value; + value = update; + return previousValue; + } + + public void set(Object update) { + value = update; + } + + public synchronized boolean compareAndSet(Object expected, Object update) { + if (value == expected) { + value = update; + return true; + } else + return false; + } + + public boolean weakCompareAndSet(Object expected, Object update) { + return compareAndSet(expected, update); + } + + public String toString() { + return value.toString(); + } +} diff --git a/src/library/scala/runtime/BooleanRef.java b/src/library/scala/runtime/BooleanRef.java new file mode 100644 index 0000000000..f0ebcabe97 --- /dev/null +++ b/src/library/scala/runtime/BooleanRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class BooleanRef implements java.io.Serializable { + public boolean elem; + public BooleanRef(boolean elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/BoxedAnyArray.scala b/src/library/scala/runtime/BoxedAnyArray.scala new file mode 100644 index 0000000000..ac37a62427 --- /dev/null +++ b/src/library/scala/runtime/BoxedAnyArray.scala @@ -0,0 +1,154 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +/** Arrays created by new Array[T](length) where T is a type variable + */ +[serializable] +final class BoxedAnyArray(val length: Int) extends BoxedArray { + + private var boxed = new Array[Object](length); + private val hash = boxed.hashCode(); + private var unboxed: Object = null; + private var elemTag: String = null; + + def apply(index: Int): Object = synchronized { + if (unboxed == null) + boxed(index); + else if (elemTag == ScalaRunTime.IntTag) + BoxedInt.box(unboxed.asInstanceOf[Array[Int]](index)) + else if (elemTag == ScalaRunTime.DoubleTag) + BoxedDouble.box(unboxed.asInstanceOf[Array[Double]](index)) + else if (elemTag == ScalaRunTime.FloatTag) + BoxedFloat.box(unboxed.asInstanceOf[Array[Float]](index)) + else if (elemTag == ScalaRunTime.LongTag) + BoxedLong.box(unboxed.asInstanceOf[Array[Long]](index)) + else if (elemTag == ScalaRunTime.CharTag) + BoxedChar.box(unboxed.asInstanceOf[Array[Char]](index)) + else if (elemTag == ScalaRunTime.ByteTag) + BoxedByte.box(unboxed.asInstanceOf[Array[Byte]](index)) + else if (elemTag == ScalaRunTime.ShortTag) + BoxedShort.box(unboxed.asInstanceOf[Array[Short]](index)) + else if (elemTag == ScalaRunTime.BooleanTag) + BoxedBoolean.box(unboxed.asInstanceOf[Array[Boolean]](index)) + else + unboxed.asInstanceOf[Array[Object]](index) + } + + def update(index: Int, elem: Object): Unit = synchronized { + if (unboxed == null) + boxed(index) = elem; + else if (elemTag == ScalaRunTime.IntTag) + unboxed.asInstanceOf[Array[Int]](index) = elem.asInstanceOf[BoxedNumber].intValue() + else if (elemTag == ScalaRunTime.DoubleTag) + unboxed.asInstanceOf[Array[Double]](index) = elem.asInstanceOf[BoxedNumber].doubleValue() + else if (elemTag == ScalaRunTime.FloatTag) + unboxed.asInstanceOf[Array[Float]](index) = elem.asInstanceOf[BoxedNumber].floatValue() + else if (elemTag == ScalaRunTime.LongTag) + unboxed.asInstanceOf[Array[Long]](index) = elem.asInstanceOf[BoxedNumber].longValue() + else if (elemTag == ScalaRunTime.CharTag) + unboxed.asInstanceOf[Array[Char]](index) = elem.asInstanceOf[BoxedNumber].charValue() + else if (elemTag == ScalaRunTime.ByteTag) + unboxed.asInstanceOf[Array[Byte]](index) = elem.asInstanceOf[BoxedNumber].byteValue() + else if (elemTag == ScalaRunTime.ShortTag) + unboxed.asInstanceOf[Array[Short]](index) = elem.asInstanceOf[BoxedNumber].shortValue() + else if (elemTag == ScalaRunTime.BooleanTag) + unboxed.asInstanceOf[Array[Boolean]](index) = elem.asInstanceOf[BoxedBoolean].value + else + unboxed.asInstanceOf[Array[Object]](index) = elem + } + + def unbox(elemTag: String): Object = synchronized { + if (unboxed == null) { + this.elemTag = elemTag; + if (elemTag == ScalaRunTime.IntTag) { + val newvalue = new Array[Int](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].intValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.DoubleTag) { + val newvalue = new Array[Double](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].doubleValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.FloatTag) { + val newvalue = new Array[Float](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].floatValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.LongTag) { + val newvalue = new Array[Long](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].longValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.CharTag) { + val newvalue = new Array[Char](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].charValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.ByteTag) { + val newvalue = new Array[Byte](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].byteValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.ShortTag) { + val newvalue = new Array[Short](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedNumber].shortValue(); + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == ScalaRunTime.BooleanTag) { + val newvalue = new Array[Boolean](length); + var i = 0; + while (i < length) { + newvalue(i) = boxed(i).asInstanceOf[BoxedBoolean].value; + i = i + 1 + } + unboxed = newvalue; + } else if (elemTag == boxed.getClass().getComponentType()) { + unboxed = boxed; + } else { + unboxed = java.lang.reflect.Array.newInstance(Class.forName(elemTag), length); + System.arraycopy(boxed, 0, unboxed, 0, length); + } + boxed = null + } + unboxed + } + + override def equals(other: Any): Boolean = ( + other.isInstanceOf[BoxedAnyArray] && (this eq (other.asInstanceOf[BoxedAnyArray])) || + (if (unboxed == null) boxed == other else unboxed == other) + ); + + override def hashCode(): Int = hash; +} + diff --git a/src/library/scala/runtime/BoxedArray.scala b/src/library/scala/runtime/BoxedArray.scala new file mode 100644 index 0000000000..29b4ccf0e2 --- /dev/null +++ b/src/library/scala/runtime/BoxedArray.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +/** A class representing Array[T] + */ +abstract class BoxedArray extends PartialFunction[Int, Object] with Seq[Object] { + /** The length of the array */ + def length: Int; + + /** The element at given index */ + def apply(index: Int): Object; + + /** Update element at given index */ + def update(index: Int, elem: Object): Unit; + + /** Convert to Java array. + * @param elemTag Either one of the tags ".N" where N is the name of a primitive type + * (@see ScalaRunTime), or a full class name. + */ + def unbox(elemTag: String): Object; + + override def isDefinedAt(x: Int): Boolean = 0 <= x && x < length; + + override def toString(): String = { + val buf = new StringBuffer(); + buf.append("Array("); + val len = length; + var i = 0; + while (i < len) { buf.append(apply(i)); i = i + 1 } + buf.append(")"); + buf.toString() + } + + def elements = new Iterator[Object] { + var index = 0; + def hasNext: Boolean = index < length; + def next: Object = { val i = index; index = i + 1; apply(i) } + } +} diff --git a/src/library/scala/runtime/BoxedBoolean.java b/src/library/scala/runtime/BoxedBoolean.java new file mode 100644 index 0000000000..ad7ec887e4 --- /dev/null +++ b/src/library/scala/runtime/BoxedBoolean.java @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public final class BoxedBoolean + implements java.io.Serializable +{ + + private final static BoxedBoolean TRUE = new BoxedBoolean(true); + private final static BoxedBoolean FALSE = new BoxedBoolean(false); + + public static BoxedBoolean box(boolean value) { + return (value ? TRUE : FALSE); + } + + public final boolean value; + + private BoxedBoolean(boolean value) { this.value = value; } + + public final boolean booleanValue() { return value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedBoolean && value == ((BoxedBoolean) other).value; + } + + public int hashCode() { + return value ? 1 : 0; + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedBooleanArray.scala b/src/library/scala/runtime/BoxedBooleanArray.scala new file mode 100644 index 0000000000..27b4af2643 --- /dev/null +++ b/src/library/scala/runtime/BoxedBooleanArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedBooleanArray(val value: Array[Boolean]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedBoolean.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedBoolean].value + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedBooleanArray] && value == other.asInstanceOf[BoxedBooleanArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedByte.java b/src/library/scala/runtime/BoxedByte.java new file mode 100644 index 0000000000..b29570081e --- /dev/null +++ b/src/library/scala/runtime/BoxedByte.java @@ -0,0 +1,58 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public final class BoxedByte extends BoxedNumber + implements java.io.Serializable +{ + + private static final int MinHashed = -128; + private static final int MaxHashed = 127; + private static BoxedByte[] canonical = new BoxedByte[MaxHashed - MinHashed + 1]; + + static { + for (int i = MinHashed; i <= MaxHashed; i++) + canonical[i - MinHashed] = new BoxedByte((byte)i); + } + + public static BoxedByte box(byte value) { + return canonical[value - MinHashed]; + } + + public final byte value; + + private BoxedByte(byte value) { this.value = value; } + + public byte byteValue() { return (byte)value; } + public short shortValue() { return (short)value; } + public char charValue() { return (char)value; } + public int intValue() { return (int)value; } + public long longValue() { return (long)value; } + public float floatValue() { return (float)value; } + public double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).byteValue(); + } + + public int hashCode() { + return value; + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedByteArray.scala b/src/library/scala/runtime/BoxedByteArray.scala new file mode 100644 index 0000000000..e011b8e805 --- /dev/null +++ b/src/library/scala/runtime/BoxedByteArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedByteArray(val value: Array[Byte]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedByte.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].byteValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedByteArray] && value == other.asInstanceOf[BoxedByteArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedChar.java b/src/library/scala/runtime/BoxedChar.java new file mode 100644 index 0000000000..02b81e111d --- /dev/null +++ b/src/library/scala/runtime/BoxedChar.java @@ -0,0 +1,59 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public class BoxedChar extends BoxedNumber + implements java.io.Serializable +{ + + private static final int MinHashed = 0; + private static final int MaxHashed = 255; + private static BoxedChar[] canonical = new BoxedChar[MaxHashed - MinHashed + 1]; + + static { + for (int i = MinHashed; i <= MaxHashed; i++) + canonical[i - MinHashed] = new BoxedChar((char)i); + } + + public static BoxedChar box(char value) { + if (MinHashed <= value && value <= MaxHashed) return canonical[value - MinHashed]; + else return new BoxedChar(value); + } + + public final char value; + + private BoxedChar(char value) { this.value = value; } + + public byte byteValue() { return (byte)value; } + public short shortValue() { return (short)value; } + public char charValue() { return (char)value; } + public int intValue() { return (int)value; } + public long longValue() { return (long)value; } + public float floatValue() { return (float)value; } + public double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).charValue(); + } + + public int hashCode() { + return value; + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedCharArray.scala b/src/library/scala/runtime/BoxedCharArray.scala new file mode 100644 index 0000000000..49e3cba25c --- /dev/null +++ b/src/library/scala/runtime/BoxedCharArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedCharArray(val value: Array[Char]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedChar.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].charValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedCharArray] && value == other.asInstanceOf[BoxedCharArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedDouble.java b/src/library/scala/runtime/BoxedDouble.java new file mode 100644 index 0000000000..98736f03a4 --- /dev/null +++ b/src/library/scala/runtime/BoxedDouble.java @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public class BoxedDouble extends BoxedNumber + implements java.io.Serializable +{ + + public static BoxedDouble box(double value) { + return new BoxedDouble(value); + } + + public final double value; + + private BoxedDouble(double value) { this.value = value; } + + public final byte byteValue() { return (byte)value; } + public final short shortValue() { return (short)value; } + public final char charValue() { return (char)value; } + public final int intValue() { return (int)value; } + public final long longValue() { return (long)value; } + public final float floatValue() { return (float)value; } + public final double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).doubleValue(); + } + + public int hashCode() { + long bits = java.lang.Double.doubleToLongBits(value); + return (int)(bits ^ (bits >>> 32)); + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedDoubleArray.scala b/src/library/scala/runtime/BoxedDoubleArray.scala new file mode 100644 index 0000000000..752ab99950 --- /dev/null +++ b/src/library/scala/runtime/BoxedDoubleArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedDoubleArray(val value: Array[Double]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedDouble.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].doubleValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedDoubleArray] && value == other.asInstanceOf[BoxedDoubleArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedFloat.java b/src/library/scala/runtime/BoxedFloat.java new file mode 100644 index 0000000000..630ee6bece --- /dev/null +++ b/src/library/scala/runtime/BoxedFloat.java @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public class BoxedFloat extends BoxedNumber + implements java.io.Serializable +{ + + public static BoxedFloat box(float value) { + return new BoxedFloat(value); + } + + public final float value; + + private BoxedFloat(float value) { this.value = value; } + + public final byte byteValue() { return (byte)value; } + public final short shortValue() { return (short)value; } + public final char charValue() { return (char)value; } + public final int intValue() { return (int)value; } + public final long longValue() { return (long)value; } + public final float floatValue() { return (float)value; } + public final double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).floatValue(); + } + + public int hashCode() { + return java.lang.Float.floatToIntBits(value); + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedFloatArray.scala b/src/library/scala/runtime/BoxedFloatArray.scala new file mode 100644 index 0000000000..bdf25b8f6b --- /dev/null +++ b/src/library/scala/runtime/BoxedFloatArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedFloatArray(val value: Array[Float]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedFloat.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].floatValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedFloatArray] && value == other.asInstanceOf[BoxedFloatArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedInt.java b/src/library/scala/runtime/BoxedInt.java new file mode 100644 index 0000000000..9842fde1ad --- /dev/null +++ b/src/library/scala/runtime/BoxedInt.java @@ -0,0 +1,59 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public final class BoxedInt extends BoxedNumber + implements java.io.Serializable +{ + + private static final int MinHashed = -128; + private static final int MaxHashed = 1024; + private static BoxedInt[] canonical = new BoxedInt[MaxHashed - MinHashed + 1]; + + static { + for (int i = MinHashed; i <= MaxHashed; i++) + canonical[i - MinHashed] = new BoxedInt(i); + } + + public static BoxedInt box(int value) { + if (MinHashed <= value && value <= MaxHashed) return canonical[value - MinHashed]; + else return new BoxedInt(value); + } + + public final int value; + + private BoxedInt(int value) { this.value = value; } + + public final byte byteValue() { return (byte)value; } + public final short shortValue() { return (short)value; } + public final char charValue() { return (char)value; } + public final int intValue() { return (int)value; } + public final long longValue() { return (long)value; } + public final float floatValue() { return (float)value; } + public final double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public final boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).intValue(); + } + + public final int hashCode() { + return value; + } + + public final String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedIntArray.scala b/src/library/scala/runtime/BoxedIntArray.scala new file mode 100644 index 0000000000..9d0db5b233 --- /dev/null +++ b/src/library/scala/runtime/BoxedIntArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedIntArray(val value: Array[Int]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedInt.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].intValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedIntArray] && value == other.asInstanceOf[BoxedIntArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedLong.java b/src/library/scala/runtime/BoxedLong.java new file mode 100644 index 0000000000..ebc86e4c92 --- /dev/null +++ b/src/library/scala/runtime/BoxedLong.java @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public class BoxedLong extends BoxedNumber + implements java.io.Serializable +{ + + public static BoxedLong box(long value) { + return new BoxedLong(value); + } + + public final long value; + + private BoxedLong(long value) { this.value = value; } + + public final byte byteValue() { return (byte)value; } + public final short shortValue() { return (short)value; } + public final char charValue() { return (char)value; } + public final int intValue() { return (int)value; } + public final long longValue() { return (long)value; } + public final float floatValue() { return (float)value; } + public final double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).longValue(); + } + + public int hashCode() { + long bits = value; + return (int)(bits ^ (bits >>> 32)); + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedLongArray.scala b/src/library/scala/runtime/BoxedLongArray.scala new file mode 100644 index 0000000000..624e992e01 --- /dev/null +++ b/src/library/scala/runtime/BoxedLongArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedLongArray(val value: Array[Long]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedLong.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].longValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedLongArray] && value == other.asInstanceOf[BoxedLongArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedNumber.java b/src/library/scala/runtime/BoxedNumber.java new file mode 100644 index 0000000000..a21abab2db --- /dev/null +++ b/src/library/scala/runtime/BoxedNumber.java @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $OldId: RunTime.java,v 1.13 2002/11/19 12:01:40 paltherr Exp $ +// $Id$ +package scala.runtime; + +public abstract class BoxedNumber { + public abstract byte byteValue(); + public abstract short shortValue(); + public abstract char charValue(); + public abstract int intValue(); + public abstract long longValue(); + public abstract float floatValue(); + public abstract double doubleValue(); +} diff --git a/src/library/scala/runtime/BoxedObjectArray.scala b/src/library/scala/runtime/BoxedObjectArray.scala new file mode 100644 index 0000000000..12ff59da8c --- /dev/null +++ b/src/library/scala/runtime/BoxedObjectArray.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +final class BoxedObjectArray(val value: Array[Object]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = value(index); + + def update(index: Int, elem: Object): Unit = { value(index) = elem } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any): Boolean = ( + value == other || + other.isInstanceOf[BoxedObjectArray] && value == other.asInstanceOf[BoxedObjectArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} diff --git a/src/library/scala/runtime/BoxedShort.java b/src/library/scala/runtime/BoxedShort.java new file mode 100644 index 0000000000..f1074c7b7f --- /dev/null +++ b/src/library/scala/runtime/BoxedShort.java @@ -0,0 +1,59 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public final class BoxedShort extends BoxedNumber + implements java.io.Serializable +{ + + private static final int MinHashed = -128; + private static final int MaxHashed = 127; + private static BoxedShort[] canonical = new BoxedShort[MaxHashed - MinHashed + 1]; + + static { + for (int i = MinHashed; i <= MaxHashed; i++) + canonical[i - MinHashed] = new BoxedShort((short)i); + } + + public static BoxedShort box(short value) { + if (MinHashed <= value && value <= MaxHashed) return canonical[value - MinHashed]; + else return new BoxedShort(value); + } + + public final short value; + + private BoxedShort(short value) { this.value = value; } + + public byte byteValue() { return (byte)value; } + public short shortValue() { return (short)value; } + public char charValue() { return (char)value; } + public int intValue() { return (int)value; } + public long longValue() { return (long)value; } + public float floatValue() { return (float)value; } + public double doubleValue() { return (double)value; } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return other instanceof BoxedNumber && value == ((BoxedNumber) other).shortValue(); + } + + public int hashCode() { + return value; + } + + public String toString() { + return String.valueOf(value); + } +} diff --git a/src/library/scala/runtime/BoxedShortArray.scala b/src/library/scala/runtime/BoxedShortArray.scala new file mode 100644 index 0000000000..01e00c5a21 --- /dev/null +++ b/src/library/scala/runtime/BoxedShortArray.scala @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +[serializable] +final class BoxedShortArray(val value: Array[Short]) extends BoxedArray { + + def length: Int = value.length; + + def apply(index: Int): Object = BoxedShort.box(value(index)); + + def update(index: Int, elem: Object): Unit = { + value(index) = elem.asInstanceOf[BoxedNumber].shortValue() + } + + def unbox(elemTag: String): Object = value; + + override def equals(other: Any) = ( + value == other || + other.isInstanceOf[BoxedShortArray] && value == other.asInstanceOf[BoxedShortArray].value + ); + + override def hashCode(): Int = value.hashCode(); +} + diff --git a/src/library/scala/runtime/BoxedUnit.java b/src/library/scala/runtime/BoxedUnit.java new file mode 100644 index 0000000000..91e5f5d03a --- /dev/null +++ b/src/library/scala/runtime/BoxedUnit.java @@ -0,0 +1,37 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.runtime; + +public final class BoxedUnit + implements java.io.Serializable +{ + + public final static BoxedUnit UNIT = new BoxedUnit(); + + private BoxedUnit() { } + + public final boolean $eq$eq(java.lang.Object other) { + return equals(other); + } + + public final boolean $bang$eq(java.lang.Object other) { + return !equals(other); + } + + public boolean equals(java.lang.Object other) { + return this == other; + } + + public int hashCode() { + return 0; + } + + public String toString() { + return "()"; + } +} diff --git a/src/library/scala/runtime/ByteRef.java b/src/library/scala/runtime/ByteRef.java new file mode 100644 index 0000000000..f4a98b8ff6 --- /dev/null +++ b/src/library/scala/runtime/ByteRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class ByteRef implements java.io.Serializable { + public byte elem; + public ByteRef(byte elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/CharRef.java b/src/library/scala/runtime/CharRef.java new file mode 100644 index 0000000000..71d3666b4e --- /dev/null +++ b/src/library/scala/runtime/CharRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class CharRef implements java.io.Serializable { + public char elem; + public CharRef(char elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/DoubleRef.java b/src/library/scala/runtime/DoubleRef.java new file mode 100644 index 0000000000..a0fd0ff68b --- /dev/null +++ b/src/library/scala/runtime/DoubleRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class DoubleRef implements java.io.Serializable { + public double elem; + public DoubleRef(double elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/ExceptionHandling.cs b/src/library/scala/runtime/ExceptionHandling.cs new file mode 100644 index 0000000000..0a59308f99 --- /dev/null +++ b/src/library/scala/runtime/ExceptionHandling.cs @@ -0,0 +1,33 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +using System; +using scala; + +namespace scala.runtime { + + public abstract class RunTime { + + public interface Runnable { + void run(); + } + + public static Exception tryCatch(Runnable runnable) { + try { + runnable.run(); + return null; + } catch (Exception exception) { + return exception; + } + } + + } + +} diff --git a/src/library/scala/runtime/ExceptionHandling.java b/src/library/scala/runtime/ExceptionHandling.java new file mode 100644 index 0000000000..293d2cad8e --- /dev/null +++ b/src/library/scala/runtime/ExceptionHandling.java @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public abstract class ExceptionHandling { + + public static Throwable tryCatch(Runnable runnable) { + try { + runnable.run(); + return null; + } catch (Throwable exception) { + return exception; + } + } + +} diff --git a/src/library/scala/runtime/FNV_Hash.java b/src/library/scala/runtime/FNV_Hash.java new file mode 100644 index 0000000000..6259c33efb --- /dev/null +++ b/src/library/scala/runtime/FNV_Hash.java @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +/** + * Provide methods to compute the various kinds of Fowler / Noll / Vo + * (FNV) hash. + * + * @author Michel Schinz + */ + +public class FNV_Hash { + public static final int INIT = -2128831035; + + public static int hashStep8(int current, int newInt8) { + return (current * 16777619) ^ newInt8; + } + + public static int hashStep32(int current, int newInt32) { + final int v1 = hashStep8(current, newInt32 >> 24); + final int v2 = hashStep8(v1, (newInt32 >> 16) & 0xFF); + final int v3 = hashStep8(v2, (newInt32 >> 8) & 0xFF); + return hashStep8(v3, newInt32 & 0xFF); + } + + public static int hash32(byte[] bytes) { + final int len = bytes.length; + + int h = INIT; + for (int i = 0; i < len; ++i) + h = hashStep8(h, bytes[i]); + + return h; + } + + public static int hash32(String str) { + try { + return hash32(str.getBytes("UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + throw new Error(e); + } + } +} diff --git a/src/library/scala/runtime/FloatRef.java b/src/library/scala/runtime/FloatRef.java new file mode 100644 index 0000000000..dc8c332b66 --- /dev/null +++ b/src/library/scala/runtime/FloatRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class FloatRef implements java.io.Serializable { + public float elem; + public FloatRef(float elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/IOMap.java b/src/library/scala/runtime/IOMap.java new file mode 100644 index 0000000000..e93b3c4a9d --- /dev/null +++ b/src/library/scala/runtime/IOMap.java @@ -0,0 +1,171 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +/** + * Purely functional maps from integers to objects. Implemented as + * red-black trees. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class IOMap implements java.io.Serializable { + + /** The map class itself */ + public static class T implements java.io.Serializable { + public case N(int c, T l, T r, int k, Object v); + public case E; + } + + public static final T EMPTY = T.E; + + // Node colors (Black and Red) + private static final int B = 0; + private static final int R = 1; + + public static class ConflictException extends Exception { + public final int key; + public final Object oldValue, newValue; + + public ConflictException(int key, Object oldValue, Object newValue) { + this.key = key; + this.oldValue = oldValue; + this.newValue = newValue; + } + public Throwable fillInStackTrace() { + // do nothing, to speed up things + return this; + } + } + + public Object resolveConflict(int k, Object oldV, Object newV) + throws ConflictException { + throw new ConflictException(k, oldV, newV); + } + + public T put(T map, int key, Object value) throws ConflictException { + switch (putAux(map, key, value)) { + case N(_, T l, T r, int k, Object v): + return T.N(B, l, r, k, v); + default: + throw new Error(); + } + } + + private T putAux(T map, int key, Object value) throws ConflictException { + switch (map) { + case N(int c, T l, T r, int k, Object v): + if (key < k) + return balance(T.N(c, putAux(l, key, value), r, k, v)); + else if (key > k) + return balance(T.N(c, l, putAux(r, key, value), k, v)); + else + return T.N(c, l, r, k, resolveConflict(k, v, value)); + case E: + return T.N(R, T.E, T.E, key, value); + default: + throw new Error(); + } + } + + private T balance(T t) { + switch (t) { + case N(B, + N(R, N(R, T a, T b, int xK, Object xV), T c, int yK, Object yV), + T d, + int zK, Object zV): + return T.N(R, T.N(B, a, b, xK, xV), T.N(B, c, d, zK, zV), yK, yV); + case N(B, + N(R, T a, N(R, T b, T c, int yK, Object yV), int xK, Object xV), + T d, + int zK, Object zV): + return T.N(R, T.N(B, a, b, xK, xV), T.N(B, c, d, zK, zV), yK, yV); + case N(B, + T a, + N(R, N(R, T b, T c, int yK, Object yV), T d, int zK, Object zV), + int xK, Object xV): + return T.N(R, T.N(B, a, b, xK, xV), T.N(B, c, d, zK, zV), yK, yV); + case N(B, + T a, + N(R, T b, N(R, T c, T d, int zK, Object zV), int yK, Object yV), + int xK, Object xV): + return T.N(R, T.N(B, a, b, xK, xV), T.N(B, c, d, zK, zV), yK, yV); + default: + return t; + } + } + + public Object get(T map, int key) { + switch (map) { + case N(_, T l, T r, int k, Object v): + if (key < k) + return get(l, key); + else if (key > k) + return get(r, key); + else + return v; + case E: + return null; + default: + throw new Error("unexpected node " + this); + } + } + + public int size(T map) { + switch (map) { + case N(_, T l, T r, _, _): + return size(l) + size(r) + 1; + case E: + return 0; + default: + throw new Error("unexpected node " + this); + } + } + + public int depth(T map) { + switch (map) { + case N(_, T l, T r, _, _): + return Math.max(depth(l), depth(r)) + 1; + case E: + return 0; + default: + throw new Error("unexpected node " + this); + } + } +} + +// class RBTest { +// static class MyIOMap extends IOMap { +// public Object resolveConflict(int k, Object oldV, Object newV) { +// throw new Error("conflict!!!"); +// } +// } + +// public static void main(String[] args) { +// MyIOMap map = new MyIOMap(); +// MyIOMap.T t = map.EMPTY; + +// long start = System.currentTimeMillis(); +// for (int i = 0; i < args.length; ++i) { +// t = map.put(t, FNV_Hash.hash32(args[i]), new Integer(i)); +// } + +// for (int i = 0; i < args.length; ++i) { +// map.get(t, FNV_Hash.hash32(args[i])); +// } +// long end = System.currentTimeMillis(); +// System.out.println("time: " + (end - start) + "ms"); + +// System.out.println("size = " + map.size(t)); +// System.out.println("depth = " + map.depth(t)); +// } +// } diff --git a/src/library/scala/runtime/IntRef.java b/src/library/scala/runtime/IntRef.java new file mode 100644 index 0000000000..1f5feacec7 --- /dev/null +++ b/src/library/scala/runtime/IntRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class IntRef implements java.io.Serializable { + public int elem; + public IntRef(int elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/InterpreterSupport.java b/src/library/scala/runtime/InterpreterSupport.java new file mode 100644 index 0000000000..9272583ca1 --- /dev/null +++ b/src/library/scala/runtime/InterpreterSupport.java @@ -0,0 +1,108 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +/** This class provides support methods for the interpreter. */ +public class InterpreterSupport { + + //######################################################################## + // Public classes + + /** This interface provides method to show definitions. */ + public static interface DefinitionPrinter { + + /** This method is invoked for each non-value definition. */ + public void showDefinition(String signature); + + /** This method is invoked for each value definition. */ + public void showValueDefinition(String signature, Object value); + + } + + /** This class describes an evaluation result. */ + public static class EvaluationResult { + + /** The value of the result */ + public final Object value; + + /** The type of the result */ + public final String type; + + /** Creates a new instance */ + public EvaluationResult(Object value, String type) { + this.value = value; + this.type = type; + } + } + + //######################################################################## + // Private Variables + + private static final ThreadLocal printer = new ThreadLocal(); + private static final ThreadLocal result = new ThreadLocal(); + + //######################################################################## + // Public Functions + + /** Sets the definition printer of the current thread. */ + public static void setDefinitionPrinter(DefinitionPrinter object) { + printer.set(object); + } + + /** Returns the definition printer of the current thread. */ + public static DefinitionPrinter getDefinitionPrinter() { + return (DefinitionPrinter)printer.get(); + } + + /** + * This function is invoked for each non-value definition. It + * forwards the call to the current thread's definition printer. + * + * @meta method (java.lang.String, scala.Any) scala.Unit; + */ + public static void showDefinition(String signature) { + DefinitionPrinter printer = getDefinitionPrinter(); + if (printer != null) printer.showDefinition(signature); + } + + /** + * This method is invoked for each value definition. It forwards + * the call to the current thread's definition printer. + * + * @meta method (java.lang.String, scala.Any) scala.Unit; + */ + public static void showValueDefinition(String signature, Object value) { + DefinitionPrinter printer = getDefinitionPrinter(); + if (printer != null) printer.showValueDefinition(signature, value); + } + + /** + * Sets the evaluation result of the current thread. + * + * @meta method (scala.Any, java.lang.String) scala.Unit; + */ + public static void setEvaluationResult(Object value, String type) { + result.set(new EvaluationResult(value, type)); + } + + /** + * Returns and resets the evaluation result of the current + * thread. A null value indicates that the last evaluation had no + * result (only definitions). + */ + public static EvaluationResult getAndResetEvaluationResult() { + Object object = result.get(); + result.set(null); + return (EvaluationResult)object; + } + + //######################################################################## +} diff --git a/src/library/scala/runtime/LongRef.java b/src/library/scala/runtime/LongRef.java new file mode 100644 index 0000000000..863472c666 --- /dev/null +++ b/src/library/scala/runtime/LongRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class LongRef implements java.io.Serializable { + public long elem; + public LongRef(long elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/MetaAttribute.cs b/src/library/scala/runtime/MetaAttribute.cs new file mode 100644 index 0000000000..28ab499bdd --- /dev/null +++ b/src/library/scala/runtime/MetaAttribute.cs @@ -0,0 +1,34 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +using System; + +namespace scala.runtime +{ + /// <summary> + /// Stores additional meta-information about classes and members. + /// Used to augment type information in classes from the scala + /// library written in Java. + /// </summary> + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Field + | AttributeTargets.Constructor | AttributeTargets.Method, + AllowMultiple = false, Inherited = false)] + public class MetaAttribute : Attribute + { + // keeps a textual representation of the pico-style attributes + // used in some classes of the runtime library + public readonly string meta; + public MetaAttribute(string meta) + { + this.meta = meta; + } + } +} diff --git a/src/library/scala/runtime/ObjectRef.java b/src/library/scala/runtime/ObjectRef.java new file mode 100644 index 0000000000..c5ba9fb640 --- /dev/null +++ b/src/library/scala/runtime/ObjectRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class ObjectRef implements java.io.Serializable { + public Object elem; + public ObjectRef(Object elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala new file mode 100644 index 0000000000..8bdabef3c0 --- /dev/null +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -0,0 +1,90 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id:ScalaRunTime.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ + +package scala.runtime; + +object ScalaRunTime { + + /** Names for primitive types, used by array unboxing */ + val ByteTag = ".Byte"; + val ShortTag = ".Short"; + val CharTag = ".Char"; + val IntTag = ".Int"; + val LongTag = ".Long"; + val FloatTag = ".Float"; + val DoubleTag = ".Double"; + val BooleanTag = ".Boolean"; + + trait Try[a] { + def Catch[b >: a](handler: PartialFunction[Throwable, b]): b; + def Finally(handler: Unit): a; + } + + def Try[a](block: => a): Try[a] = new Try[a] with Runnable { + var result: a = _; + var exception: Throwable = ExceptionHandling.tryCatch(this); + + def run(): Unit = result = block; + + def Catch[b >: a](handler: PartialFunction[Throwable, b]): b = + if (exception == null) + result.asInstanceOf$erased[b] + // !!! else if (exception is LocalReturn) + // !!! // ... + else if (handler isDefinedAt exception) + handler(exception) + else + throw exception; + + def Finally(handler: Unit): a = + if (exception == null) + result.asInstanceOf$erased[a] + else + throw exception; + } + + def caseFields(x: CaseClass): List[Any] = { + val arity = x.caseArity; + def fields(from: Int): List[Any] = + if (from >= arity) List() + else x.caseElement(from) :: fields(from + 1); + fields(0) + } + + def _toString(x: CaseClass): String = { + caseFields(x).mkString(x.caseName + "(", ",", ")") + } + + def _hashCode(x: CaseClass): Int = { + var code = x.getClass().hashCode(); + val arity = x.caseArity; + var i = 0; + while (i < arity) { + code = code * 41 + x.caseElement(i).hashCode(); + i = i + 1 + } + code + } + + def _equals(x: CaseClass, y: Any): Boolean = y match { + case y1: CaseClass => + (x.getClass() eq y1.getClass()) && { + val arity = x.caseArity; + var i = 0; + while (i < arity && x.caseElement(i) == y1.caseElement(i)) + i = i + 1; + i == arity + } + case _ => + false + } + + def Seq[a](xs: a*): Seq[a] = null; // interpreted specially by new backend. +} diff --git a/src/library/scala/runtime/ShortRef.java b/src/library/scala/runtime/ShortRef.java new file mode 100644 index 0000000000..3cada1fab8 --- /dev/null +++ b/src/library/scala/runtime/ShortRef.java @@ -0,0 +1,16 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.runtime; + +public class ShortRef implements java.io.Serializable { + public short elem; + public ShortRef(short elem) { this.elem = elem; } +} diff --git a/src/library/scala/runtime/SymtabAttribute.cs b/src/library/scala/runtime/SymtabAttribute.cs new file mode 100644 index 0000000000..fcb273a857 --- /dev/null +++ b/src/library/scala/runtime/SymtabAttribute.cs @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +using System; + +namespace scala.runtime +{ + /// <summary> + /// Stores the symbol table for every top-level Scala class. + /// </summary> + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class SymtabAttribute : Attribute + { + // stores scalac symbol table + public readonly byte[] symtab; + + // indicates if the type should be considered by the compiler; + // used for synthetic classes introduced by the Scala compiler + public readonly bool shouldLoadClass; + + public SymtabAttribute(byte[] symtab) + { + this.symtab = symtab; + this.shouldLoadClass = true; + } + + public SymtabAttribute() { + this.symtab = new byte[0]; + this.shouldLoadClass = false; + } + } +} diff --git a/src/library/scala/runtime/compat/Math.scala b/src/library/scala/runtime/compat/Math.scala new file mode 100644 index 0000000000..147ffeb0f9 --- /dev/null +++ b/src/library/scala/runtime/compat/Math.scala @@ -0,0 +1,36 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.runtime.compat; + +object Math { + val MIN_BYTE = java.lang.Byte.MIN_VALUE; + val MAX_BYTE = java.lang.Byte.MAX_VALUE; + val MIN_SHORT = java.lang.Short.MIN_VALUE; + val MAX_SHORT = java.lang.Short.MAX_VALUE; + val MIN_CHAR = java.lang.Character.MIN_VALUE; + val MAX_CHAR = java.lang.Character.MAX_VALUE; + val MIN_INT = java.lang.Integer.MIN_VALUE; + val MAX_INT = java.lang.Integer.MAX_VALUE; + val MIN_LONG = java.lang.Long.MIN_VALUE; + val MAX_LONG = java.lang.Long.MAX_VALUE; + val MIN_FLOAT = java.lang.Float.MIN_VALUE; + val MAX_FLOAT = java.lang.Float.MIN_VALUE; + val MIN_DOUBLE = java.lang.Double.MIN_VALUE; + val MAX_DOUBLE = java.lang.Double.MAX_VALUE; + + def max(x: Int, y: Int): Int = java.lang.Math.max(x, y); + + def ceil (x: Double): Double = java.lang.Math.ceil(x); + def floor(x: Double): Double = java.lang.Math.floor(x); + def log (x: Double): Double = java.lang.Math.log(x); + def sqrt (x: Double): Double = java.lang.Math.sqrt(x); +} diff --git a/src/library/scala/runtime/compat/Platform.scala b/src/library/scala/runtime/compat/Platform.scala new file mode 100644 index 0000000000..282421a981 --- /dev/null +++ b/src/library/scala/runtime/compat/Platform.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.runtime.compat; + +object Platform { + def arraycopy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: int): Unit = + System.arraycopy(src, srcPos, dest, destPos, length); + def getClass(obj: AnyRef) = obj.getClass(); + def getClassName(obj: AnyRef) = obj.getClass().getName(); + def printStackTrace(exc: java.lang.Throwable) = exc.printStackTrace(); + def getMessage(exc: java.lang.Throwable) = exc.getMessage(); + def split(str: String, separator: Char): Array[String] = { + str.split(separator.toString()); + } + + def currentThread = java.lang.Thread.currentThread(); + + def parseByte(s: String): Byte = java.lang.Byte.parseByte(s); + def parseShort(s: String): Short = java.lang.Short.parseShort(s); + def parseInt(s: String): Int = java.lang.Integer.parseInt(s); + def parseLong(s: String): Long = java.lang.Long.parseLong(s); + def parseFloat(s: String): Float = java.lang.Float.parseFloat(s); + def parseDouble(s: String): Double = java.lang.Double.parseDouble(s); + + def isDigit(c: Char): Boolean = java.lang.Character.isDigit(c); +} diff --git a/src/library/scala/runtime/compat/StringBuilder.scala b/src/library/scala/runtime/compat/StringBuilder.scala new file mode 100644 index 0000000000..bfb790723e --- /dev/null +++ b/src/library/scala/runtime/compat/StringBuilder.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + + +package scala.runtime.compat; + +class StringBuilder { + val str = new StringBuffer(); + def append(x: Any): StringBuilder = { + str.append(x); + this + } + def append(x: String): StringBuilder = { + str.append(x); + this + } + def length(): Int = str.length(); + override def toString() = str.toString(); +} diff --git a/src/library/scala/runtime/matching/Address.scala b/src/library/scala/runtime/matching/Address.scala new file mode 100644 index 0000000000..3f548e402f --- /dev/null +++ b/src/library/scala/runtime/matching/Address.scala @@ -0,0 +1,30 @@ +package scala.runtime.matching ; + +object Address { + def empty = new Address(); +} + +//import List.list2ordered; + +/** Address holds the path in reverse Dewey notation +*/ +class Address( l:Int* ) extends Ordered[Address] { + + private val list:List[Int] = l.toList; + + def compareTo [b >: Address <% Ordered[b]](y: b): int = y match { + case o:Address => list.reverse.compareTo(o.list.reverse) + //(xs => List.view(xs)(Predef.int2ordered)); + case _ => -(y compareTo this) + } + + def down: Address = new Address( ( 1 :: list ):_* ); + + /** precond: p is nonempty */ + def right: Address = list match { + case i :: rest => new Address( ((i+1) :: rest ):_* ) + } + + override def toString() = list.mkString("Address(",".",")"); + +} diff --git a/src/library/scala/runtime/matching/NonTerm.scala b/src/library/scala/runtime/matching/NonTerm.scala new file mode 100644 index 0000000000..35bc0f60ce --- /dev/null +++ b/src/library/scala/runtime/matching/NonTerm.scala @@ -0,0 +1,52 @@ +package scala.runtime.matching ; + +import scala.collection.immutable ; + +abstract class NonTerm { + + override def toString() = this match { + case TreeNT( i ) => "t"+i; + case h @ HedgeNT( i ) => "h"+i+{ if( h.nullable )"~" else "" }; + } + +} + +/** tree nonterminal. holds set of binding variable indices. + */ +case class TreeNT(i:int) extends NonTerm with Ordered[TreeNT] { + + var vset:immutable.Set[Int] = new immutable.TreeSet[Int]() ; + + /** vset should be sorted to allow fast lookup */ + def this( i:int, vset:immutable.Set[Int] ) = { + this( i ); + this.vset = new immutable.TreeSet[Int]() incl vset ; + } + + def compareTo [b >: TreeNT <% Ordered[b]](y: b): int = y match { + case TreeNT( y1 ) => i compareTo y1; + case _ => -(y compareTo this) + } +}; + +/** hedge nonterminals. holds nullable property. + */ +case class HedgeNT(i:int) extends NonTerm with Ordered[HedgeNT] { + + var nullable:boolean = false; + + def this( i:int, nullable:boolean ) = { + this( i ); + this.nullable = nullable; + } + + def compareTo [b >: HedgeNT <% Ordered[b]](y: b): int = y match { + case HedgeNT( y1 ) => i compareTo y1; + case _ => -(y compareTo this) + } +}; + +//case object EMPTYHEDGE extends HedgeNT( 0, true ) ; +//case object ANYHEDGE extends HedgeNT( 1, true ) ; +object ANYTREE extends TreeNT( 1 ); + diff --git a/src/library/scala/runtime/matching/PatternTests.scala b/src/library/scala/runtime/matching/PatternTests.scala new file mode 100644 index 0000000000..ea04481d58 --- /dev/null +++ b/src/library/scala/runtime/matching/PatternTests.scala @@ -0,0 +1,5 @@ +package scala.runtime.matching ; + +abstract class PatternTests extends Function2[Int,Any,Boolean]{ + def apply(i:Int, inp:Any): Boolean; +} diff --git a/src/library/scala/runtime/matching/Rule.scala b/src/library/scala/runtime/matching/Rule.scala new file mode 100644 index 0000000000..7bd6d40fd1 --- /dev/null +++ b/src/library/scala/runtime/matching/Rule.scala @@ -0,0 +1,102 @@ +package scala.runtime.matching ; + +/* hedge grammar rules */ +abstract class Rule extends Ordered[Rule] { + + def compareTo [b >: Rule <% Ordered[b]](that: b): int = that match { + case r:Rule => + if( rule_smaller( this, r ) ) + -1 + else if( rule_eq( this, r ) ) + 0 + else + 1 + case _ => -(that compareTo this) + } + + final def rule_smaller( r1:Rule, r2:Rule ):boolean = r1 match { + case HedgeRule( h1, _, hh1 ) => r2 match { + case HedgeRule( h2, _, hh2 ) => + ((h1 == h2)&&( hh1.i < hh2.i )) || h1.i < h2.i; + case HedgeChainRule( h2, hh2 ) => + ((h1 == h2)&&( hh1.i < hh2.i )) || h1.i < h2.i; + case _ => false; + } + case HedgeChainRule( h1, hh1 ) => r2 match { + case HedgeRule( h2, _, hh2 ) => + ((h1 == h2)&&( hh1.i < hh2.i )) || h1.i < h2.i; + case HedgeChainRule( h2, hh2 ) => + ((h1 == h2)&&( hh1.i < hh2.i )) || h1.i < h2.i; + case _ => false; + } + case TreeRule( t1, _, hh1 ) => r2 match { + case TreeRule( t2, _, hh2 ) => + ((t1 == t2 )&&(hh1.i < hh2.i )) || t1.i < t2.i; + case AnyTreeRule( t2 ) => false; + case AnyNodeRule( t2, hh2 ) => + ((t1 == t2 )&&(hh1.i < hh2.i )) || t1.i < t2.i; + case _ => true; + } + case AnyTreeRule( t1 ) => r2 match { + case TreeRule( _, _ , _ ) => true; + case AnyTreeRule( t2 ) => t1.i < t2.i; + case AnyNodeRule( t2, _ ) => true + case _ => true; + } + case AnyNodeRule( t1, hh1 ) => r2 match { + case TreeRule( t2, _, hh2 ) => + ((t1 == t2 )&&(hh1.i < hh2.i )) || t1.i < t2.i; + case AnyTreeRule( t2 ) => false; + case AnyNodeRule( t2, hh2 ) => + ((t1 == t2 )&&(hh1.i < hh2.i )) || t1.i < t2.i; + case _ => true; + } + }; + final def rule_eq( r1:Rule, r2:Rule ):boolean = r1 == r2; + + + override def toString() = this match { + case HedgeChainRule( n, m ) => + n.toString()+" ::= "+m.toString(); + + case TreeRule( n, label, n2 ) => + (n.toString()+{ if( !n.vset.isEmpty ) n.vset.toString() else "" }+ + " ::= "+label+"( "+n2.toString()+{if( n2.nullable ) "~" else ""}+" )") + + case AnyTreeRule( n ) => + n.toString()+{ if( !n.vset.isEmpty ) n.vset.toString() else "" }+" ::= _ "; + + case AnyNodeRule( n, h ) => + n.toString()+{ if( !n.vset.isEmpty ) n.vset.toString() else "" }+" ::= _ ( "+h.toString()+" )"; + + case HedgeRule( n, t, h ) => + n.toString()+( + if( n.nullable ) "~" else " " + )+" ::= "+( + if( t == ANYTREE ) "_" else t.toString() + )+" "+h.toString(); + + } +} + +abstract class TRule extends Rule; +abstract class HRule extends Rule; +/* +a tree rule is of the from A -> s(B) +where A,B are TreeNTs and s is an identifier (string). + +If s is the empty string, then the node label is arbitrary +If HedgeNT is AnyHedgeNT, then the tree is arbitrary +*/ +case class HedgeChainRule( n: HedgeNT, rhs: HedgeNT ) extends HRule; +case class TreeRule( n:TreeNT, test:Int, h:HedgeNT ) extends TRule { + def this(i:Int, s:Int, n:Int ) = { + this( new TreeNT(i), s, new HedgeNT(n)); + } +}; +case class AnyTreeRule( n:TreeNT ) extends TRule { +} +case class AnyNodeRule( n:TreeNT, h:HedgeNT ) extends TRule { +} +case class HedgeRule( n:HedgeNT, t:TreeNT, h:HedgeNT ) extends HRule; + diff --git a/src/library/scala/runtime/matching/TestAlphabet.scala b/src/library/scala/runtime/matching/TestAlphabet.scala new file mode 100644 index 0000000000..9ef4698ebc --- /dev/null +++ b/src/library/scala/runtime/matching/TestAlphabet.scala @@ -0,0 +1,9 @@ +package scala.runtime.matching ; + +trait TestAlphabet; + +case class TestLabel(i: Int) extends TestAlphabet ; + +case object AnyNode extends TestAlphabet { + def view(x: Int): TestLabel = TestLabel(x); +} diff --git a/src/library/scala/serializable.scala b/src/library/scala/serializable.scala new file mode 100644 index 0000000000..2b9ad9f177 --- /dev/null +++ b/src/library/scala/serializable.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +*/ + +package scala; + +class serializable extends Attribute {} diff --git a/src/library/scala/testing/Benchmark.scala b/src/library/scala/testing/Benchmark.scala new file mode 100644 index 0000000000..f6523a7271 --- /dev/null +++ b/src/library/scala/testing/Benchmark.scala @@ -0,0 +1,74 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + + +/** + * Scala benchmarking mini framework. + */ + +package scala.testing; + +/** <code>Benchmark</code> can be used to quickly turn an existing + * class into a benchmark. Here is a short example: + * + * <pre> + * object sort1 extends Sorter with Benchmark { + * def run = sort(List.range(1, 1000)); + * } + * </pre> + * + * The run method has to be defined by the user, who will perform + * the timed operation there. + * Run the benchmark as follows: + * <pre> + * scala sort1 5 times.log + * </pre> + * This will run the benchmark 5 times and log the execution times in + * a file called times.log + */ +trait Benchmark { + + /** this method should be implemented by the concrete benchmark */ + def run: Unit; + + /** Run the benchmark the specified number of times + * and return a list with the execution times in milliseconds + * in reverse order of the execution */ + def runBenchmark(noTimes: Int): List[Long] = + + for (val i <- List.range(1, noTimes + 1)) yield { + val startTime = System.currentTimeMillis(); + run; + val stopTime = System.currentTimeMillis(); + System.gc(); + + stopTime - startTime + } + + /** + * The entry point. It takes two arguments: the number of + * consecutive runs, and the name of a log file where to + * append the times. + * + */ + def main(args: Array[String]): Unit = { + if (args.length > 1) { + val logFile = new java.io.FileWriter(args(1), true); // append, not overwrite + + logFile.write(getClass().getName()); + for (val t <- runBenchmark(Integer.parseInt(args(0)))) + logFile.write("\t\t" + t); + + logFile.write("\n"); + logFile.flush(); + } else + Console.println("Usage: scala benchmarks.program <runs> <logfile>"); + } +} + diff --git a/src/library/scala/testing/SUnit.scala b/src/library/scala/testing/SUnit.scala new file mode 100644 index 0000000000..4faa0c9cd0 --- /dev/null +++ b/src/library/scala/testing/SUnit.scala @@ -0,0 +1,188 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.testing; + +/** unit testing methods in the spirit of JUnit framework. + * use these classes like this: +<code> + import scala.testing.SUnit; + import SUnit._; + + + class MyTest(n:String) extends TestCase(n) { + + override def runTest() = n match { + case "myTest1" => assertTrue( true ); + case "myTest2" => assertTrue( "hello", false ); + } + } + + val r = new TestResult(); + suite.run(r); + for(val tf <- r.failures()) { + Console.println(tf.toString()) + } +} +</code> + */ +object SUnit { + + /** a Test can be run with its result being collected */ + trait Test { + def run(r: TestResult):Unit; + } + + /** a TestCase defines the fixture to run multiple tests */ + class TestCase(val name: String) extends Test with Assert { + protected def createResult() = + new TestResult(); + + protected def runTest(): Unit = + {} + + def run(r: TestResult): Unit = + try { + runTest(); + } catch { + case t:Throwable => r.addFailure(this, t); + } + + def run(): Unit = + run(createResult()); + + def setUp() = + {} + + def tearDown() = + {} + + override def toString() = + name; + } + + /** a TestFailure collects a failed test together with the thrown exception */ + class TestFailure(val failedTest:Test, val thrownException:Throwable) { + + def this(p:Pair[Test,Throwable]) = this(p._1, p._2); + + override def toString() = + failedTest.toString()+" failed due to "+thrownException.toString(); + + def trace(): String = { + val s = new StringBuffer(); + for(val trElem <- thrownException.getStackTrace()) { + s.append(trElem.toString()); + s.append('\n'); + } + s.toString() + } + } + + /** a TestResult collects the result of executing a test case */ + class TestResult { + val buf = + new scala.collection.mutable.ArrayBuffer[Pair[Test,Throwable]](); + + def addFailure(test:Test, t:Throwable) = + buf += Pair(test,t); + + def failureCount() = + buf.length; + + def failures() = + buf.elements map { x => new TestFailure(x) }; + } + + /** a TestSuite runs a composite of test cases */ + class TestSuite(tests:Test*) extends Test { + + def this(names:Seq[String], constr:String=>Test) = + this((names.toList map constr):_*); + + val buf = + new scala.collection.mutable.ArrayBuffer[Test](); + + buf ++= tests; + + def addTest(t: Test) = + buf += t; + + def run(r: TestResult):Unit = { + for(val t <- buf) { + t.run(r); + } + } + } + + /** an AssertFailed is thrown for a failed assertion */ + case class AssertFailed(msg:String) extends java.lang.RuntimeException { + override def toString() = + "failed assertion:"+msg; + } + + /** this trait defined useful assert methods */ + trait Assert { + /** equality */ + def assertEquals[A](msg:String, expected:A, actual: => A): Unit = + if( expected != actual ) fail(msg); + + /** equality */ + def assertEquals[A](expected:A, actual: => A): Unit = + assertEquals("(no message)", expected, actual); + + /** falseness */ + def assertFalse(msg:String, actual: => Boolean): Unit = + assertEquals(msg, false, actual); + /** falseness */ + def assertFalse(actual: => Boolean): Unit = + assertFalse("(no message)", actual); + + /** not null */ + def assertNotNull(msg:String, actual: => AnyRef): Unit = + if( null == actual ) fail(msg); + + /** not null */ + def assertNotNull(actual: => AnyRef): Unit = + assertNotNull("(no message)", actual); + + /** reference inequality */ + def assertNotSame(msg:String, expected: => AnyRef, actual: => AnyRef): Unit = + if(expected.eq(actual)) fail(msg); + /** reference inequality */ + def assertNotSame(expected: => AnyRef, actual: => AnyRef): Unit = + assertNotSame("(no message)", expected, actual); + + /** null */ + def assertNull(msg:String, actual: => AnyRef): Unit = + if( null != actual ) fail(msg); + /** null */ + def assertNull(actual: => AnyRef): Unit = + assertNull("(no message)", actual); + + + /** reference equality */ + def assertSame(msg:String, expected: => AnyRef, actual: => AnyRef): Unit = + if(!expected.eq(actual)) fail(msg); + /** reference equality */ + def assertSame(expected: => AnyRef, actual: => AnyRef): Unit = + assertSame("(no message)", expected, actual); + + /** trueness */ + def assertTrue(msg:String, actual: => Boolean): Unit = + assertEquals(msg, true, actual); + /** trueness */ + def assertTrue(actual: => Boolean): Unit = + assertTrue("(no message)", actual); + + /** throws AssertFailed with given message */ + def fail(msg:String): Unit = + throw new AssertFailed(msg); + } +} diff --git a/src/library/scala/testing/UnitTest.scala b/src/library/scala/testing/UnitTest.scala new file mode 100644 index 0000000000..2f8878b2eb --- /dev/null +++ b/src/library/scala/testing/UnitTest.scala @@ -0,0 +1,58 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.testing; + +/** some simple methods to support unit testing with assertions +** to contain more JUnit style assertions which use Scala's features. +*/ +object UnitTest { + + class Report( report_ok:()=>Unit, report_fail:(String,String)=>Unit ) { + def ok:Unit = report_ok(); + def fail( actual:String, expected:String ):Unit = + report_fail( actual, expected ); + } + + var report = new Report( + { () => Console.println("passed ok") }, + { (actual:String, expected:String) => + Console.print("failed! we got"); + Console.print( "\""+ actual +"\"" ); + Console.println(" but expected \"" + expected + "\"") }); + + def setReporter( r:Report ) = { + this.report = r; + } + + def assertSameElements[a]( actual:Seq[a] , expected: Seq[a] ):Unit = + if( actual.sameElements( expected ) ) + report.ok + else + report.fail( actual.toString(), expected.toString() ); + + def assertEquals[a]( actual: a, expected: a ):Unit = + if( actual == expected ) + report.ok + else + report.fail( actual.toString(), expected.toString() ); + + def assertTrue( actual: Boolean ):Unit = assertEquals(actual, true); + def assertFalse( actual: Boolean ):Unit = assertEquals(actual, false); + + + def assertNotEquals[a]( actual: a, expected: a ):Unit = + if( actual != expected ) + report.ok + else + report.fail( actual.toString(), "x != "+expected.toString() ); + + //def test[a]( def doit: a, expected: a ):Unit = assertEquals( doit, expected ); + +} // unitTest diff --git a/src/library/scala/text/Document.scala b/src/library/scala/text/Document.scala new file mode 100644 index 0000000000..40d5a3a7c4 --- /dev/null +++ b/src/library/scala/text/Document.scala @@ -0,0 +1,119 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.text; + +import java.io.PrintWriter; +import java.io.Writer; + +case object DocNil extends Document; +case object DocBreak extends Document; +case class DocText(txt: String) extends Document; +case class DocGroup(doc: Document) extends Document; +case class DocNest(indent: Int, doc: Document) extends Document; +case class DocCons(hd: Document, tl: Document) extends Document; + +/** + * A basic pretty-printing library, based on Lindig's strict version + * of Wadler's adaptation of Hughes' pretty-printer. + * + * @version 1.0 + * @author Michel Schinz + */ + +abstract class Document { + def ::(hd: Document): Document = DocCons(hd, this); + def ::(hd: String): Document = DocCons(DocText(hd), this); + def :/:(hd: Document): Document = hd :: DocBreak :: this; + def :/:(hd: String): Document = hd :: DocBreak :: this; + + /** + * Format this document on WRITER and try to set line breaks so that + * the result fits in WIDTH columns. + */ + def format(width: Int, writer: Writer): Unit = { + type FmtState = Triple[Int,Boolean,Document]; + + def fits(w: Int, state: List[FmtState]): boolean = state match { + case _ if w < 0 => + false + case List() => + true + case Triple(_, _, DocNil) :: z => + fits(w, z) + case Triple(i, b, DocCons(h, t)) :: z => + fits(w, Triple(i,b,h) :: Triple(i,b,t) :: z) + case Triple(_, _, DocText(t)) :: z => + fits(w - t.length(), z) + case Triple(i, b, DocNest(ii, d)) :: z => + fits(w, Triple(i + ii, b, d) :: z) + case Triple(_, false, DocBreak) :: z => + fits(w - 1, z) + case Triple(_, true, DocBreak) :: z => + true + case Triple(i, _, DocGroup(d)) :: z => + fits(w, Triple(i, false, d) :: z) + } + + def spaces(n: Int): Unit = { + var rem = n; + while (rem >= 16) { writer write " "; rem = rem - 16 }; + if (rem >= 8) { writer write " "; rem = rem - 8 }; + if (rem >= 4) { writer write " "; rem = rem - 4 }; + if (rem >= 2) { writer write " "; rem = rem - 2}; + if (rem == 1) { writer write " " }; + } + + def fmt(k: Int, state: List[FmtState]): Unit = state match { + case List() => () + case Triple(_, _, DocNil) :: z => + fmt(k, z) + case Triple(i, b, DocCons(h, t)) :: z => + fmt(k, Triple(i, b, h) :: Triple(i, b, t) :: z) + case Triple(i, _, DocText(t)) :: z => + writer write t; + fmt(k + t.length(), z) + case Triple(i, b, DocNest(ii, d)) :: z => + fmt(k, Triple(i + ii, b, d) :: z) + case Triple(i, true, DocBreak) :: z => + writer write "\n"; + spaces(i); + fmt(i, z) + case Triple(i, false, DocBreak) :: z => + writer write " "; + fmt(k + 1, z) + case Triple(i, b, DocGroup(d)) :: z => + val fitsFlat = fits(width - k, Triple(i, false, d) :: z); + fmt(k, Triple(i, !fitsFlat, d) :: z) + } + + fmt(0, Triple(0, false, DocGroup(this)) :: Nil); + } +} + +object Document { + /** The empty document */ + def empty = DocNil; + + /** A break, which will either be turned into a space or a line break */ + def break = DocBreak; + + /** A document consisting of some text literal */ + def text(s: String): Document = DocText(s); + + /** + * A group, whose components will either be printed with all breaks + * rendered as spaces, or with all breaks rendered as line breaks. + */ + def group(d: Document): Document = DocGroup(d); + + /** A nested document, which will be indented as specified. */ + def nest(i: Int, d: Document): Document = DocNest(i, d); +} diff --git a/src/library/scala/transient.scala b/src/library/scala/transient.scala new file mode 100644 index 0000000000..d051c80761 --- /dev/null +++ b/src/library/scala/transient.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:transient.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +class transient extends Attribute {} diff --git a/src/library/scala/util/automata/BaseBerrySethi.scala b/src/library/scala/util/automata/BaseBerrySethi.scala new file mode 100644 index 0000000000..ec6a37b79f --- /dev/null +++ b/src/library/scala/util/automata/BaseBerrySethi.scala @@ -0,0 +1,180 @@ +package scala.util.automata; + +import scala.util.regexp.Base; + +import scala.collection.mutable; +import scala.collection.immutable; +import scala.runtime.compat.Platform; + +/** this turns a regexp over A into a NondetWorkAutom over A using the + * celebrated position automata construction (also called Berry-Sethi or + * Glushkov) + */ +abstract class BaseBerrySethi { + + val lang: Base; + import lang.{Alt,Eps,Meta,RegExp,Sequ,Star} ; + + protected var pos = 0;; + + protected var globalFirst: immutable.Set[Int] = _; + + // results which hold all info for the NondetWordAutomaton + protected var follow: mutable.HashMap[Int, immutable.Set[Int]] = _; + + protected var finalTag: Int = _; + + protected var finals: immutable.TreeMap[int,int] = _; // final states + + // constants -------------------------- + + final val emptySet:immutable.Set[Int] = new immutable.TreeSet[Int](); + + /** computes first( r ) for the word regexp r */ + protected def compFirst(r: RegExp): immutable.Set[Int] = r match { + case x:Alt => + var tmp = emptySet; + val it = x.rs.elements; // union + while( it.hasNext ) { tmp = tmp incl compFirst( it.next ); }; + tmp + case Eps => emptySet; + //case x:Letter => emptySet + posMap(x); // singleton set + case x:Meta => compFirst( x.r ) + case x:Sequ => + var tmp = emptySet; + val it = x.rs.elements; // union + while( it.hasNext ) { + val z = it.next; + tmp = tmp incl compFirst( z ); + if( !z.isNullable ) + return tmp + }; + tmp + case _ => error("unexpected pattern " + Platform.getClass(r)); + } + + /** computes last( r ) for the regexp r */ + protected def compLast(r: RegExp): immutable.Set[Int] = r match { + case x:Alt => + var tmp = emptySet; + val it = x.rs.elements; // union + while( it.hasNext ) { tmp = tmp incl compFirst( it.next ); }; + tmp + case Eps => emptySet; + //case x:Letter => emptySet + posMap(x) // singleton set + case x:Meta => compLast( x.r ) + case x:Sequ => + var tmp = emptySet; + val it = x.rs.elements.toList.reverse.elements; // union + while( it.hasNext ) { + val z = it.next; + tmp = tmp incl compLast( z ); + if( !z.isNullable ) + return tmp + }; + tmp + case Star(t) => compLast(t); + case _ => error("unexpected pattern " + Platform.getClass(r)); + } + + // starts from the right-to-left + // precondition: pos is final + // pats are successor patterns of a Sequence node + protected def compFollow(r: Seq[RegExp]): immutable.Set[Int] = { + //Console.println("compFollow( "+r.toList); + var first = emptySet; + var fol = emptySet; + if( r.length > 0 ) {//non-empty expr + + val it = r.elements.toList.reverse.elements; + + fol = fol + pos; // don't modify pos ! + while( it.hasNext ) { + val p = it.next; + //Console.println(" p now = "+p); + first = compFollow1( fol, p ); + //Console.println(" first = "+first); + //Console.println(" fol before = "+fol); + fol = if( p.isNullable ) + fol incl first + else + first; + //Console.println(" fol after = "+fol); + } + } + this.follow.update( 0, fol /*first*/ ); + //Console.println("follow(0) = "+fol); + return fol; + } + + /** returns the first set of an expression, setting the follow set along + * the way + */ + protected def compFollow1( fol1:immutable.Set[Int], r:RegExp): immutable.Set[Int] = { + var fol = fol1; + //System.out.println("compFollow1("+fol+","+r+")"); + r match { + + case x:Alt => + var first = emptySet; + val it = x.rs.elements.toList.reverse.elements; + while( it.hasNext ) + first = first incl compFollow1( fol, it.next ); + first; + + /* + case x:Letter => + val i = posMap( x ); + this.follow.update( i, fol ); + emptySet + i; + */ + case x:Meta => + compFollow1( fol1, x.r ); + + case x:Star => + fol = fol incl compFirst( x.r ); + compFollow1( fol, x.r ); + + case x:Sequ => + var first = emptySet; + val it = x.rs.elements.toList.reverse.elements; + while( it.hasNext ) { + val p = it.next; + first = compFollow1( fol, p ); + fol = if( p.isNullable ) + fol incl first ; + else + first; + } + first; + + case _ => error("unexpected pattern: " + Platform.getClass(r)); + } + } + + /** returns "Sethi-length" of a pattern, creating the set of position + * along the way + */ + + // todo: replace global variable pos with acc + protected def traverse(r: RegExp): Unit = { + r match { // (is tree automaton stuff, more than Berry-Sethi) + + case x:Alt => + val it = x.rs.elements; + while( it.hasNext ) traverse( it.next ); + + case x:Sequ => + val it = x.rs.elements; + while( it.hasNext ) traverse( it.next ); + + case x:Meta => traverse( x.r ) + + case Star(t) => + traverse(t) + + case _ => error("unexp pattern " + Platform.getClass(r)); + } + } + +} diff --git a/src/library/scala/util/automata/DetWordAutom.scala b/src/library/scala/util/automata/DetWordAutom.scala new file mode 100644 index 0000000000..12049e0012 --- /dev/null +++ b/src/library/scala/util/automata/DetWordAutom.scala @@ -0,0 +1,56 @@ +package scala.util.automata ; + +import scala.collection.{ Set, Map }; + +/** A deterministic automaton. States are integers, where + * 0 is always the only initial state. Transitions are represented + * in the delta function. A default transitions is one that + * is taken when no other transition can be taken. + * All states are reachable. Accepting states are those for which + * the partial function 'finals' is defined. + */ +abstract class DetWordAutom[T <: AnyRef] { + + val nstates: Int; + val finals: Array[Int] ; + val delta: Array[Map[T,Int]]; + val default: Array[Int] ; + + def isFinal(q: Int) = finals(q) != 0; + + def isSink(q: Int) = delta(q).isEmpty && default(q) == q; + + def next(q: Int, label: T) = { + delta(q).get(label) match { + case Some(p) => p + case _ => default(q) + } + } + + override def toString() = { + val sb = new StringBuffer(); + sb.append("[DetWordAutom nstates="); + sb.append(nstates); + sb.append(" finals="); + var map = new scala.collection.immutable.ListMap[Int,Int]; + var j = 0; while( j < nstates ) { + if(j < finals.length) + map = map.update(j,finals(j)); + j = j + 1; + } + sb.append(map.toString()); + sb.append(" delta=\n"); + for( val i <- Iterator.range(0,nstates)) { + sb.append( i ); + sb.append("->"); + sb.append(delta(i).toString()); + sb.append('\n'); + if(i < default.length) { + sb.append("_>"); + sb.append(default(i).toString()); + sb.append('\n'); + } + } + sb.toString(); + } +} diff --git a/src/library/scala/util/automata/Inclusion.scala b/src/library/scala/util/automata/Inclusion.scala new file mode 100644 index 0000000000..4b155c7f06 --- /dev/null +++ b/src/library/scala/util/automata/Inclusion.scala @@ -0,0 +1,54 @@ +package scala.util.automata ; + +/** a fast test of language inclusion between minimal automata. + * inspired by the AMoRE automata library + * @author Burak + */ +trait Inclusion[A <: AnyRef] { + + val labels: Seq[A]; + + /** returns true if dfa1 is included in dfa2 */ + def inclusion(dfa1: DetWordAutom[A], dfa2: DetWordAutom[A]) = { + + def encode(q1:Int, q2:Int) = 1 + q1 + q2 * dfa1.nstates; + def decode2(c:Int) = (c-1) / (dfa1.nstates); //integer division + def decode1(c:Int) = (c-1) % (dfa1.nstates); + + var q1 = 0; //dfa1.initstate; // == 0 + var q2 = 0; //dfa2.initstate; // == 0 + + val max = 1 + dfa1.nstates * dfa2.nstates; + val mark = new Array[Int](max); + + var result = true; + var current = encode(q1,q2); + var last = current; + mark(last) = max; // mark (q1,q2) + while(current != 0 && result) { + //Console.println("current = [["+q1+" "+q2+"]] = "+current); + for(val letter <- labels) { + val r1 = dfa1.next(q1,letter); + val r2 = dfa2.next(q2,letter); + if(dfa1.isFinal(r1) && !dfa2.isFinal(r2)) + result = false; + val test = encode(r1,r2); + //Console.println("test = [["+r1+" "+r2+"]] = "+test); + if(mark(test) == 0) { + mark(last) = test; + mark(test) = max; + last = test; + } + } + val ncurrent = mark(current); + if( ncurrent != max ) { + q1 = decode1(ncurrent); + q2 = decode2(ncurrent); + current = ncurrent + } else { + current = 0 + } + } + result + } +} diff --git a/src/library/scala/util/automata/NondetWordAutom.scala b/src/library/scala/util/automata/NondetWordAutom.scala new file mode 100644 index 0000000000..a0a2757233 --- /dev/null +++ b/src/library/scala/util/automata/NondetWordAutom.scala @@ -0,0 +1,101 @@ +package scala.util.automata ; + +import scala.collection.{ immutable, mutable, Set, Map }; + +/** A nondeterministic automaton. States are integers, where + * 0 is always the only initial state. Transitions are represented + * in the delta function. Default transitions are transitions that + * are taken when no other transitions can be applied. + * All states are reachable. Accepting states are those for which + * the partial function 'finals' is defined. + */ +abstract class NondetWordAutom[T <: AnyRef] { + + val nstates: Int; + val labels: Seq[T]; + + val finals: Array[Int] ; // 0 means not final + val delta: Array[Map[T, immutable.BitSet]]; + val default: Array[immutable.BitSet]; + + /** returns true if the state is final */ + final def isFinal(state: Int) = finals( state ) > 0; + + /** returns tag of final state */ + final def finalTag(state: Int) = finals( state ); + + /** returns true if the set of states contains at least one final state */ + final def containsFinal(Q: immutable.BitSet): Boolean = { + val it = Q.toSet(true).elements; + while( it.hasNext ) + if( isFinal( it.next )) + return true; + return false; + } + + /** returns true if there are no accepting states */ + final def isEmpty = { + var r = true; + var j = 0; while( r && ( j < nstates )) { + if(isFinal(j)) + r = false; + } + r + } + + /** returns a bitset with the next states for given state and label */ + def next(q:Int, a: T): immutable.BitSet = { + delta(q).get(a) match { + case Some(bs) => bs + case _ => default(q) + } + } + + /** returns a bitset with the next states for given state and label */ + def next(Q:immutable.BitSet, a: T): immutable.BitSet = { + val x = new mutable.BitSet(nstates); + for(val q <- Q.toSet(true)) { + for(val i <- next(q,a).toSet(true)) { + x.set(i); + } + } + x.makeImmutable + } + + + def nextDefault(Q:immutable.BitSet): immutable.BitSet = { + val x = new mutable.BitSet(nstates); + for(val q <- Q.toSet(true)) { + for(val i <- default(q).toSet(true)) { //@todo: OR + x.set(i) + } + } + x.makeImmutable; + } + + override def toString() = { + val sb = new StringBuffer(); + sb.append("[NondetWordAutom nstates="); + sb.append(nstates); + sb.append(" finals="); + var map = new scala.collection.immutable.ListMap[Int,Int]; + var j = 0; while( j < nstates ) { + if(isFinal(j)) + map = map.update(j,finals(j)); + j = j + 1; + } + sb.append(map.toString()); + sb.append(" delta=\n"); + for( val i <- Iterator.range(0,nstates)) { + sb.append(" "); + sb.append( i ); + sb.append("->"); + sb.append(delta(i).toString()); + sb.append("\n "); + sb.append(" _>"); + sb.append(default(i).toString()); + sb.append('\n'); + } + sb.toString(); + } +} diff --git a/src/library/scala/util/automata/SubsetConstruction.scala b/src/library/scala/util/automata/SubsetConstruction.scala new file mode 100644 index 0000000000..ddf122dbe3 --- /dev/null +++ b/src/library/scala/util/automata/SubsetConstruction.scala @@ -0,0 +1,143 @@ +package scala.util.automata ; + +class SubsetConstruction[T <: AnyRef](val nfa: NondetWordAutom[T]) { + + //import nfa.{ _labelT, labels }; + import nfa.labels ; + import scala.collection.{immutable, mutable, Map} ; + + import immutable.{ BitSet, TreeMap, TreeSet } ; + + /** the set {0} */ + final val _initialBitSet = { + val rbs = new mutable.BitSet(1); + rbs.set(0); + rbs.makeImmutable; + } + + /** the set {} */ + final val _sinkBitSet = { + new mutable.BitSet(1).makeImmutable; + } + + final val _emptyBitSet = { + val rbs = new scala.collection.mutable.BitSet(1); + new BitSet(rbs); + } + + def selectTag(Q:BitSet, finals:Array[Int]) = { + val it = Q.toSet(true).elements; + var mintag = scala.runtime.compat.Math.MAX_INT; + while(it.hasNext) { + val tag = finals(it.next); + if((0 < tag) && (tag < mintag)) + mintag = tag + } + mintag + } + + def determinize: DetWordAutom[ T ] = { + + // for assigning numbers to bitsets + var indexMap = new TreeMap[ BitSet, Int ]; + var invIndexMap = new TreeMap[ Int, BitSet ]; + var ix = 0; + + // we compute the dfa with states = bitsets + var states = new TreeSet[BitSet](); + val delta = new mutable.HashMap[BitSet, + mutable.HashMap[T, BitSet]]; + var deftrans = new TreeMap[BitSet, BitSet]; + var finals = new TreeMap[BitSet, Int]; + + val q0 = _initialBitSet; + states = states + q0; + + val sink = _emptyBitSet; + states = states + sink; + + deftrans = deftrans.update(q0,sink); + deftrans = deftrans.update(sink,sink); + + val rest = new mutable.Stack[BitSet](); + + def add(Q: BitSet): Unit = { + if(!states.contains(Q)) { + states = states + Q; + rest.push(Q); + if(nfa.containsFinal(Q)) + finals = finals.update(Q, selectTag(Q,nfa.finals)); + } + } + rest.push( sink ); + val sinkIndex = 1; + rest.push( q0 ); + while(!rest.isEmpty) { + // assign a number to this bitset + val P = rest.pop; + indexMap = indexMap.update(P,ix); + invIndexMap = invIndexMap.update(ix,P); + ix = ix + 1; + + // make transitiion map + val Pdelta = new mutable.HashMap[T, BitSet]; + delta.update( P, Pdelta ); + + val it = labels.elements; while(it.hasNext) { + val label = it.next; + + val Q = nfa.next(P,label); + + Pdelta.update( label, Q ); + + add(Q); + } + + // collect default transitions + val Pdef = nfa.nextDefault(P); + deftrans = deftrans.update(P,Pdef); + add(Pdef); + }; + + // create DetWordAutom, using indices instead of sets + val nstatesR = states.size; + val deltaR = new Array[Map[T,Int]](nstatesR); + val defaultR = new Array[Int](nstatesR); + val finalsR = new Array[Int](nstatesR); + + for(val w <- states) { + val Q = w; + val q = indexMap(Q); + val trans = delta(Q); + val transDef = deftrans(Q); + val qDef = indexMap(transDef); + val ntrans = new mutable.HashMap[T,Int](); + val it = trans.keys; while(it.hasNext) { + val label = it.next; + val p = indexMap(trans(label)); + if( p != qDef ) + ntrans.update(label, p) + } + deltaR.update(q, ntrans); + defaultR.update(q, qDef); + + //cleanup? leave to garbage collector? + //delta.remove(Q); + //default.remove(Q); + + } + + for(val fQ <- finals.keys) { + finalsR(indexMap(fQ)) = finals(fQ); + } + + new DetWordAutom [ T ] { + + //type _labelT = SubsetConstruction.this.nfa._labelT; + val nstates = nstatesR; + val delta = deltaR; + val default = defaultR; + val finals = finalsR; + } + } +} diff --git a/src/library/scala/util/automata/WordBerrySethi.scala b/src/library/scala/util/automata/WordBerrySethi.scala new file mode 100644 index 0000000000..f361b6b174 --- /dev/null +++ b/src/library/scala/util/automata/WordBerrySethi.scala @@ -0,0 +1,275 @@ +package scala.util.automata ; + +import scala.util.regexp.WordExp ; + +import scala.collection.{immutable, + mutable, + Map } ; + +/** this turns a regexp into a NondetWordAutom using the + * celebrated position automata construction (also called Berry-Sethi or + * Glushkov) + */ +abstract class WordBerrySethi extends BaseBerrySethi { + + override val lang: WordExp; + + type _labelT = this.lang._labelT; + + import lang.{Alt, Eps, Letter, Meta, RegExp, Sequ, Star} ; + + + protected var labels:mutable.HashSet[_labelT] = _ ; + // don't let this fool you, only labelAt is a real, surjective mapping + protected var labelAt: immutable.TreeMap[Int, _labelT] = _; // new alphabet "gamma" + + protected var deltaq: Array[mutable.HashMap[_labelT,List[Int]]] = _; // delta + + protected var defaultq: Array[List[Int]] = _; // default transitions + + protected var initials:immutable.Set[Int] = _ ; + //NondetWordAutom revNfa ; + + // maps a letter to an Integer ( the position ) + // is not *really* needed (preorder determines position!) + //protected var posMap: mutable.HashMap[RegExp, Int] = _; + + /** computes first( r ) where the word regexp r */ + protected override def compFirst(r: RegExp): immutable.Set[Int] = r match { + case x:Letter => emptySet + x.pos ;//posMap(x); // singleton set + case Eps => emptySet /*ignore*/ + case _ => super.compFirst(r); + } + + /** computes last( r ) where the word regexp r */ + protected override def compLast(r: RegExp): immutable.Set[Int] = r match { + case x:Letter => emptySet + x.pos; //posMap(x) // singleton set + case Eps => emptySet /*ignore*/ + case _ => super.compLast(r) + } + + /** returns the first set of an expression, setting the follow set along + * the way + */ + protected override def compFollow1( fol1:immutable.Set[Int], r:RegExp ): immutable.Set[Int] = + r match { + + case x:Letter => + //val i = posMap( x ); + val i = x.pos; + this.follow.update( i, fol1 ); + emptySet + i; + + case Eps => emptySet /*ignore*/ + + case _ => super.compFollow1(fol1, r) + + } + + /** returns "Sethi-length" of a pattern, creating the set of position + * along the way + */ + + + /** called at the leaves of the regexp */ + protected def seenLabel( r:RegExp, i:Int, label: _labelT ): Unit = { + //Console.println("seenLabel (1)"); + //this.posMap.update( r, i ); + this.labelAt = this.labelAt.update( i, label ); + //@ifdef if( label != Wildcard ) { + this.labels += label ; + //@ifdef } + } + + // overriden in BindingBerrySethi + protected def seenLabel( r: RegExp, label: _labelT ): Int = { + //Console.println("seenLabel (2)"); + pos = pos + 1; + seenLabel( r, pos, label ); + pos + } + + + // todo: replace global variable pos with acc + override def traverse(r: RegExp): Unit = r match { + case a @ Letter( label ) => a.pos = seenLabel( r, label ) ; + case Eps => /*ignore*/ + case _ => super.traverse(r) + } + + + protected def makeTransition(src: Int, dest:Int, label: _labelT ):Unit = { + //@ifdef compiler if( label == Wildcard ) + //@ifdef compiler defaultq.update(src, dest::defaultq( src )) + //@ifdef compiler else + val q = deltaq( src ); + q.update(label, dest::(q.get(label) match { + case Some(x) => x + case _ => Nil + })); + } + + protected def initialize(subexpr: Seq[RegExp]): Unit = { + //this.posMap = new mutable.HashMap[RegExp,Int](); + this.labelAt = new immutable.TreeMap[Int,_labelT](); + this.follow = new mutable.HashMap[Int,immutable.Set[Int]](); + this.labels = new mutable.HashSet[_labelT](); + + this.pos = 0; + + // determine "Sethi-length" of the regexp + //activeBinders = new Vector(); + var it = subexpr.elements; + while( it.hasNext ) + traverse( it.next ); + + //assert ( activeBinders.isEmpty() ); + this.initials = emptySet + 0; + } + + protected def initializeAutom(): Unit = { + + finals = immutable.TreeMap.Empty[Int,Int]; // final states + deltaq = new Array[mutable.HashMap[_labelT,List[Int]]]( pos ); // delta + defaultq = new Array[List[Int]]( pos ); // default transitions + + var j = 0; + while( j < pos ) { + deltaq( j ) = new mutable.HashMap[_labelT,List[Int]](); + defaultq( j ) = Nil; + j = j + 1 + } + } + + protected def collectTransitions(): Unit = { // make transitions + //Console.println("WBS.collectTrans, this.follow.keys = "+this.follow.keys); + //Console.println("WBS.collectTrans, pos = "+this.follow.keys); + var j = 0; while( j < pos ) { + //Console.println("WBS.collectTrans, j = "+j); + val fol = this.follow( j ); + val it = fol.elements; + while( it.hasNext ) { + val k = it.next; + if( pos == k ) + finals = finals.update( j, finalTag ) + else + makeTransition( j, k, labelAt( k )); + } + j = j + 1; + } + } + + def automatonFrom(pat: RegExp, finalTag: Int): NondetWordAutom[_labelT] = { + this.finalTag = finalTag; + + pat match { + case x:Sequ => + // (1,2) compute follow + first + initialize( x.rs ); + pos = pos + 1; + globalFirst = compFollow( x.rs ); + + //System.out.print("someFirst:");debugPrint(someFirst); + // (3) make automaton from follow sets + initializeAutom(); + collectTransitions(); + + if( x.isNullable ) // initial state is final + finals = finals.update( 0, finalTag ); + + var delta1: immutable.TreeMap[Int,Map[_labelT,List[Int]]] = + new immutable.TreeMap[Int,Map[_labelT,List[Int]]]; + + var i = 0; + while( i < deltaq.length ) { + delta1 = delta1.update( i, deltaq( i )); + i = i + 1; + } + val finalsArr = new Array[Int](pos); + { + var k = 0; while(k < pos) { + finalsArr(k) = finals.get(k) match { + case Some(z) => z; + case None => 0; // 0 == not final + }; + k = k + 1; + } + } + + val initialsArr = new Array[Int](initials.size); + val it = initials.elements; + { + var k = 0; while(k < initials.size) { + initialsArr(k) = it.next; + k = k + 1; + } + } + + val deltaArr = new Array[Map[_labelT,immutable.BitSet]](pos); + { + var k = 0; while(k < pos) { + val labels = delta1(k).keys; + val hmap = + new mutable.HashMap[_labelT,immutable.BitSet]; + for(val lab <- labels) { + val trans = delta1(k); + val x = new mutable.BitSet(pos); + for(val q <- trans(lab)) + x.set(q); + hmap.update(lab, x.makeImmutable); + } + deltaArr(k) = hmap; + k = k + 1; + } + } + val defaultArr = new Array[immutable.BitSet](pos); + { + var k = 0; while(k < pos) { + val x = new mutable.BitSet(pos); + for(val q <- defaultq(k)) + x.set(q); + defaultArr(k) = x.makeImmutable; + k = k + 1; + } + } + + new NondetWordAutom[_labelT] { + type _labelT = WordBerrySethi.this._labelT; + val nstates = pos; + val labels = WordBerrySethi.this.labels.toList; + val initials = initialsArr; + val finals = finalsArr; + val delta = deltaArr; + val default = defaultArr; + } + case z:this.lang._regexpT => automatonFrom(Sequ(z), finalTag); + } + } + + /* + void print1() { + System.out.println("after sethi-style processing"); + System.out.println("#positions:" + pos); + System.out.println("posMap:"); + + for( Iterator it = this.posMap.keySet().iterator(); + it.hasNext(); ) { + Tree t = (Tree) it.next(); + switch(t) { + case Literal( _ ): + System.out.print( "(" + t.toString() + " -> "); + String s2 = ((Integer) posMap.get(t)).toString(); + System.out.print( s2 +") "); + } + } + System.out.println("\nfollow: "); + for( int j = 1; j < pos; j++ ) { + TreeSet fol = (TreeSet) this.follow.get(new Integer(j)); + System.out.print("("+j+" -> "+fol.toString()+") "); + //debugPrint( fol ); + System.out.println(); + } + + } + */ +} diff --git a/src/library/scala/util/grammar/HedgeRHS.scala b/src/library/scala/util/grammar/HedgeRHS.scala new file mode 100644 index 0000000000..e435909eb9 --- /dev/null +++ b/src/library/scala/util/grammar/HedgeRHS.scala @@ -0,0 +1,12 @@ +package scala.util.grammar; + +abstract class HedgeRHS; + +/** right hand side of a hedge production, deriving a single tree */ +case class ConsRHS(tnt: Int, hnt: Int) extends HedgeRHS; + +/** right hand side of a hedge production, deriving any hedge */ +case object AnyHedgeRHS extends HedgeRHS; + +/** right hand side of a hedge production, deriving the empty hedge */ +case object EmptyHedgeRHS extends HedgeRHS; diff --git a/src/library/scala/util/grammar/TreeRHS.scala b/src/library/scala/util/grammar/TreeRHS.scala new file mode 100644 index 0000000000..e4d391e12f --- /dev/null +++ b/src/library/scala/util/grammar/TreeRHS.scala @@ -0,0 +1,9 @@ +package scala.util.grammar; + +/** right hand side of a tree production */ +abstract class TreeRHS; + +/** right hand side of a tree production, labelled with a letter from an alphabet */ +case class LabelledRHS[A](label: A, hnt: Int) extends TreeRHS; + +case object AnyTreeRHS extends TreeRHS; diff --git a/src/library/scala/util/logging/ConsoleLogger.scala b/src/library/scala/util/logging/ConsoleLogger.scala new file mode 100644 index 0000000000..fabfa7a698 --- /dev/null +++ b/src/library/scala/util/logging/ConsoleLogger.scala @@ -0,0 +1,12 @@ +// $Id$ + +package scala.util.logging; +/** + * A ConsoleLogger is mixed into a concrete class who has class Logged + * among its base traits. + */ +trait ConsoleLogger { + /** logs argument to Console using Console.println + */ + def log(msg:String): Unit = Console.println(msg); +} diff --git a/src/library/scala/util/logging/Logged.scala b/src/library/scala/util/logging/Logged.scala new file mode 100644 index 0000000000..a19429c1f6 --- /dev/null +++ b/src/library/scala/util/logging/Logged.scala @@ -0,0 +1,23 @@ +// $Id$ + +package scala.util.logging; + +/** + * Mixing in the trait Logged indicates that a class provides support + * for logging. For instance, a developer of a library writes + * <code> + class MyClass with Logged { ... do stuff, call log } + </code> + * + * The user of the library instantiates: + <code> + val x = new MyClass() with ConsoleLogger; + </code> + * and the logging will be sent to the Console. + */ +trait Logged { + /** this method should log the message given as argument somewhere + * as a side-effect + */ + def log(msg:String): Unit = {}; +} diff --git a/src/library/scala/util/parsing/CharInputStreamIterator.scala b/src/library/scala/util/parsing/CharInputStreamIterator.scala new file mode 100644 index 0000000000..16ba724619 --- /dev/null +++ b/src/library/scala/util/parsing/CharInputStreamIterator.scala @@ -0,0 +1,35 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.util.parsing; + +import java.io._; + +class CharInputStreamIterator(in: InputStream) extends Iterator[char] { + + private var ch: int = _; + private var chSet = false; + private var error: IOException = null; + + private def lookahead: unit = try { + ch = in.read(); chSet = ch >= 0; + } catch { + case (ex: EOFException) => ch = -1 + case (ex: IOException) => ch = 1; error = ex + } + + def hasNext: boolean = { + if (!chSet) lookahead; + chSet + } + + def next: char = { + if (!chSet) lookahead; + chSet = false; + ch.asInstanceOf[char] + } +} diff --git a/src/library/scala/util/parsing/Parsers.scala b/src/library/scala/util/parsing/Parsers.scala new file mode 100644 index 0000000000..e0064f12e5 --- /dev/null +++ b/src/library/scala/util/parsing/Parsers.scala @@ -0,0 +1,68 @@ +package scala.util.parsing; + +abstract class Parsers { + + type inputType; + + trait Parser[a] { + + type Result = Option[Pair[a, inputType]]; + + def apply(in: inputType): Result; + + def filter(pred: a => boolean) = new Parser[a] { + def apply(in: inputType): Result = Parser.this.apply(in) match { + case None => None + case Some(Pair(x, in1)) => if (pred(x)) Some(Pair(x, in1)) else None + } + } + + def map[b](f: a => b) = new Parser[b] { + def apply(in: inputType): Result = Parser.this.apply(in) match { + case None => None + case Some(Pair(x, in1)) => Some(Pair(f(x), in1)) + } + } + + def flatMap[b](f: a => Parser[b]) = new Parser[b] { + def apply(in: inputType): Result = Parser.this.apply(in) match { + case None => None + case Some(Pair(x, in1)) => f(x).apply(in1) + } + } + + def ||| (p: => Parser[a]) = new Parser[a] { + def apply(in: inputType): Result = Parser.this.apply(in) match { + case None => p(in) + case s => s + } + } + + def &&& [b](p: => Parser[b]): Parser[b] = + for (val _ <- this; val x <- p) yield x; + } + + def not[a](p: Parser[a]) = new Parser[unit] { + def apply(in: inputType): Result = p.apply(in) match { + case None => Some(Pair((), in)) + case Some(_) => None + } + } + + def succeed[a](x: a) = new Parser[a] { + def apply(in: inputType): Result = Some(Pair(x, in)) + } + + def rep[a](p: Parser[a]): Parser[List[a]] = + rep1(p) ||| succeed(List()); + + def rep1[a](p: Parser[a]): Parser[List[a]] = + for (val x <- p; val xs <- rep(p)) yield x :: xs; + + def repWith[a, b](p: Parser[a], sep: Parser[b]): Parser[List[a]] = + for (val x <- p; val xs <- rep(sep &&& p)) yield x :: xs; + + def opt[a](p: Parser[a]): Parser[List[a]] = + (for (val x <- p) yield List(x)) ||| succeed(List()); +} + diff --git a/src/library/scala/util/parsing/SimpleTokenizer.scala b/src/library/scala/util/parsing/SimpleTokenizer.scala new file mode 100644 index 0000000000..20a23a8492 --- /dev/null +++ b/src/library/scala/util/parsing/SimpleTokenizer.scala @@ -0,0 +1,46 @@ +package scala.util.parsing; + +class SimpleTokenizer(in: Iterator[char], delimiters: String) extends Iterator[String] { + + val tracing = false; + + private def max(x: int, y: char): int = if (x < y) y else x; + + private def delimArray: Array[boolean] = { + val ds = List.fromString(delimiters); + val da = new Array[boolean]((0 /: ds)(max) + 1); + for (val ch <- ds) { da(ch) = true } + da + } + + private val isdelim = delimArray; + private def isDelimiter(ch: int) = ch >= 0 && ch < isdelim.length && isdelim(ch); + + private val EOI = -1; + + private def nextChar: int = if (in.hasNext) in.next else EOI; + + private var ch: int = nextChar; + + private val buf = new StringBuffer(); + + def hasNext: boolean = ch != EOI; + + def next: String = { + while (ch <= ' ' && ch != EOI) ch = nextChar; + if (ch == EOI) "" + else { + buf.setLength(0); + if (isDelimiter(ch)) { + buf append ch.asInstanceOf[char]; ch = nextChar + } else { + while (ch > ' ' && ch != EOI && !isDelimiter(ch)) { + buf append ch.asInstanceOf[char]; ch = nextChar; + } + } + if (tracing) System.out.println("<" + buf.toString() + ">"); + buf.toString() + } + } +} + diff --git a/src/library/scala/util/regexp/Base.scala b/src/library/scala/util/regexp/Base.scala new file mode 100644 index 0000000000..cfdaaac10f --- /dev/null +++ b/src/library/scala/util/regexp/Base.scala @@ -0,0 +1,63 @@ +// $Id$ + +package scala.util.regexp ; + +/** basic regular expressions */ + +trait Base { + + type _regexpT <: RegExp; + + abstract class RegExp { + val isNullable:Boolean; + } + + /** Alt( R,R,R* ) */ + case class Alt(rs: _regexpT*) extends RegExp { + + // check rs \in R,R,R* + // @todo: flattening + if({ val it = rs.elements; !it.hasNext || {it.next; !it.hasNext }}) + throw new SyntaxError("need at least 2 branches in Alt"); + + final val isNullable = { + val it = rs.elements; + while( it.hasNext && it.next.isNullable ) {} + !it.hasNext + } + } + case class Sequ(rs: _regexpT*) extends RegExp { + // @todo: flattening + // check rs \in R,R* + if({ val it = rs.elements; !it.hasNext }) + throw new SyntaxError("need at least 1 item in Sequ"); + + final val isNullable = { + val it = rs.elements; + while( it.hasNext && it.next.isNullable ) {} + !it.hasNext + } + } + + case class Star(r: _regexpT) extends RegExp { + final val isNullable = true; + } + + case object Eps extends RegExp { + final val isNullable = true; + override def toString() = "Eps"; + } + + /** this class can be used to add meta information to regexps */ + class Meta( r1: _regexpT ) extends RegExp { + final val isNullable = r1.isNullable; + def r = r1; + } + + final def mkSequ(rs: _regexpT *): RegExp = + if(!rs.elements.hasNext) + Eps + else + Sequ(rs:_*); + +} diff --git a/src/library/scala/util/regexp/PointedHedgeExp.scala b/src/library/scala/util/regexp/PointedHedgeExp.scala new file mode 100644 index 0000000000..27da6338db --- /dev/null +++ b/src/library/scala/util/regexp/PointedHedgeExp.scala @@ -0,0 +1,25 @@ +// $Id$ + +package scala.util.regexp ; + +/** pointed regular hedge expressions, a useful subclass of + * regular hedge expressions. + */ +trait PointedHedgeExp extends Base { + + type _regexpT <: RegExp; + type _labelT; + + case class Node(label: _labelT, r: _regexpT) extends RegExp { + final val isNullable = false; + } + + case class TopIter(r1: _regexpT, r2: _regexpT) extends RegExp { + final val isNullable = r1.isNullable && r2.isNullable; //? + } + + case object Point extends RegExp { + final val isNullable = false; + } + +} diff --git a/src/library/scala/util/regexp/SyntaxError.scala b/src/library/scala/util/regexp/SyntaxError.scala new file mode 100644 index 0000000000..c6599df75c --- /dev/null +++ b/src/library/scala/util/regexp/SyntaxError.scala @@ -0,0 +1,6 @@ +package scala.util.regexp ; + +/** this runtime exception is thrown if an attempt to instantiate a + * syntactically incorrect expression is detected */ +class SyntaxError(e: String) + extends java.lang.RuntimeException(e); diff --git a/src/library/scala/util/regexp/WordExp.scala b/src/library/scala/util/regexp/WordExp.scala new file mode 100644 index 0000000000..63817e0151 --- /dev/null +++ b/src/library/scala/util/regexp/WordExp.scala @@ -0,0 +1,24 @@ +// $Id$ + +package scala.util.regexp ; + +/** regular word expressions. + */ +trait WordExp extends Base { + + trait Label; + + type _regexpT <: RegExp ; + type _labelT <: Label; + + case class Letter(a: _labelT) extends RegExp { + final val isNullable = false; + var pos = -1; + } + + case class Wildcard() extends RegExp { + final val isNullable = false; + var pos = -1; + } +} + diff --git a/src/library/scala/volatile.scala b/src/library/scala/volatile.scala new file mode 100644 index 0000000000..905d95371e --- /dev/null +++ b/src/library/scala/volatile.scala @@ -0,0 +1,12 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id:volatile.scala 5359 2005-12-16 16:33:49 +0100 (Fri, 16 Dec 2005) dubochet $ +\* */ + +package scala; + +class volatile extends Attribute {} diff --git a/src/library/scala/xml/Atom.scala b/src/library/scala/xml/Atom.scala new file mode 100644 index 0000000000..d37d6dbf56 --- /dev/null +++ b/src/library/scala/xml/Atom.scala @@ -0,0 +1,41 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** an XML node for text (PCDATA). Used in both non-bound and bound XML + * representations + * @author Burak Emir + * @param text the text contained in this node, may not be null. + */ +[serializable] +class Atom[+A]( val data: A ) extends SpecialNode { + + final override def typeTag$:Int = -1; + + /** the constant "#PCDATA" + */ + def label = "#PCDATA"; + + override def equals(x:Any) = x match { + case s:Atom[A] => data == s.data ; + case _ => false; + } + + /** hashcode for this Text */ + override def hashCode() = + data.hashCode(); + + /** returns text, with some characters escaped according to XML spec */ + def toString(sb:StringBuffer) = + Utility.escape( data.toString(), sb ); + + override def text: String = data.toString(); + +} diff --git a/src/library/scala/xml/Comment.scala b/src/library/scala/xml/Comment.scala new file mode 100644 index 0000000000..4bf7a267f7 --- /dev/null +++ b/src/library/scala/xml/Comment.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +import scala.collection.immutable ; + +/** an XML node for comments. + * + * @author Burak Emir + * @param text text contained in this node, may not contain "--" + */ + +case class Comment(commentText: String) extends SpecialNode { + + final override def typeTag$:Int = -3; + + if( commentText.indexOf("--" ) != -1 ) + throw new IllegalArgumentException("text containts \"--\""); + + /** structural equality */ + override def equals(x: Any): Boolean = x match { + case Comment(x) => x.equals(commentText); + case _ => false + } + + /** the constant "#REM" */ + def label = "#REM"; + + /** hashcode for this Comment */ + override def hashCode() = commentText.hashCode(); + + override def text = ""; + + /** appends "<!-- text -->" to this stringbuffer */ + def toString(sb: StringBuffer) = { + sb.append("<!--").append(commentText).append("-->") + } +} diff --git a/src/library/scala/xml/Document.scala b/src/library/scala/xml/Document.scala new file mode 100644 index 0000000000..38503eb834 --- /dev/null +++ b/src/library/scala/xml/Document.scala @@ -0,0 +1,84 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +/** A document information item (according to InfoSet spec). The comments + * are copied from the Infoset spec, only augmented with some information + * on the Scala types for definitions that might have no value. + */ +class Document extends NodeSeq { + + /** An ordered list of child information items, in document + * order. The list contains exactly one element information item. The + * list also contains one processing instruction information item for + * each processing instruction outside the document element, and one + * comment information item for each comment outside the document + * element. Processing instructions and comments within the DTD are + * excluded. If there is a document type declaration, the list also + * contains a document type declaration information item. + */ + var children: Seq[Node] = _; + + /** The element information item corresponding to the document element. */ + var docElem: Node = _; + + /** The dtd that comes with the document, if any */ + var dtd: scala.xml.dtd.DTD = _; + + /** An unordered set of notation information items, one for each notation + * declared in the DTD. If any notation is multiply declared, this property + * has no value. + */ + def notations: Seq[scala.xml.dtd.NotationDecl] = + dtd.notations; + + /** An unordered set of unparsed entity information items, one for each + * unparsed entity declared in the DTD. + */ + def unparsedEntities: Seq[scala.xml.dtd.EntityDecl] = + dtd.unparsedEntities; + + /** The base URI of the document entity. */ + var baseURI: String = _; + + /** The name of the character encoding scheme in which the document entity + * is expressed. + */ + var encoding: Option[String] = _; + + /** An indication of the standalone status of the document, either + * true or false. This property is derived from the optional standalone + * document declaration in the XML declaration at the beginning of the + * document entity, and has no value (None) if there is no standalone + * document declaration. + */ + var standAlone: Option[Boolean] = _; + + /** A string representing the XML version of the document. This + * property is derived from the XML declaration optionally present at + * the beginning of the document entity, and has no value (None) if there is + * no XML declaration. + */ + var version: Option[String] = _; + + /** 9. This property is not strictly speaking part of the infoset of + * the document. Rather it is an indication of whether the processor + * has read the complete DTD. Its value is a boolean. If it is false, + * then certain properties (indicated in their descriptions below) may + * be unknown. If it is true, those properties are never unknown. + */ + var allDeclarationsProcessed = false; + + // methods for NodeSeq + + def theSeq: Seq[Node] = this.docElem ; + +} diff --git a/src/library/scala/xml/Elem.scala b/src/library/scala/xml/Elem.scala new file mode 100644 index 0000000000..6a6d70d1e6 --- /dev/null +++ b/src/library/scala/xml/Elem.scala @@ -0,0 +1,74 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +import scala.collection.mutable.ArrayBuffer; + +/** The case class <code>Elem</code> implements the Node trait, + * providing an immutable data object representing an XML element. + * + * @param prefix (may be null) + * @param label the element name + * @param attribute the attribute map + * @param child the children of this node + * @author Burak Emir + */ +// "val" is redundant for non-overriding arguments +case class Elem(override val prefix: String, + val label: String, + override val attributes: MetaData, + override val scope: NamespaceBinding, + val child: Node*) extends Node { + + if (prefix != null && 0 == prefix.length()) + scala.Predef.error("prefix of zero length, use null instead"); + + if (null == scope) + scala.Predef.error("scope is null"); + + //@todo: copy the children, + // setting namespace scope if necessary + // cleaning adjacent text nodes if necessary + + //final val namespaceIntern = namespace$$.intern(); + //final def namespace = namespaceIntern; + + //final val labelIntern = label$$.intern(); + //final def label = labelIntern; + + final override def typeTag$: Int = 0; + + override def hashCode(): Int = { + Utility.hashCode(prefix, label, attributes.hashCode(), scope.hashCode(), child); + } + /** Return a new element with updated attributes + * + * @param attrs + * @return a new symbol with updated attributes + */ + final def %(attrs: MetaData): Elem = + Elem(prefix, + label, + attrs.append(attributes), + scope, + child:_*); + + /* returns concatenation of text(n) for each child n */ + override def text = { + val sb = new StringBuffer(); + val it = child.elements; + while(it.hasNext) { + sb.append(it.next.text); + } + sb.toString() + } + +} diff --git a/src/library/scala/xml/EntityRef.scala b/src/library/scala/xml/EntityRef.scala new file mode 100644 index 0000000000..3ae1ef6866 --- /dev/null +++ b/src/library/scala/xml/EntityRef.scala @@ -0,0 +1,40 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** an XML node for entity references + * + * @author buraq + * @param text the text contained in this node + **/ + +case class EntityRef(entityName: String) extends SpecialNode { + + final override def typeTag$:Int = -5; + + /** structural equality */ + override def equals(x: Any): Boolean = x match { + case EntityRef(x) => x.equals(entityName); + case _ => false + } + + /** the constant "#ENTITY" + */ + def label = "#ENTITY"; + + override def hashCode() = entityName.hashCode(); + + override def text = ""; + + /** appends "& entityName;" to this stringbuffer */ + def toString(sb:StringBuffer) = + sb.append("&").append(entityName).append(";"); + +} diff --git a/src/library/scala/xml/MalformedAttributeException.scala b/src/library/scala/xml/MalformedAttributeException.scala new file mode 100644 index 0000000000..bcb4c0f393 --- /dev/null +++ b/src/library/scala/xml/MalformedAttributeException.scala @@ -0,0 +1,3 @@ +package scala.xml; + +case class MalformedAttributeException(msg:String) extends java.lang.RuntimeException(msg); diff --git a/src/library/scala/xml/MetaData.scala b/src/library/scala/xml/MetaData.scala new file mode 100644 index 0000000000..285e3eb850 --- /dev/null +++ b/src/library/scala/xml/MetaData.scala @@ -0,0 +1,159 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +/** Attribute information item, and linked list of attribute information items. + * These are triples consisting of prefix,key,value. To obtain the namespace, + * getNamespace must be called with the parent. If next is null, this is + * the last attribute in the MetaData list. + * + * either an UnprefixedAttribute or a PrefixedAttribute + * + * @todo _vlue should be a normalized attribute value + */ +[serializable] +abstract class MetaData extends Iterable[MetaData] { + + /** appends given MetaData items to this MetaData list */ + def append(m: MetaData): MetaData = + next.append(copy(m)); + + def apply(s:String) = getValue(s); + + def apply(uri:String, scp:NamespaceBinding, k:String)= getValue(uri, scp, k); + + def containedIn1(m: MetaData): Boolean = + m.equals1(this) || containedIn1(m.next); + + /** returns a copy of this MetaData item with next field set to argument */ + def copy(next: MetaData): MetaData; + + /** if owner is the element of this metadata item, returns namespace */ + def getNamespace(owner: Node): String; + + def hasNext = (Null != next); + + def length: Int = length(0); + + def length(i: Int): Int = next.length(i + 1); + + def isPrefixed: Boolean; + + //def containedIn(m:MetaData): Boolean; + + //def deepCopy: MetaData; + + //def deepCopy(tail:MetaData): MetaData; + + /** deep equals method */ + override def equals(that: Any) = { + that match { + case m: MetaData => + var res = (this.length == m.length) && (this.hashCode() == m.hashCode()); + val it = this.elements; + while (res && it.hasNext) { res = it.next.containedIn1(m) } + res + + case _ => false; + } + } + + /** returns an iterator on attributes */ + def elements = new Iterator[MetaData] { + var x: MetaData = MetaData.this; + def hasNext = Null != x; + def next = { + val y = x; + x = x.next; + y + } + } + + /* returns a sequences of "pseudo nodes" that contain attribute info. + not sure if this useful, and it violates contract for nodes... + def nodes = { + class NodeProxy(last:MetaData) extends Node { + override def prefix = last match { + case p:PrefixedAttribute => p.pre; + case _ => null + } + override def label = "@"+last.key; + override def child = Text(last.value); + override def text = last.value + } + val ns = new Array[Node](this.length); + var i = 0; + val it = elements; + while(it.hasNext) { + val a = it.next; + ns(i) = new NodeProxy(a); + i = i + 1; + } + val seq = array2seq(ns); + NodeSeq.fromSeq(seq); + } + */ + + /** shallow equals method */ + def equals1(that: MetaData): Boolean; + + /** filters this sequence of meta data */ + def filter(f: MetaData => Boolean): MetaData = { + if (f(this)) copy(next filter f) else next filter f; + } + + /** returns key of this MetaData item */ + def key: String; + + /** returns key of this MetaData item */ + def value: String; + + /** maps this sequence of meta data */ + def map(f: MetaData => Text): List[Text] = f(this)::(next map f); + + /** returns Null or the next MetaData item */ + def next: MetaData; + + /** gets value of unqualified (unprefixed) attribute with given key */ + def getValue(key: String): String; + + /** gets value of qualified (prefixed) attribute with given key */ + def getValue(namespace: String, owner: Node, key: String): String = + getValue(namespace, owner.scope, key); + + /** gets value of qualified (prefixed) attribute with given key */ + def getValue(namespace: String, scope: NamespaceBinding, key: String): String; + override def hashCode(): Int; + + def toString1(): String = { + val sb = new StringBuffer(); + toString1(sb); + sb.toString(); + } + + //appends string representations of single attribute to StringBuffer + def toString1(sb:StringBuffer): Unit; + + override def toString(): String = { + val sb = new StringBuffer(); + toString(sb); + sb.toString(); + } + + def toString(sb: StringBuffer): Unit = { + sb.append(' '); + toString1(sb); + next.toString(sb); + } + + def wellformed(scope: NamespaceBinding): Boolean; + +} diff --git a/src/library/scala/xml/Molecule.scala b/src/library/scala/xml/Molecule.scala new file mode 100644 index 0000000000..d2141d069b --- /dev/null +++ b/src/library/scala/xml/Molecule.scala @@ -0,0 +1,39 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** an XML node for a list of data items. + * @author buraq + * @param list a list of data items, space separated + */ +class Molecule[+A]( val list: List[A] ) extends SpecialNode { + + final override def typeTag$:Int = -1; + + /** the constant "#PCDATA" + */ + def label = "#PCDATA"; + + final override def equals(x:Any) = x match { + case s:Molecule[A] => list == s.list ; + case _ => false; + } + + /** hashcode for this Text */ + override def hashCode() = + list.hashCode(); + + override def text = list.mkString(""," ",""); + + /** returns text, with some characters escaped according to XML spec */ + def toString(sb:StringBuffer) = + sb.append(list.mkString(""," ","")) + +} diff --git a/src/library/scala/xml/NamespaceBinding.scala b/src/library/scala/xml/NamespaceBinding.scala new file mode 100644 index 0000000000..3ad7c49957 --- /dev/null +++ b/src/library/scala/xml/NamespaceBinding.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +import Predef._; + +/** The class <code>NamespaceBinding</code> represents namespace bindings + * and scopes. The binding for the default namespace is treated as a null + * prefix. the absent namespace is represented with the null uri. Neither + * prefix nor uri may be empty, which is not checked. + * + * @version 1.0 + * @author Burak Emir + */ +[serializable] +class NamespaceBinding(val prefix: String, + val uri: String, + val parent: NamespaceBinding) extends AnyRef { + + private val serialVersionUID = 0 - 2518644165573446725L; + + if (null != prefix && 0 == prefix.length()) + error("zero length prefix not allowed"); + + def getURI(_prefix: String): String = + if (prefix == _prefix) uri else parent.getURI(_prefix); + + /** Returns some prefix that is mapped to the prefix. + * + * @param _uri + * @return + */ + def getPrefix(_uri: String): String = + if (_uri == uri) uri else parent.getURI(_uri); + + override def toString(): String = { + val sb = new StringBuffer(); + toString(sb, TopScope); + sb.toString(); + } + + def toString(stop: NamespaceBinding): String = { + val sb = new StringBuffer(); + toString(sb, stop); + sb.toString(); + } + + def toString(sb:StringBuffer, stop:NamespaceBinding): Unit = { + if (this ne stop) { // contains? + sb.append(" xmlns"); + if (prefix != null) { + sb.append(':').append(prefix) + } + sb.append('=') + .append('"') + .append(uri) + .append('"'); + parent.toString(sb, stop); // copy(ignore) + } + } + +} diff --git a/src/library/scala/xml/Node.scala b/src/library/scala/xml/Node.scala new file mode 100644 index 0000000000..2daf643d7a --- /dev/null +++ b/src/library/scala/xml/Node.scala @@ -0,0 +1,145 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +import scala.collection.Map; + +/** + * This object provides methods + */ +object Node { + + /** the constant empty attribute sequence */ + final def NoAttributes: MetaData = Null; + + /** the empty namespace */ + val EmptyNamespace = ""; + +} + +/** + * An abstract class representing XML with nodes of a labelled tree. + * This class contains an implementation of a subset of XPath for navigation. + * + * @author Burak Emir and others + * @version 1.1 + */ +abstract class Node extends NodeSeq { + + /** prefix of this node */ + def prefix: String = null; + + /** label of this node. I.e. "foo" for <foo/>) */ + def label: String; + + /** used internally. Atom/Molecule = -1 PI = -2 Comment = -3 EntityRef = -5 */ + def typeTag$: Int = 0; + + /** the namespace bindings */ + def scope: NamespaceBinding = TopScope; + + def namespace = getNamespace(prefix); + + def getNamespace(_pre: String) = + if (scope == null) null else scope.getURI(_pre); + + /** + * Looks up an unprefixed attribute in attributes of this node. + * + * @param key of queried attribute. + * @return value of <code>UnprefixedAttribute</code> with given key + * in attributes, if it exists, otherwise <code>null</code>. + */ + final def attribute(key: String) = + attributes.getValue(key); + + /** + * Looks up a prefixed attribute in attributes of this node. + * + * @param uri namespace of queried attribute (may not be null). + * @param key of queried attribute. + * @return value of <code>PrefixedAttribute</code> with given namespace + * and given key, otherwise <code>null</code>. + */ + final def attribute(uri: String, key: String) = + attributes.getValue(uri, this, key); + + /** + * Attribute axis - all attributes of this node, in order defined by attrib + */ + def attributes: MetaData = + Null; + + /** child axis (all children of this node) */ + def child: Seq[Node]; + + /** descendant axis (all descendants of this node, not including not itself) */ + def descendant: List[Node] = + child.toList.flatMap { x => x::x.descendant } ; + + /** descendant axis (all descendants of this node, including this node) */ + def descendant_or_self: List[Node] = this :: descendant; + + /** structural equality */ + override def equals(x: Any): Boolean = x match { + case that: Node => + ((that.prefix == this.prefix ) + &&(that.label == this.label ) + &&(that.attributes == this.attributes) + && that.child.sameElements(this.child)) // sameElements + case _ => false + } + /** returns a hashcode */ + override def hashCode(): Int; + //Utility.hashCode(pre, label, attributes.hashCode(), child); + + + /** method for NodeSeq */ + final def theSeq = this :: Nil; + + /** + * String representation of this node + * + * @param stripComment if true, strips comment nodes from result + */ + def toString(stripComment: Boolean): String = + Utility.toXML(this, stripComment); + + /** + * Same as <code>toString(false)</code>. + * + * @see "toString(Boolean)" + */ + override def toString(): String = + toString(false); + + /** + * Appends qualified name of this node to <code>StringBuffer</code>. + * + * @param sb + * @return .. + */ + def nameToString(sb: StringBuffer): StringBuffer = { + if (null != prefix) { + sb.append(prefix); + sb.append(':'); + } + sb.append(label); + } + + /** + * Returns a type symbol (e.g. DTD, XSD), default <code>null</code>. + */ + def xmlType(): TypeSymbol = null; + + override def text: String; + +} diff --git a/src/library/scala/xml/NodeBuffer.scala b/src/library/scala/xml/NodeBuffer.scala new file mode 100644 index 0000000000..b61e24181c --- /dev/null +++ b/src/library/scala/xml/NodeBuffer.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** + * This class acts as a Buffer for nodes. If it is used as a sequence + * of nodes <code>Seq[Node]</code>, it must be ensured that no updates + * occur after that point, because <code>scala.xml.Node</code> is assumed + * to be immutable. + * + * Despite this being a sequence, don't use it as key in a hashtable. + * Calling the hashcode function will result in a runtime error. + */ +class NodeBuffer extends scala.collection.mutable.ArrayBuffer[Node] { + + /** + * Append a single node to this buffer, returns reference on this + * NodeBuffer for convenience. + * + * Append an iterable object to this buffer, returns reference on + * this NodeBuffer for convenience. + * + * Append given string as a <code>scala.xml.Text</code> node to this + * buffer, returns reference on this NodeBuffer for convenience. + * + * @param n + */ + def &+(o: Any): NodeBuffer = { + o match { + case n:Node => super.+(n); + case ns:Iterable[AnyRef] => + val it = ns.elements; + while(it.hasNext) { + this &+ it.next; + if (it.hasNext) + this &+ " "; + } + case _ => super.+(Text(o.toString())); + } + this + } + /* + def +(o: AnyVal): NodeBuffer = { + super.+(Text(o.toString())); + this + } + */ +} diff --git a/src/library/scala/xml/NodeSeq.scala b/src/library/scala/xml/NodeSeq.scala new file mode 100644 index 0000000000..84d37d5790 --- /dev/null +++ b/src/library/scala/xml/NodeSeq.scala @@ -0,0 +1,104 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml ; + +object NodeSeq { + final val Empty = new NodeSeq { def theSeq = Nil; } + def fromSeq(s:Seq[Node]):NodeSeq = new NodeSeq { + def theSeq = s; + } + implicit def view(s:Seq[Node]):NodeSeq = fromSeq(s); +} + +/** a wrapper around Seq[Node] that adds XPath and comprehension methods */ +abstract class NodeSeq extends Seq[Node] { + import NodeSeq.view; // import view magic for NodeSeq wrappers + def theSeq: Seq[Node]; + def length = theSeq.length; + def elements = theSeq.elements ; + def apply(i: int ): Node = theSeq.apply( i ); + + def apply(f: Node => Boolean): NodeSeq = filter(f); + + /** structural equality */ + override def equals(x: Any) = x match { + case z:Node => (length == 1) && z == apply(0) + case z:Seq[Node] => sameElements( z ) + case z:String => text == z + case _ => false; + } + + /** projection function. Similar to XPath, use this \ "foo" to get a list + * of all elements of this sequence that are labelled with "foo". + * Use \ "_" as a wildcard. The document order is preserved. + */ + def \(that: String):NodeSeq = { + var res: NodeSeq = NodeSeq.Empty; + that match { + case "_" => + res = for( val x <- this; val y <- x.child: NodeSeq) yield { y } + + case _ if (that.charAt(0) == '@') && (this.length == 1) => + val k = that.substring(1); + val y = this(0); + val v = y.attribute(k); + if( v != null ) { + res = NodeSeq.fromSeq(Seq.single(Text(v))); + } + + case _ => + res = for( val x <- this; val y <- x.child: NodeSeq; y.label == that ) + yield { y } + } + res + } + + /** projection function. Similar to XPath, use this \\ 'foo to get a list + * of all elements of this sequence that are labelled with "foo". + * Use \ "_" as a wildcard. The document order is preserved. + */ + + def \\ ( that:String ): NodeSeq = that match { + case "_" => for( val x <- this; + val y <- x.descendant_or_self: NodeSeq ) + yield { y } + case _ if that.charAt(0) == '@' => + val attrib = that.substring(1); + (for(val x <- this; + val y <- x.descendant_or_self: NodeSeq; + val z <- y \ that) + yield { z }):NodeSeq + case _ => for( val x <- this; + val y <- x.descendant_or_self: NodeSeq; + y.label == that) + yield { y } + } + + override def toString():String = theSeq.elements.foldLeft ("") { + (s:String,x:Node) => s + x.toString() + } + + def asList = elements.toList; + + def map(f: Node => Node): NodeSeq = { val x = asList map f; x } + + def flatMap(f:Node => NodeSeq): NodeSeq = { val y = asList flatMap { x => f(x).asList }; y } + + def filter(f:Node => Boolean): NodeSeq = { val x = asList filter f; x } + + def text: String = { + val sb = new StringBuffer(); + val it = elements; + while(it.hasNext) { + sb.append(it.next.text); + } + sb.toString(); + } +} diff --git a/src/library/scala/xml/NodeTraverser.scala b/src/library/scala/xml/NodeTraverser.scala new file mode 100644 index 0000000000..690c85534d --- /dev/null +++ b/src/library/scala/xml/NodeTraverser.scala @@ -0,0 +1,30 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +import parsing.MarkupHandler; + +trait NodeTraverser extends MarkupHandler { + + def traverse(n: Node): Unit = n match { + case x:ProcInstr => procInstr(0, x.target, x.text) + case x:Comment => comment(0, x.text) + case x:Text => text(0, x.data) + case x:EntityRef => entityRef(0, x.entityName) + case _ => + elemStart(0, n.prefix, n.label, n.attributes, n.scope); + for (val m <- n.child) + traverse(m); + elem(0, n.prefix, n.label, n.attributes, n.scope, NodeSeq.fromSeq(n.child)); + elemEnd(0, n.prefix, n.label); + } + +} diff --git a/src/library/scala/xml/Null.scala b/src/library/scala/xml/Null.scala new file mode 100644 index 0000000000..5790a2fdfd --- /dev/null +++ b/src/library/scala/xml/Null.scala @@ -0,0 +1,75 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +case object Null extends MetaData { + + /** appends given MetaData items to this MetaData list */ + override def append(m: MetaData): MetaData = m; + + override def containedIn1(m: MetaData): Boolean = false; + + /** returns its argument */ + def copy(next: MetaData) = next; + + + override def elements = Iterator.empty[MetaData]; + + override def filter(f: MetaData => Boolean): MetaData = this; + + /** returns null */ + def getNamespace(owner: Node) = null; + + final override def hasNext = false; + + final override def length = 0; + + final override def length(i: Int) = i; + + def isPrefixed = false; + + /** deep equals method */ + override def equals(that: Any) = that match { + case m: MetaData => m.length == 0 + case _ => false; + } + + def equals1(that:MetaData) = that.length == 0; + + def key = null; + + def value = null; + + override def map(f: MetaData => Text): List[Text] = Nil; + + def next = null; + + /** null */ + def getValue(key: String) = null; + + /** gets value of qualified (prefixed) attribute with given key */ + def getValue(namespace: String, scope: NamespaceBinding, key: String) = + null; + + override def hashCode(): Int = 0; + + override def toString1(): String = ""; + + //appends string representations of single attribute to StringBuffer + def toString1(sb:StringBuffer) = {}; + + override def toString(): String = ""; + + override def toString(sb: StringBuffer): Unit = {} + + override def wellformed(scope: NamespaceBinding) = true; + +} diff --git a/src/library/scala/xml/Parsing.scala b/src/library/scala/xml/Parsing.scala new file mode 100644 index 0000000000..bc86d18e62 --- /dev/null +++ b/src/library/scala/xml/Parsing.scala @@ -0,0 +1,102 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.xml ; + +/** DEPRECATED - use either parsing.TokenTests, or Utilty (helper functions + * for parsing XML fragments ). + */ +object Parsing { + + /** (#x20 | #x9 | #xD | #xA) */ + final def isSpace( ch:Char ):Boolean = ch match { + case '\u0009' | '\u000A' | '\u000D' | '\u0020' => true + case _ => false; + } + + /** (#x20 | #x9 | #xD | #xA)+ */ + final def isSpace( cs:Seq[Char] ):Boolean = { + val it = cs.elements; + it.hasNext && it.forall { isSpace }; + } + + /** NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' + * | CombiningChar | Extender + * + * see [4] and Appendix B of XML 1.0 specification + */ + def isNameChar( ch:Char ) = isNameStart( ch ) || (ch match { + case '.' | '-' | ':' => true; + case _ => java.lang.Character.getType( ch ).asInstanceOf[Byte] match { + case java.lang.Character.COMBINING_SPACING_MARK => true; // Mc + case java.lang.Character.ENCLOSING_MARK => true; // Me + case java.lang.Character.NON_SPACING_MARK => true; // Mn + case java.lang.Character.MODIFIER_LETTER => true; // Lm + case java.lang.Character.DECIMAL_DIGIT_NUMBER => true; // Nd + case _ => false; + } + }); + + /** NameStart ::= ( Letter | '_' ) + * where Letter means in one of the Unicode general + * categories { Ll, Lu, Lo, Lt, Nl } + * + * We do not allow a name to start with ':'. + * see [3] and Appendix B of XML 1.0 specification + */ + def isNameStart( ch:Char ) = + java.lang.Character.getType( ch ).asInstanceOf[Byte] match { + case java.lang.Character.LOWERCASE_LETTER => true; + case java.lang.Character.UPPERCASE_LETTER => true; + case java.lang.Character.OTHER_LETTER => true; + case java.lang.Character.TITLECASE_LETTER => true; + case java.lang.Character.LETTER_NUMBER => true; + case _ => ch match { + case '_' => true + case _ => false; + } + } + + /** Name ::= ( Letter | '_' ) (NameChar)* + * + * see [5] of XML 1.0 specification + */ + def isName( s:String ):boolean = { + if( s.length() > 0 ) { + val z:Seq[Char] = s; + val y = z.elements; + if( isNameStart( y.next ) ) { + while( y.hasNext && isNameChar( y.next ) ) {}; + !y.hasNext + } else false; + } else false; + } + + def isPubIDChar( c:Char ) = c match { + case '\u0020' | '\u000D' | '\u000A' => true; + case _ if + ('0' < c && c < '9')||('a' < c && c < 'z')||('A' < c && c < 'Z') => true; + case '-' | '\''| '(' | ')' | '+' | ',' | '.' | '/' | ':' | '=' | + '?' | ';' | '!' | '*' | '#' | '@' | '$' | '_' | '%' => true + case _ => false; + } + + def checkSysID( s:String ):boolean = { + s.indexOf('"') == -1 || s.indexOf('\'') == -1 + } + + def checkPubID( s:String ):boolean = { + if( s.length() > 0 ) { + val z:Seq[Char] = s; + val y = z.elements; + while( y.hasNext && isPubIDChar( y.next ) ){}; + !y.hasNext + } else true + } + +} diff --git a/src/library/scala/xml/PrefixedAttribute.scala b/src/library/scala/xml/PrefixedAttribute.scala new file mode 100644 index 0000000000..4eb947e2c1 --- /dev/null +++ b/src/library/scala/xml/PrefixedAttribute.scala @@ -0,0 +1,87 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +/** prefixed attributes always have a non-null namespace + */ +class PrefixedAttribute(val pre: String, + val key: String, + val value: String, + val next: MetaData) extends MetaData { + + // verify that value is a proper attribute value (references, no <) + Utility.checkAttributeValue(value) match { + case null => ; + case msg => throw new MalformedAttributeException(msg); + } + + /** Returns a copy of this unprefixed attribute with the given + * next field. + */ + def copy(next: MetaData) = + new PrefixedAttribute(pre, key, value, next); + + //** duplicates the MetaData (deep copy), not preserving order */ + //def deepCopy: MetaData = deepCopy(null); + + //** duplicates the MetaData (deep copy), prepending it to tail */ + /* + def deepCopy(tail: MetaData): MetaData = { + val md = copy(tail); + if (null == next) + md + else + next.deepCopy(md) + } + */ + + def equals1(m: MetaData) = + (m.isPrefixed && + (m.asInstanceOf[PrefixedAttribute].pre == pre) && + (m.key == key) && (m.value == value)); + + def getNamespace(owner: Node) = + owner.getNamespace(pre); + + /** forwards the call to next */ + def getValue(key: String): String = next.getValue(key); + + /** gets attribute value of qualified (prefixed) attribute with given key + */ + def getValue(namespace: String, scope: NamespaceBinding, key: String): String = { + if (key == this.key && scope.getURI(pre) == namespace) + value + else + next.getValue(namespace, scope, key); + } + + /** returns true */ + final def isPrefixed = true; + + override def hashCode() = + pre.hashCode() * 41 + key.hashCode() * 7 + value.hashCode() * 3 + next.hashCode(); + + + def toString1(sb:StringBuffer): Unit = { + sb.append(pre); + sb.append(':'); + sb.append(key); + sb.append('='); + Utility.appendQuoted(value, sb); + } + + def wellformed(scope: NamespaceBinding): Boolean = { + (null == next.getValue(scope.getURI(pre), scope, key) + && next.wellformed(scope)); + } + +} + diff --git a/src/library/scala/xml/PrettyPrinter.scala b/src/library/scala/xml/PrettyPrinter.scala new file mode 100644 index 0000000000..3410c5dd9a --- /dev/null +++ b/src/library/scala/xml/PrettyPrinter.scala @@ -0,0 +1,274 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml ; + +import java.lang.StringBuffer ; /* Java dependency! */ +import scala.collection.Map ; + +/** Class for pretty printing. After instantiating, you can use the + * toPrettyXML methods to convert XML to a formatted string. The class + * can be reused to pretty print any number of XML nodes. + * + * @param width the width to fit the output into + * @step indentation +**/ + +class PrettyPrinter( width:Int, step:Int ) { + + class BrokenException() extends java.lang.Exception(); + + class Item ; + case object Break extends Item { + override def toString() = "\\"; + }; + case class Box( col:Int, s:String ) extends Item; + case class Para( s:String ) extends Item; + + protected var items:List[Item] = Nil; + + protected var cur = 0; + //protected var pmap:Map[String,String] = _; + + protected def reset() = { + cur = 0; + items = Nil; + } + + /* try to cut at whitespace */ + protected def cut( s:String, ind:Int ):List[Item] = { + val tmp = width - cur; + if( s.length() < tmp ) + return List(Box(ind,s)); + val sb = new StringBuffer(); + var i = s.indexOf(' '); + if(i > tmp || i == -1) throw new BrokenException(); // cannot break + + var last:List[Int] = i::Nil; + while(i < tmp) { + last = i::last; + i = s.indexOf(' ', i ); + } + var res:List[Item] = Nil; + while( Nil != last ) try { + val b = Box( ind, s.substring( 0, last.head )); + cur = ind; + res = b :: Break :: cut( s.substring( last.head, s.length()), ind ); + // backtrac + } catch { + case _:BrokenException => last = last.tail; + } + throw new BrokenException() + } + + /** try to make indented box, if possible, else para */ + protected def makeBox( ind:Int, s:String ) = { + if( cur < ind ) + cur == ind; + if( cur + s.length() > width ) { // fits in this line + items = Box( ind, s ) :: items; + cur = cur + s.length() + } else try { + for( val b <- cut( s, ind ).elements ) // break it up + items = b :: items + } catch { + case _:BrokenException => makePara( ind, s ); // give up, para + } + } + + // dont respect indent in para, but afterwards + protected def makePara( ind:Int, s:String ) = { + items = Break::Para( s )::Break::items; + cur = ind; + } + + // respect indent + protected def makeBreak() = { // using wrapping here... + items = Break::items; + cur = 0; + } + + protected def leafTag( n:Node ) = { + val sb = new StringBuffer("<"); + n.nameToString(sb); + //Utility.appendPrefixedName( n.prefix, n.label, pmap, sb ); + n.attributes.toString(sb); + //Utility.attr2xml( n.scope, n.attributes, pmap, sb ); + sb.append("/>"); + sb.toString(); + } + + protected def startTag(n: Node, pscope: NamespaceBinding): Pair[String, Int] = { + val sb = new StringBuffer("<"); + n.nameToString(sb); //Utility.appendPrefixedName( n.prefix, n.label, pmap, sb ); + val i = sb.length() + 1; + n.attributes.toString(sb); + n.scope.toString(sb, pscope); + sb.append('>'); + Pair(sb.toString(), i); + } + + protected def endTag(n: Node) = { + val sb = new StringBuffer("</"); + n.nameToString(sb); //Utility.appendPrefixedName( n.prefix, n.label, pmap, sb ); + sb.append('>'); + sb.toString(); + } + + + protected def childrenAreLeaves(n: Node): Boolean = { + val it = n.child.elements; + while( it.hasNext ) + it.next match { + case _:Atom[Any] | _: Molecule[Any] | _:Comment | _:EntityRef | _:ProcInstr => + case _:Node => + return false; + } + return true; + } + + + protected def fits(test: String) = { + test.length() < width - cur; + } + + /** @param tail: what we'd like to sqeeze in */ + protected def traverse(node: Node, pscope: NamespaceBinding, ind: Int): Unit = node match { + + case _:Atom[Any] | _:Molecule[Any] | _:Comment | _:EntityRef | _:ProcInstr => + makeBox( ind, node.toString() ); + + case _:Node => + val test = { val sb = new StringBuffer(); Utility.toXML(node, pscope, sb, false); sb.toString()}; + if(childrenAreLeaves(node) && fits(test)) { + makeBox( ind, test ); + } else { + val Pair(stg, len2) = startTag( node, pscope ); + val etg = endTag( node ); + if( stg.length() < width - cur ) { // start tag fits + + makeBox( ind, stg ); + makeBreak(); + traverse( node.child.elements, node.scope, ind + step ); + makeBox( ind, etg ); + + } else if( len2 < width - cur ) { + // <start label + attrs + tag + content + end tag + makeBox( ind, stg.substring( 0, len2 )); + makeBreak(); // todo: break the rest in pieces + /*{ //@todo + val sq:Seq[String] = stg.split(" "); + val it = sq.elements; + it.next; + for( val c <- it ) { + makeBox( ind+len2-2, c ); + makeBreak(); + } + }*/ + makeBox( ind, stg.substring( len2, stg.length() )); + makeBreak(); + traverse( node.child.elements, node.scope, ind + step ); + makeBox( cur, etg ); + } else { // give up + makeBox( ind, test ); + makeBreak(); + } + } + } + + protected def traverse( it:Iterator[Node], scope:NamespaceBinding, ind: Int ): Unit = { + for( val c <- it ) { + traverse( c, scope, ind ); + makeBreak(); + } + } + + /** appends a formatted string containing well-formed XML with + * given namespace to prefix mapping to the given stringbuffer + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + * @param sb the stringbuffer to append to + */ + def format(n: Node, sb: StringBuffer ): Unit = { // entry point + format(n,null,sb) + } + + def format(n: Node, pscope:NamespaceBinding, sb: StringBuffer): Unit = { // entry point + reset(); + traverse( n, pscope, 0 ); + var cur = 0; + //Console.println( items.reverse ); + for( val b <- items.reverse ) b match { + case Break => + sb.append('\n'); // on windows: \r\n ? + cur = 0; +// while( cur < last ) { +// sb.append(' '); +// cur = cur + 1; +// } + + case Box(i, s) => + while( cur < i ) { + sb.append(' '); + cur = cur + 1; + } + sb.append( s ); + case Para( s ) => + sb.append( s ); + } + } + + // public convenience methods + + /** returns a formatted string containing well-formed XML with + * default namespace prefix mapping + * @param n the node to be serialized + */ + def format(n: Node): String = format(n, null); //Utility.defaultPrefixes( n )); + + /** returns a formatted string containing well-formed XML with + * given namespace to prefix mapping + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + */ + def format(n: Node, pscope: NamespaceBinding): String = { + val sb = new StringBuffer(); + format( n, pscope, sb ); + sb.toString(); + } + + /* returns a formatted string containing well-formed XML nodes with + * default namespace prefix mapping + */ + def formatNodes( nodes:Seq[Node] ):String = { + formatNodes(nodes, null) + } + + /** returns a formatted string containing well-formed XML + * @param nodes the sequence of nodes to be serialized + * @param pmap the namespace to prefix mapping + */ + def formatNodes( nodes:Seq[Node], pscope: NamespaceBinding ):String = { + var sb = new StringBuffer(); + formatNodes( nodes, pscope, sb ); + sb.toString(); + } + + /** appends a formatted string containing well-formed XML with + * the given namespace to prefix mapping to the given stringbuffer + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + * @param sb the string buffer to which to append to + */ + def formatNodes( nodes: Seq[Node], pscope: NamespaceBinding, sb: StringBuffer ): Unit = { + for( val n <- nodes.elements ) { + sb.append(format( n, pscope )) + } + } +} diff --git a/src/library/scala/xml/ProcInstr.scala b/src/library/scala/xml/ProcInstr.scala new file mode 100644 index 0000000000..b4160139e6 --- /dev/null +++ b/src/library/scala/xml/ProcInstr.scala @@ -0,0 +1,63 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** an XML node for processing instructions (PI) + * + * @author Burak Emir + * @param target target name of this PI + * @param text text contained in this node, may not contain "?>" +**/ + +case class ProcInstr(target:String, proctext:String) extends SpecialNode { + + if( !Utility.isName( target ) ) + throw new IllegalArgumentException(target+" must be an XML Name"); + else if( text.indexOf("?>" ) != -1 ) + throw new IllegalArgumentException(proctext+" may not contain \"?>\""); + + final override def typeTag$:Int = -2; + + val z:Seq[Char] = target; z match { + case Seq('X'|'x','M'|'m','L'|'l') => + throw new IllegalArgumentException(target+" is reserved"); + case _ => + } + + /** structural equality */ + override def equals(x: Any): Boolean = x match { + case ProcInstr(x,y) => x.equals(target) && y.equals(proctext); + case _ => false + } + + /** the constant "#PI" */ + final def label = "#PI"; + + /** hashcode for this PI */ + override def hashCode() = target.hashCode() * 7 + proctext.hashCode(); + + + override def text = ""; + + /** appends "<?" target (" "+text)?+"?>" + * to this stringbuffer. + */ + def toString(sb: StringBuffer) = { + sb + .append("<?") + .append(target); + if( proctext.length() > 0 ) { + sb + .append(' ') + .append(text); + }; + sb.append("?>"); + } +} diff --git a/src/library/scala/xml/SpecialNode.scala b/src/library/scala/xml/SpecialNode.scala new file mode 100644 index 0000000000..1162287e2f --- /dev/null +++ b/src/library/scala/xml/SpecialNode.scala @@ -0,0 +1,31 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** a special XML node is either text (PCDATA), a comment, a PI, or + * an entity ref +**/ +abstract class SpecialNode extends Node { + + /** always empty */ + final override def attributes = Null; + + /** always Node.EmptyNamespace */ + final override def namespace = null; + + /** always empty */ + final def child = Nil; + + final override def toString(): String = + toString(new StringBuffer()).toString(); + + def toString(sb:StringBuffer): StringBuffer ; + +} diff --git a/src/library/scala/xml/Text.scala b/src/library/scala/xml/Text.scala new file mode 100644 index 0000000000..507239cc8b --- /dev/null +++ b/src/library/scala/xml/Text.scala @@ -0,0 +1,32 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +/** an XML node for text (PCDATA). Used in both non-bound and bound XML + * representations + * @author Burak Emir + * @param text the text contained in this node, may not be null. + */ +case class Text( _data: String ) extends Atom[String](_data) { + + if(null == data) + throw new java.lang.NullPointerException("tried to construct Text with null"); + + final override def equals(x:Any) = x match { + case s:String => s.equals( data.toString() ); + case s:Text => data == s.data ; + case _ => false; + } + + /** returns text, with some characters escaped according to XML spec */ + override def toString(sb:StringBuffer) = + Utility.escape( data.toString(), sb ); + +} diff --git a/src/library/scala/xml/TextBuffer.scala b/src/library/scala/xml/TextBuffer.scala new file mode 100644 index 0000000000..448fd861d5 --- /dev/null +++ b/src/library/scala/xml/TextBuffer.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.xml ; + +object TextBuffer { + def fromString(str: String): TextBuffer = { + new TextBuffer().append( str ); + } +} + +/** this classes is for creating text nodes without surplus whitespace. + * all occurrences of one or more whitespace in strings appended with the + * append method will be replaced by a single space character, and + * leading and trailing space will be removed completely. + */ +class TextBuffer { + + val sb = new StringBuffer(); + var ws = true; + + def appendSpace = if( !ws ) { ws = true; sb.append(' ');} else {}; + def appendChar(c:char) = { ws = false; sb.append( c );} + + /** appends this string to the text buffer, trimming whitespaces as needed */ + def append( cs:Seq[Char] ):TextBuffer = { + for( val c <- cs ) { + if( Utility.isSpace( c ) ) + appendSpace; + else + appendChar( c ) + } + this + } + + /** returns an empty sequence if text is only whitespace */ + def toText:Seq[Text] = { + var len = sb.length(); /* invariant */ + if( len == 0 ) return Nil; + + if( Utility.isSpace( sb.charAt( len - 1 ) )) { + len = len - 1; + sb.setLength( len ) + } + if( len == 0 ) return Nil; + + List( Text( sb.toString() ) ); + } + +} diff --git a/src/library/scala/xml/TopScope.scala b/src/library/scala/xml/TopScope.scala new file mode 100644 index 0000000000..ff3b3b7cba --- /dev/null +++ b/src/library/scala/xml/TopScope.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +case object TopScope extends NamespaceBinding(null, null, null) { + + /* + override def contains(pre:String) = false; + */ + override def getURI(_prefix: String) = null; + + override def getPrefix(_uri: String) = null; + + override def toString() = ""; + + override def toString(stop: NamespaceBinding) = ""; + + override def toString(sb: StringBuffer, ignore: NamespaceBinding) = {}; + +} diff --git a/src/library/scala/xml/TypeSymbol.scala b/src/library/scala/xml/TypeSymbol.scala new file mode 100644 index 0000000000..1d5fba7ae6 --- /dev/null +++ b/src/library/scala/xml/TypeSymbol.scala @@ -0,0 +1,11 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $id: $ + +package scala.xml; trait TypeSymbol {} diff --git a/src/library/scala/xml/UnprefixedAttribute.scala b/src/library/scala/xml/UnprefixedAttribute.scala new file mode 100644 index 0000000000..82d30ac804 --- /dev/null +++ b/src/library/scala/xml/UnprefixedAttribute.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml; + +/** unprefixed attributes have the null namespace + */ +class UnprefixedAttribute(val key: String, val value: String, val next: MetaData) extends MetaData { + + // verify that value is a proper attribute value (references, no <) + Utility.checkAttributeValue(value) match { + case null => ; + case msg => throw new MalformedAttributeException(msg); + } + + /** returns a copy of this unprefixed attribute with the given next field*/ + def copy(next: MetaData) = + new UnprefixedAttribute(key, value, next); + + def equals1(m:MetaData) = + !m.isPrefixed && (m.key == key) && (m.value == value); + + /** returns null */ + final def getNamespace(owner: Node): String = + null; + + /** + * Gets value of unqualified (unprefixed) attribute with given key. + * + * @param key + * @return .. + */ + def getValue(key: String): String = + if (key == this.key) value else next.getValue(key); + + /** + * Forwards the call to next. + * + * @param namespace + * @param scope + * @param key + * @return .. + */ + def getValue(namespace: String, scope: NamespaceBinding, key: String): String = + next.getValue(namespace, scope, key); + + override def hashCode() = + key.hashCode() * 7 + value.hashCode() * 53 + next.hashCode(); + + /** returns false */ + final def isPrefixed = false; + + def toString1(sb:StringBuffer): Unit = { + sb.append(key); + sb.append('='); + Utility.appendQuoted(value, sb); + } + + def wellformed(scope: NamespaceBinding): Boolean = + (null == next.getValue(null, scope, key)) && next.wellformed(scope); + +} + diff --git a/src/library/scala/xml/Utility.scala b/src/library/scala/xml/Utility.scala new file mode 100644 index 0000000000..2737e75d58 --- /dev/null +++ b/src/library/scala/xml/Utility.scala @@ -0,0 +1,244 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml; + +import java.lang.StringBuffer; +import scala.collection.mutable; + +/** + * Utility functions for processing instances of bound and not bound XML + * classes, as well as escaping text nodes + */ +object Utility extends AnyRef with parsing.TokenTests { + + def view(s: String): Text = Text(s); + + /* escapes the characters < > & and " from string */ + final def escape(text: String): String = + escape(text, new StringBuffer()).toString(); + + + /* appends escaped string to s */ + final def escape(text: String, s: StringBuffer): StringBuffer = { + for (val c <- Iterator.fromString(text)) c match { + case '<' => s.append("<"); + case '>' => s.append(">"); + case '&' => s.append("&"); + case '"' => s.append("""); + case _ => s.append(c); + } + s + } + + /** + * Returns a set of all namespaces used in a sequence of nodes + * and all their descendants, including the empty namespaces. + * + * @param nodes + */ + + def collectNamespaces(nodes: Seq[Node]): mutable.Set[String] = { + var m = new mutable.HashSet[String](); + val it = nodes.elements; + while (it.hasNext) + collectNamespaces(it.next, m); + m + } + + /** adds all namespaces in node to set */ + def collectNamespaces(n: Node, set: mutable.Set[String]): Unit = { + if( n.typeTag$ >= 0 ) { + set += n.namespace; + for (val a <- n.attributes) a match { + case _:PrefixedAttribute => + set += a.getNamespace(n) + case _ => + } + for (val i <- n.child) + collectNamespaces(i, set); + } + } + + /** string representation of an XML node, with comments stripped the comments + * @see "toXML(Node, Boolean)" + */ + def toXML(n: Node): String = toXML(n, true); + + /** + * String representation of a Node. uses namespace mapping from + * <code>defaultPrefixes(n)</code>. + * + * @param n + * @param stripComment + * + * @todo define a way to escape literal characters to &xx; references + */ + def toXML(n: Node, stripComment: Boolean): String = { + val sb = new StringBuffer(); + toXML(n, TopScope, sb, stripComment); + sb.toString(); + } + + + /** appends a tree to the given stringbuffer within given namespace scope. + * + * @param n the node + * @param pscope the parent scope + * @param sb stringbuffer to append to + * @param stripComment if true, strip comments + */ + def toXML(x: Node, pscope: NamespaceBinding, sb: StringBuffer, stripComment: Boolean): Unit = { + x match { + + case c: Comment if !stripComment => + c.toString(sb) + + case x: SpecialNode => + x.toString(sb) + + case _ => + // print tag with namespace declarations + sb.append('<'); + x.nameToString(sb); + if (x.attributes != null) { + x.attributes.toString(sb) + } + x.scope.toString(sb, pscope); + sb.append('>'); + for (val c <- x.child.elements) { + toXML(c, x.scope, sb, stripComment); + } + sb.append("</"); + x.nameToString(sb); + sb.append('>') + + } + } + + + /** returns prefix of qualified name if any */ + final def prefix(name: String): Option[String] = { + val i = name.indexOf(':'); + if( i != -1 ) Some( name.substring(0, i) ) else None + } + + /** + * Returns a hashcode for the given constituents of a node + * + * @param uri + * @param label + * @param attribHashCode + * @param children + */ + def hashCode(pre: String, label: String, attribHashCode: Int, scpeHash: Int, children: Seq[Node]) = { + ( if(pre!=null) {41 * pre.hashCode() % 7} else {0}) + + label.hashCode() * 53 + + attribHashCode * 7 + + scpeHash * 31 + + children.hashCode() + } + + /** + * Returns a hashcode for the given constituents of a node + * + * @param uri + * @param label + * @param attribs + * @param children + def hashCode(uri: String, label: String, attribs: scala.collection.mutable.HashMap[Pair[String,String],String], scpe: Int, children: Seq[Node]): Int = { + 41 * uri.hashCode() % 7 + label.hashCode() + attribs.toList.hashCode() + scpe + children.hashCode() + } + */ + + def systemLiteralToString(s: String): String = { + val sb = new StringBuffer(); + systemLiteralToString(sb, s); + sb.toString(); + } + + def systemLiteralToString(sb: StringBuffer, s: String): StringBuffer = { + sb.append("SYSTEM "); + appendQuoted(s, sb); + } + + def publicLiteralToString(s: String): String = { + val sb = new StringBuffer(); + systemLiteralToString(sb, s); + sb.toString(); + } + + def publicLiteralToString(sb: StringBuffer, s: String): StringBuffer = { + sb.append("PUBLIC \"").append(s).append('"') + } + + /** + * Appends "s" if s does not contain ", 's' + * otherwise + * + * @param s + * @param sb + */ + def appendQuoted(s: String, sb: StringBuffer) = { + val ch = if (s.indexOf('"') == -1) '"' else '\''; + sb.append(ch).append(s).append(ch) + } + + /** + * Appends "s" and escapes and " i s with \" + * + * @param s + * @param sb + */ + def appendEscapedQuoted(s: String, sb: StringBuffer) = { + sb.append('"'); + val z:Seq[Char] = s; + for( val c <- z ) c match { + case '"' => sb.append('\\'); sb.append('"'); + case _ => sb.append( c ); + } + sb.append('"') + } + + def getName(s: String, index: Int): String = { + var i = index; + val sb = new StringBuffer(); + if(i < s.length()) { + var c = s.charAt(i); + if(isNameStart(s.charAt(i))) + while(i < s.length() && { c = s.charAt(i); isNameChar(c)}) { + sb.append(c); + i = i + 1; + } + sb.toString(); + } else null + } + + /** returns null if the value is a correct attribute value, error message if it isn't */ + def checkAttributeValue(value: String): String = { + var i = 0; + while(i < value.length()) { + value.charAt(i) match { + case '<' => + return "< not allowed in attribute value"; + case '&' => + val n = getName(value, i+1); + if(n== null) + return "malformed entity reference in attribute value ["+value+"]"; + i = i + n.length() + 1; + if(i >= value.length() || value.charAt(i) != ';') + return "malformed entity reference in attribute value ["+value+"]"; + case _ => + } + i = i + 1; + } + return null; + } + +} diff --git a/src/library/scala/xml/XML.scala b/src/library/scala/xml/XML.scala new file mode 100644 index 0000000000..1fadac7fe8 --- /dev/null +++ b/src/library/scala/xml/XML.scala @@ -0,0 +1,75 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.xml ; + +import scala.xml.parsing.NoBindingFactoryAdapter ; +import org.xml.sax.InputSource; + +// import scala.xml.Utility ; + +/** functions to load and save XML elements. use this when data binding is not +** desired, i.e. when XML is handled using Symbol nodes +**/ +object XML { + + import java.io._ ; + + // functions for generic xml loading, saving + + /** loads XML from given file */ + final def loadFile( file: File ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( + new FileInputStream( file ) + )); + + /** loads XML from given file descriptor */ + final def loadFile( fileDesc: FileDescriptor ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( + new FileInputStream( fileDesc ) + )); + + /** loads XML from given file */ + final def loadFile( fileName:String ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( + new FileInputStream( fileName ) + )); + + /** loads XML from given InputStream */ + final def load( is:InputStream ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( is )); + + /** loads XML from given Reader */ + final def load( reader:Reader ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( reader )); + + /** loads XML from given sysID */ + final def load( sysID:String ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( new InputSource( sysID )); + + /** loads XML from a given input source*/ + final def load( source:InputSource ): scala.xml.Elem = + new NoBindingFactoryAdapter().loadXML( source ); + + /** saves XML to filename with encoding ISO-8859-1 */ + final def save( filename:String, doc:Elem ):Unit = { + /* using NIO classes of JDK 1.4 */ + import java.io.{FileOutputStream,Writer}; + import java.nio.channels.{Channels,FileChannel}; + + val fos = new FileOutputStream( filename ); + val w:Writer = Channels.newWriter( fos.getChannel(), "ISO-8859-1" ); + + /* 2do: optimize by giving writer parameter to toXML*/ + w.write( Utility.toXML( doc )); + + w.close(); + fos.close(); + } + +} diff --git a/src/library/scala/xml/dtd/ContentModel.scala b/src/library/scala/xml/dtd/ContentModel.scala new file mode 100644 index 0000000000..78132dcc4a --- /dev/null +++ b/src/library/scala/xml/dtd/ContentModel.scala @@ -0,0 +1,200 @@ +package scala.xml.dtd ; + +import scala.util.regexp.WordExp; +import scala.util.automata._; + +object ContentModel extends WordExp { + type _labelT = ElemName; + type _regexpT = RegExp; + + object Translator extends WordBerrySethi { + + override val lang: ContentModel.this.type = ContentModel.this; + import lang._ ; + //val re = Sequ(Star(Letter(IntConst( 3 )))); + //val aut = automatonFrom(re, 7) + } + + + case class ElemName(name: String) extends Label { + override def toString() = "ElemName(\""+name+"\")"; + } + + def isMixed(cm: ContentModel) = cm.isInstanceOf[MIXED]; + def containsText(cm: ContentModel) = (cm == PCDATA) || isMixed(cm); + + def parse(s: String): ContentModel = ContentModelParser.parse( s ); + + def getLabels(r: RegExp): scala.collection.Set[String] = { + val s = new scala.collection.mutable.HashSet[String](); + def traverse1(xs: Seq[RegExp]): Unit = { + val it = xs.elements; + while( it.hasNext ) + traverse( it.next ); + } + def traverse(r: RegExp): Unit = { + r match { + case Letter(ElemName( name )) => s += name; + case Star( x @ _ ) => traverse( x ); // bug if x@_* + case Sequ( xs @ _* ) => traverse1(xs); + case Alt( xs @ _* ) => traverse1(xs); + } + } + traverse( r ); + return s + } + + def toString(r: RegExp):String = { + val sb = new StringBuffer(); + toString(r, sb); + sb.toString(); + } + + /* precond: rs.length >= 1 */ + private def toString(rs: Seq[RegExp], sb: StringBuffer, sep: Char): Unit = { + + val it = rs.elements; + val fst = it.next; + toString(fst, sb); + for(val z <- it) { + sb.append( sep ); + toString( z, sb ); + } + sb + } + + def toString(c: ContentModel, sb: StringBuffer): StringBuffer = c match { + + case ANY => + sb.append("ANY"); + + case EMPTY => + sb.append("EMPTY"); + + case PCDATA => + sb.append("(#PCDATA)"); + + case ELEMENTS( _ ) | MIXED( _ ) => + c.toString(sb) + + } + + def toString(r: RegExp, sb:StringBuffer): StringBuffer = { + r match { + case Eps => + sb + + case Sequ(rs @ _*) => + sb.append( '(' ); toString(rs, sb, ','); sb.append( ')' ); + + case Alt(rs @ _*) => + sb.append( '(' ); toString(rs, sb, '|'); sb.append( ')' ); + + case Star(r: RegExp) => + sb.append( '(' ); toString(r, sb); sb.append( ")*" ); + + case Letter(ElemName(name)) => + sb.append(name); + + } + } + +} + +sealed abstract class ContentModel { + override def toString(): String = { + val sb = new StringBuffer(); + toString(sb); + sb.toString(); + } + + def toString(sb:StringBuffer): StringBuffer; + /* + def validate(cs: NodeSeq): Boolean = this.match { + case ANY => true ; + case EMPTY => cs.length == 0; + case PCDATA => cs.length == 0 + || (cs.length == 1 && cs(0).isInstanceOf[Text]); + case m@MIXED(r) => m.runDFA(cs); + case e@ELEMENTS(r) => e.runDFA(cs); + } + */ +} + +case object PCDATA extends ContentModel { + def toString(sb:StringBuffer): StringBuffer = sb.append("(#PCDATA)"); +} +case object EMPTY extends ContentModel { + def toString(sb:StringBuffer): StringBuffer = sb.append("EMPTY"); +} +case object ANY extends ContentModel { + def toString(sb:StringBuffer): StringBuffer = sb.append("ANY"); +} +abstract class DFAContentModel extends ContentModel { + import ContentModel.{ ElemName }; + def r: ContentModel.RegExp; + private var _dfa: DetWordAutom[ContentModel.ElemName] = null; + + def dfa = { + if(null == _dfa) { + val nfa = ContentModel.Translator.automatonFrom(r, 1); + _dfa = new SubsetConstruction(nfa).determinize; + } + _dfa + } +} +case class MIXED(r:ContentModel.RegExp) extends DFAContentModel { + import ContentModel.{ Alt, Eps, RegExp }; + /* + def getIterator(ns:NodeSeq) = new Iterator[String] { + def cond(n:Node) = + !n.isInstanceOf[Text] && !n.isInstanceOf[SpecialNode]; +Console.println("ns = "+ns); + val jt = ns.elements; + def hasNext = jt.hasNext; + def next = { + var r: Node = jt.next; + while(!cond(r) && jt.hasNext) { + Console.println("skipping "+r); + r = jt.next; + } + Console.println("MIXED, iterator.next, r = "+r); + if(Text("") == r) + null + else + r.label + } + } + */ + def toString(sb:StringBuffer): StringBuffer = { + sb.append("(#PCDATA|"); + //r match { + // case Alt(Eps, rs@_*) => ContentModel.toString(Alt(rs:_*):RegExp, sb); + //} + ContentModel.toString(Alt(r.asInstanceOf[Alt].rs.toList.drop(1):_*):RegExp, sb); + sb.append(")*"); + } +} + +case class ELEMENTS(r:ContentModel.RegExp) extends DFAContentModel { + /* + def getIterator(ns:NodeSeq) = new Iterator[String] { + val jt = ns.elements.buffered; + def hasNext = jt.hasNext; + def next = { + var r: Node = jt.next; + while(r.isInstanceOf[SpecialNode] && jt.hasNext) { + r = jt.head; + jt.next; + } + Console.println("MIXED, iterator.next, r = "+r); + if(r.isInstanceOf[Text]) + throw ValidationException("Text not allowed here!") + else + r.label + } + } + */ + def toString(sb:StringBuffer): StringBuffer = + ContentModel.toString(r, sb); +} diff --git a/src/library/scala/xml/dtd/ContentModelParser.scala b/src/library/scala/xml/dtd/ContentModelParser.scala new file mode 100644 index 0000000000..08232d118d --- /dev/null +++ b/src/library/scala/xml/dtd/ContentModelParser.scala @@ -0,0 +1,144 @@ +package scala.xml.dtd ; + +/** Parser for regexps (content models in DTD element declarations) */ + +object ContentModelParser extends Scanner { // a bit too permissive concerning #PCDATA + import ContentModel._ ; + + /** parses the argument to a regexp */ + def parse(s:String): ContentModel = { initScanner( s ); contentspec } + + // zzz parser methods zzz + def accept( tok:int ) = { + if( token != tok ) { + if(( tok == STAR )&&( token == END )) // common mistake + error("in DTDs, \n"+ + "mixed content models must be like (#PCDATA|Name|Name|...)*"); + else + error("expected "+token2string(tok)+ + ", got unexpected token:"+token2string(token)); + } + nextToken + } + + // s [ '+' | '*' | '?' ] + def maybeSuffix(s:RegExp) = token match { + case STAR => nextToken; Star( s ) + case PLUS => nextToken; Sequ( s, Star( s )) + case OPT => nextToken; Alt( Eps, s ) + case _ => s + } + + + // contentspec ::= EMPTY | ANY | (#PCDATA) | "(#PCDATA|"regexp) + + def contentspec: ContentModel = token match { + + case NAME => value match { + case "ANY" => ANY + case "EMPTY" => EMPTY + case _ => error("expected ANY, EMPTY or '(' instead of " + value ); + } + case LPAREN => + + nextToken; + sOpt; + if( token != TOKEN_PCDATA ) + ELEMENTS(regexp); + else { + nextToken; + token match { + case RPAREN => + PCDATA + case CHOICE => + val res = MIXED(choiceRest(Eps)); + sOpt; + accept( RPAREN ); + accept( STAR ); + res + case _ => + error("unexpected token:" + token2string(token) ); + } + } + + case _ => + error("unexpected token:" + token2string(token) ); + } + // sopt ::= S? + def sOpt = if( token == S ) nextToken; + + // (' S? mixed ::= '#PCDATA' S? ')' + // | '#PCDATA' (S? '|' S? atom)* S? ')*' + /* + def mixed = { + accept( TOKEN_PCDATA ); + sOpt; + if( token == RPAREN ) + PCDATA_ + else { + val t = choiceRest( PCDATA_ ); + if( !isMixed( t ) ) + error("mixed content models must be like (#PCDATA.|.|.|.)*"); + accept( RPAREN ); + // lax: (workaround for buggy Java XML parser in JDK1.4.2) + if( token == STAR ) accept( STAR ); + // strict: + // accept( STAR ); + Star( t ) + } + } +*/ + // '(' S? regexp ::= cp S? [seqRest|choiceRest] ')' [ '+' | '*' | '?' ] + def regexp:RegExp = { + //Console.println("regexp, token = "+token2string(token)); + val p = particle; + sOpt; + maybeSuffix( token match { + case RPAREN => nextToken; p + case CHOICE => val q = choiceRest( p );accept( RPAREN ); q + case COMMA => val q = seqRest( p ); accept( RPAREN ); q + }) + } + + + // seqRest ::= (',' S? cp S?)+ + def seqRest( p:RegExp ) = { + var k = List( p ); + while( token == COMMA ) { + nextToken; + sOpt; + k = particle::k; + sOpt; + } + Sequ( k.reverse:_* ) + } + + // choiceRest ::= ('|' S? cp S?)+ + def choiceRest( p:RegExp ) = { + var k = List( p ); + while( token == CHOICE ) { + nextToken; + sOpt; + k = particle::k; + sOpt; + } + Alt( k.reverse:_* ) + } + + // particle ::= '(' S? regexp + // | name [ '+' | '*' | '?' ] + def particle = { + //Console.println("particle, token="+token2string(token)); + token match { + case LPAREN => nextToken; sOpt; regexp; + case NAME => val a = Letter(ElemName(value)); nextToken; maybeSuffix( a ) + case _ => error("expected '(' or Name, got:"+token2string( token )); + } + } + + // atom ::= name + def atom = token match { + case NAME => val a = Letter(ElemName(value)); nextToken; a + case _ => error("expected Name, got:"+token2string( token )); + } +} diff --git a/src/library/scala/xml/dtd/DTD.scala b/src/library/scala/xml/dtd/DTD.scala new file mode 100644 index 0000000000..9cf55f7077 --- /dev/null +++ b/src/library/scala/xml/dtd/DTD.scala @@ -0,0 +1,48 @@ +package scala.xml.dtd; + +import scala.io.Source; +import scala.collection.mutable.{ HashMap, Map } + +/** a document type declaration */ +abstract class DTD { + + var externalID: ExternalID = null; + + def notations: Seq[NotationDecl] = Nil; + + def unparsedEntities: Seq[EntityDecl] = Nil; + + var elem: Map[String, ElemDecl] = new HashMap[String, ElemDecl](); + + var attr: Map[String, AttListDecl] = new HashMap[String, AttListDecl](); + + var ent: Map[String, EntityDecl] = new HashMap[String, EntityDecl](); + + var decls: List[Decl] = Nil; + + //def getElemDecl(elem:String): ElemDecl; + + //def getAttribDecl(elem: String, attr: String): AttrDecl; + + override def toString() = { + val sb = new StringBuffer(); + sb.append("DTD [\n"); + if(null != externalID) + sb.append(externalID.toString()).append('\n'); + for(val d <- decls) + sb.append(d.toString()).append('\n'); + sb.append("]").toString() + } + + /* + def initializeEntities() = { + for(val x <- decls) x match { + case y @ ParsedEntityDecl(name, _) => ent.update(name, y); + case y @ UnparsedEntityDecl(name, _, _) => ent.update(name, y); + case y @ ParameterEntityDecl(name, _) => ent.update(name, y); + case _ => + } + } + */ + +} diff --git a/src/library/scala/xml/dtd/Decl.scala b/src/library/scala/xml/dtd/Decl.scala new file mode 100644 index 0000000000..1e151a5c84 --- /dev/null +++ b/src/library/scala/xml/dtd/Decl.scala @@ -0,0 +1,182 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml.dtd ; + +abstract class Decl ; + +abstract class MarkupDecl extends Decl { + + final override def toString(): String = { + toString(new StringBuffer()).toString(); + } + + def toString(sb: StringBuffer): StringBuffer; + +} + +/** an element declaration + */ +case class ElemDecl(name: String, contentModel: ContentModel) extends MarkupDecl with DtdTypeSymbol { + + //def mixed = ; // to do + + def toString(sb: StringBuffer): StringBuffer = { + sb + .append("<!ELEMENT ") + .append(name) + .append(' '); + + ContentModel.toString(contentModel, sb); + sb.append('>'); + } + +} // ElemDecl + +case class AttListDecl(name: String, attrs:List[AttrDecl]) extends MarkupDecl with DtdTypeSymbol { + + def toString(sb: StringBuffer): StringBuffer = { + sb + .append("<!ATTLIST ") + .append(name) + .append('\n') + .append(attrs.mkString("","\n",">")); + } +} + +/** an attribute declaration. at this point, the tpe is a string. Future + * versions might provide a way to access the attribute types more + * directly. + */ +case class AttrDecl( name:String, tpe:String, default:DefaultDecl ) { + + final override def toString(): String = + toString(new StringBuffer()).toString(); + + final def toString(sb: StringBuffer): StringBuffer = { + sb.append(" ").append( name ).append(' ').append( tpe ).append(' '); + default.toString(sb) + } + +} + +/** an entity declaration */ +abstract class EntityDecl extends MarkupDecl; + +/** a parsed general entity declaration */ +case class ParsedEntityDecl( name:String, entdef:EntityDef ) extends EntityDecl { + + final def toString(sb: StringBuffer): StringBuffer = { + sb.append("<!ENTITY ").append( name ).append(' '); + entdef.toString(sb).append('>'); + } +} + +/** a parameter entity declaration */ +case class ParameterEntityDecl(name: String, entdef: EntityDef) extends EntityDecl { + + final def toString(sb: StringBuffer): StringBuffer = { + sb.append("<!ENTITY % ").append( name ).append(' '); + entdef.toString(sb).append('>'); + } +} + +/** an unparsed entity declaration */ +case class UnparsedEntityDecl( name:String, extID:ExternalID, notation:String ) extends EntityDecl { + final def toString(sb: StringBuffer): StringBuffer = { + sb.append("<!ENTITY ").append( name ).append(' '); + extID.toString(sb).append(" NDATA ").append(notation).append('>'); + } +} +/** a notation declaration */ +case class NotationDecl( name:String, extID:ExternalID ) extends MarkupDecl { + final def toString(sb: StringBuffer): StringBuffer = { + sb.append("<!NOTATION ").append( name ).append(' '); + extID.toString(sb); + } +} + +abstract class EntityDef { + final override def toString(): String = + toString(new StringBuffer()).toString(); + + def toString(sb: StringBuffer): StringBuffer; +} + +case class IntDef(value:String) extends EntityDef { + private def validateValue(): Unit = { + var tmp = value; + var ix = tmp.indexOf('%'); + while( ix != -1) { + val iz = tmp.indexOf(';', ix); + if(iz == -1 && iz == ix + 1) + error("no % allowed in entity value, except for parameter-entity-references"); + else { + val n = tmp.substring(ix, iz); + + if( !Utility.isName( n )) + throw new IllegalArgumentException("interal entity def: \""+n+"\" must be an XML Name"); + + tmp = tmp.substring(iz+1, tmp.length()); + ix = tmp.indexOf('%'); + } + } + } + validateValue(); + + final def toString(sb: StringBuffer): StringBuffer = + Utility.appendQuoted(value, sb); + +} + +case class ExtDef(extID:ExternalID) extends EntityDef { + final def toString(sb: StringBuffer): StringBuffer = + extID.toString(sb); +} + + + +/** a parsed entity reference */ +case class PEReference(ent:String) extends MarkupDecl { + if( !Utility.isName( ent )) + throw new IllegalArgumentException("ent must be an XML Name"); + + final def toString(sb: StringBuffer): StringBuffer = + sb.append('%').append(ent).append(';'); +} + + +// default declarations for attributes + +abstract class DefaultDecl { + override def toString(): String; + def toString(sb: StringBuffer): StringBuffer; +} + +case object REQUIRED extends DefaultDecl { + final override def toString(): String = "#REQUIRED"; + final def toString(sb:StringBuffer) = sb.append("#REQUIRED"); +} + +case object IMPLIED extends DefaultDecl { + final override def toString(): String = "#IMPLIED"; + final def toString(sb:StringBuffer) = sb.append("#IMPLIED"); +} + +case class DEFAULT(fixed:boolean, attValue:String) extends DefaultDecl { + final override def toString(): String = + toString(new StringBuffer()).toString(); + + final def toString(sb:StringBuffer): StringBuffer = { + if(fixed) + sb.append("#FIXED "); + Utility.appendEscapedQuoted( attValue, sb ); + } +} diff --git a/src/library/scala/xml/dtd/DocType.scala b/src/library/scala/xml/dtd/DocType.scala new file mode 100644 index 0000000000..014c7046aa --- /dev/null +++ b/src/library/scala/xml/dtd/DocType.scala @@ -0,0 +1,44 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.dtd; + +/** an XML node for document type declaration + * + * @author Burak Emir + * @param target name of this DOCTYPE + * @param extID None, or Some(external ID of this doctype) + * @param intSubset sequence of internal subset declarations +**/ + +case class DocType( name:String, extID:ExternalID, intSubset:Seq[dtd.Decl]) { + + if( !Utility.isName( name ) ) + throw new IllegalArgumentException(name+" must be an XML Name"); + + /** hashcode for this processing instruction */ + final override def hashCode() = name.hashCode() + 7 * extID.hashCode() + 41*intSubset.toList.hashCode(); + + /** returns "<!DOCTYPE + name + extID? + ("["+intSubSet+"]")? >" */ + final override def toString() = { + val sb = new StringBuffer("<!DOCTYPE "); + sb.append( name ); + sb.append(' '); + sb.append(extID.toString()); + if( intSubset.length > 0 ) { + sb.append('['); + for( val d <- intSubset ) { + sb.append( d.toString() ); + } + sb.append(']'); + } + sb.append('>'); + sb.toString(); + } +} diff --git a/src/library/scala/xml/dtd/DtdTypeSymbol.scala b/src/library/scala/xml/dtd/DtdTypeSymbol.scala new file mode 100644 index 0000000000..633e11e646 --- /dev/null +++ b/src/library/scala/xml/dtd/DtdTypeSymbol.scala @@ -0,0 +1 @@ +package scala.xml.dtd; trait DtdTypeSymbol {} diff --git a/src/library/scala/xml/dtd/ElementValidator.scala b/src/library/scala/xml/dtd/ElementValidator.scala new file mode 100644 index 0000000000..52ba82cb54 --- /dev/null +++ b/src/library/scala/xml/dtd/ElementValidator.scala @@ -0,0 +1,166 @@ +package scala.xml.dtd; + +import ContentModel.ElemName ; +import scala.util.automata._ ; + +/** validate children and/or attributes of an element + * exceptions are created but not thrown. + */ +class ElementValidator() extends Function1[Node,Boolean] { + + var exc: List[ValidationException] = Nil; + + protected var contentModel: ContentModel = _; + protected var dfa: DetWordAutom[ElemName] = _; + protected var adecls: List[AttrDecl] = _; + + /** set content model, enabling element validation */ + def setContentModel(cm:ContentModel) = { + contentModel = cm; cm match { + case ELEMENTS( r ) => + val nfa = ContentModel.Translator.automatonFrom(r, 1); + dfa = new SubsetConstruction(nfa).determinize; + case _ => + dfa = null; + } + } + + def getContentModel = contentModel; + + /** set meta data, enabling attribute validation */ + def setMetaData(adecls: List[AttrDecl]) = + this.adecls = adecls; + + def getIterator(nodes: Seq[Node], skipPCDATA: Boolean): Iterator[ElemName] = + nodes . toList + . filter { x => x match { + case y:SpecialNode => y match { + + case a:Atom[String] if (a.data.asInstanceOf[String].trim().length() == 0 ) => + false; // always skip all-whitespace nodes + + case _ => + !skipPCDATA + + } + case _ => + x.namespace == null + }} + . map { x => ElemName(x.label) } + . elements; + + /** check attributes, return true if md corresponds to attribute declarations in adecls. + */ + def check(md: MetaData): Boolean = { + //Console.println("checking md = "+md); + //Console.println("adecls = "+adecls); + //@todo other exceptions + import MakeValidationException._; + val len: Int = exc.length; + var j = 0; + var ok = new scala.collection.mutable.BitSet(adecls.length); + def find(Key:String): AttrDecl = { + var attr: AttrDecl = null; + val jt = adecls.elements; while(j < adecls.length) { + jt.next match { + case a @ AttrDecl(Key, _, _) => attr = a; ok.set(j); j = adecls.length; + case _ => j = j + 1; + } + } + attr + } + val it = md.elements; while(it.hasNext) { + val attr = it.next; + //Console.println("attr:"+attr); + j = 0; + find(attr.key) match { + + case null => + //Console.println("exc"); + exc = fromUndefinedAttribute( attr.key ) :: exc; + + case AttrDecl(_, tpe, DEFAULT(true, fixedValue)) if(attr.value != fixedValue) => + exc = fromFixedAttribute( attr.key, fixedValue, attr.value.toString()) :: exc; + + case s => + //Console.println("s: "+s); + + } + } + //Console.println("so far:"+(exc.length == len)); + + val missing = ok.toSet( false ); + j = 0; var kt = adecls.elements; while(kt.hasNext) { + kt.next match { + case AttrDecl(key, tpe, REQUIRED) if !ok(j) => + exc = fromMissingAttribute( key, tpe ) :: exc; + j = j + 1; + case _ => + j = j + 1; + } + } + //Console.println("finish:"+(exc.length == len)); + (exc.length == len) //- true if no new exception + } + + /** check children, return true if conform to content model + * @pre contentModel != null + */ + def check(nodes: Seq[Node]): Boolean = contentModel match { + + case ANY => true ; + + case EMPTY => !getIterator(nodes, false).hasNext + + case PCDATA => !getIterator(nodes, true).hasNext; + + case MIXED(ContentModel.Alt(branches @ _*)) => //@todo + val j = exc.length; + def find(Key: String): Boolean = { + var res = false; + val jt = branches.elements; + while(jt.hasNext && !res) + jt.next match { + case ContentModel.Letter(ElemName(Key)) => res = true; + case _ => + } + res + } + + var it = getIterator(nodes, true); while(it.hasNext) { + var label = it.next.name; + if(!find(label)) { + exc = MakeValidationException.fromUndefinedElement(label) :: exc; + } + } + + (exc.length == j) //- true if no new exception + + case _:ELEMENTS => + var q = 0; + val it = getIterator(nodes, false); + //Console.println("it empty from the start? "+(!it.hasNext)); + while( it.hasNext ) { + val e = it.next; + dfa.delta(q).get(e) match { + case Some(p) => q = p; + case _ => throw ValidationException("element "+e+" not allowed here") + } + //Console.println("q now " + q); + } + dfa.isFinal(q) //- true if arrived in final state + } + + /** applies various validations - accumulates error messages in exc + * @todo: fail on first error, ignore other errors (rearranging conditions) + */ + def apply(n: Node): Boolean = { + //- ? check children + var res = (null == contentModel) || check( n.child ); + + //- ? check attributes + res = ((null == adecls) || check( n.attributes )) && res; + + res + } +} diff --git a/src/library/scala/xml/dtd/ExternalID.scala b/src/library/scala/xml/dtd/ExternalID.scala new file mode 100644 index 0000000000..965d330a99 --- /dev/null +++ b/src/library/scala/xml/dtd/ExternalID.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.dtd; + +/** an ExternalIDs - either PublicID or SystemID + * + * @author Burak Emir + * @param target target name of this PI + * @param text text contained in this node, may not contain "?>" +**/ + +abstract class ExternalID { + + /** returns "PUBLIC "+publicLiteral+" SYSTEM "+systemLiteral */ + override def toString(): String; + + /** returns "PUBLIC "+publicLiteral+" SYSTEM "+systemLiteral */ + def toString(sb: StringBuffer): StringBuffer; + + def systemId: String; + +} + +/** a system identifier + * + * @author Burak Emir + * @param systemLiteral the system identifier literal +**/ + +case class SystemID( systemId:String ) extends ExternalID with parsing.TokenTests{ + + if( !checkSysID( systemId ) ) + throw new IllegalArgumentException( + "can't use both \" and ' in systemLiteral" + ); + /** returns " SYSTEM "+systemLiteral */ + final override def toString() = + Utility.systemLiteralToString( systemId ); + + final def toString(sb: StringBuffer): StringBuffer = + Utility.systemLiteralToString( sb, systemId ); +} + + +/** a public identifier + * + * @author Burak Emir + * @param publicLiteral the public identifier literal + * @param systemLiteral (can be null for notation pubIDs) the system identifier literal +**/ +case class PublicID( publicId:String, systemId:String ) extends ExternalID with parsing.TokenTests{ + //Console.println("constructing PublicID \""+publicLiteral+"\" "+systemLiteral); + + //Console.println("util returns "+checkPubID( publicLiteral )); + + if( !checkPubID( publicId )) + throw new IllegalArgumentException( + "publicId must consist of PubidChars" + ); + if( systemId != null && !checkSysID( systemId ) ) + throw new IllegalArgumentException( + "can't use both \" and ' in systemId" + ); + + /** the constant "#PI" */ + final def label = "#PI"; + + /** always empty */ + final def attribute = Node.NoAttributes; + + /** always empty */ + final def child = Nil; + + /** returns "PUBLIC "+publicId+" SYSTEM "+systemId */ + final override def toString(): String = { + toString(new StringBuffer()).toString(); + } + + /** appends "PUBLIC "+publicId+" SYSTEM "+systemId to argument */ + final def toString(sb: StringBuffer): StringBuffer = { + Utility.publicLiteralToString( sb, publicId ).append(' '); + if(systemId!=null) + Utility.systemLiteralToString( sb, systemId ); + else + sb + } +} diff --git a/src/library/scala/xml/dtd/Scanner.scala b/src/library/scala/xml/dtd/Scanner.scala new file mode 100644 index 0000000000..c51ac7cbec --- /dev/null +++ b/src/library/scala/xml/dtd/Scanner.scala @@ -0,0 +1,87 @@ +package scala.xml.dtd ; + +/** Scanner for regexps (content models in DTD element declarations) + * todo: cleanup + */ +class Scanner extends Tokens with parsing.TokenTests { + + // zzz constants zzz + final val ENDCH = '\u0000'; + + // zzz fields zzz + var token:Int = END; + var value:String = _; + + private var it:Iterator[Char] = null; + private var c:Char = 'z'; + + + /** initializes the scanner on input s */ + final def initScanner( s:String ) = { + //Console.println("[scanner init on \""+s+"\"]"); + value = ""; + it = Iterator.fromString( s ); + token = 1+END; + next; + nextToken; + } + + /** scans the next token */ + final def nextToken:Unit = { + if( token != END ) token = readToken; + //Console.println("["+token2string( token )+"]"); + } + + // zzz scanner methods zzz + + // todo: see XML specification... probably isLetter,isDigit is fine + final def isIdentChar = ( ('a' <= c && c <= 'z') + || ('A' <= c && c <= 'Z')); + + final def next = if( it.hasNext ) c = it.next else c = ENDCH; + + final def acc( d:char ):Unit = + if( c == d ) next; else error("expected '"+d+"' found '"+c+"' !"); + + final def accS( ds:Seq[Char] ):Unit = { + val jt = ds.elements; while( jt.hasNext ) { acc( jt.next ) } + } + + /* + final def isSpace = c match { + case '\u0020' | '\u0009' | '\u000D' | '\u000A' => true + case _ => false; + } + */ + + final def readToken:int = + if(isSpace(c)) { + while( isSpace(c) ) { + c = it.next; + } + S + } else c match { + case '(' => next; LPAREN + case ')' => next; RPAREN + case ',' => next; COMMA + case '*' => next; STAR + case '+' => next; PLUS + case '?' => next; OPT + case '|' => next; CHOICE + case '#' => next; accS( "PCDATA" ); TOKEN_PCDATA + case ENDCH => END; + case _ => + if( isNameStart( c ) ) name; // NAME + else { + error("unexpected character:"+c); END + } + } + + final def name = { + val sb = new StringBuffer(); + do { sb.append( c ); next } while ( isNameChar( c ) ) ; + value = sb.toString(); + NAME + } + +} diff --git a/src/library/scala/xml/dtd/Tokens.scala b/src/library/scala/xml/dtd/Tokens.scala new file mode 100644 index 0000000000..27935c1d08 --- /dev/null +++ b/src/library/scala/xml/dtd/Tokens.scala @@ -0,0 +1,32 @@ +package scala.xml.dtd ; + +class Tokens { + + // Tokens + + final val TOKEN_PCDATA = 0; + final val NAME = 1; + final val LPAREN = 3; + final val RPAREN = 4; + final val COMMA = 5; + final val STAR = 6; + final val PLUS = 7; + final val OPT = 8; + final val CHOICE = 9; + final val END = 10; + final val S = 13; + + final def token2string( i:int ):String = i match { + case 0 => "#PCDATA"; + case 1 => "NAME"; + case 3 => "("; + case 4 => ")"; + case 5 => ","; + case 6 => "*"; + case 7 => "+"; + case 8 => "?"; + case 9 => "|"; + case 10 => "END"; + case 13 => " "; + } +} diff --git a/src/library/scala/xml/dtd/ValidationException.scala b/src/library/scala/xml/dtd/ValidationException.scala new file mode 100644 index 0000000000..337259ffb1 --- /dev/null +++ b/src/library/scala/xml/dtd/ValidationException.scala @@ -0,0 +1,33 @@ +package scala.xml.dtd ; + +case class ValidationException( e:String ) extends Exception( e ); + +object MakeValidationException { + def fromFixedAttribute( k: String, value: String, actual: String ) = + ValidationException("value of attribute " + k + " FIXED to \""+value+"\", but document tries \""+actual+"\""); + + def fromNonEmptyElement() = { + new ValidationException("element should be *empty*"); + } + def fromUndefinedElement( label:String ) = + new ValidationException("element \""+ label +"\" not allowed here"); + + def fromUndefinedAttribute( key:String ) = + new ValidationException("attribute " + key +" not allowed here" ); + + def fromMissingAttribute( allKeys:scala.collection.Set[String] ) = { + val sb = new StringBuffer(); + sb.append("missing value for REQUIRED attribute"); + if( allKeys.size > 1 ) sb.append('s'); + val it = allKeys.elements; + while (it.hasNext) { + sb.append('\'').append(it.next).append('\'') + } + new ValidationException(sb.toString()); + } + + def fromMissingAttribute( key: String, tpe: String ) = { + new ValidationException("missing value for REQUIRED attribute "+key+" of type "+tpe); + } + +} diff --git a/src/library/scala/xml/factory/Binder.scala b/src/library/scala/xml/factory/Binder.scala new file mode 100644 index 0000000000..d387e90139 --- /dev/null +++ b/src/library/scala/xml/factory/Binder.scala @@ -0,0 +1,43 @@ +package scala.xml.factory ; + +import scala.xml.parsing.ValidatingMarkupHandler; + +abstract class Binder(val preserveWS: Boolean) extends ValidatingMarkupHandler { + + var result: NodeBuffer = new NodeBuffer(); + + def reportSyntaxError(pos:Int, str:String) = {} + + final def procInstr(pos: Int, target: String, txt: String ) = + ProcInstr(target, txt); + + final def comment(pos: Int, txt: String ) = + Comment( txt ); + + final def entityRef(pos: Int, n: String) = + EntityRef( n ); + + final def text(pos: Int, txt:String) = + Text( txt ); + + final def traverse(n:Node): Unit = n match { + case x:ProcInstr => result &+ procInstr(0, x.target, x.text) + case x:Comment => result &+ comment(0, x.text) + case x:Text => result &+ text(0, x.data) + case x:EntityRef => result &+ entityRef(0, x.entityName) + case _ => + elemStart(0, n.prefix, n.label, n.attributes, n.scope); + val old = result; + result = new NodeBuffer(); + for(val m <- n.child) + traverse(m); + result = old &+ elem(0, n.prefix, n.label, n.attributes, n.scope, NodeSeq.fromSeq(result)).toList; + elemEnd(0, n.prefix, n.label); + } + + final def validate(n:Node): Node = { + this.rootLabel = n.label; + traverse(n); + result(0) + } +} diff --git a/src/library/scala/xml/factory/LoggedNodeFactory.scala b/src/library/scala/xml/factory/LoggedNodeFactory.scala new file mode 100644 index 0000000000..cb57069ac2 --- /dev/null +++ b/src/library/scala/xml/factory/LoggedNodeFactory.scala @@ -0,0 +1,78 @@ +package scala.xml.factory; + +/** This class logs what the nodefactory is actually doing. +If you want to see what happens during loading, use it like this: +object testLogged with Application { + + val x = new scala.xml.nobinding.NoBindingFactoryAdapter + with scala.xml.LoggedNodeFactory[scala.xml.Elem]() + with scala.util.logging.ConsoleLogger; + + Console.println("Start"); + + val doc = x.loadXML(new org.xml.sax.InputSource("http://lamp.epfl.ch/~buraq")); + + Console.println("End"); + + Console.println(doc); +} + +*/ +abstract class LoggedNodeFactory[A <: Node] +extends NodeFactory[A] +with scala.util.logging.Logged { + + // configuration values; + val logNode = true; + val logText = false; + val logComment = false; + val logProcInstr = false; + + final val NONE = 0; + final val CACHE = 1; + final val FULL = 2; + /** 0 = no loggging, 1 = cache hits, 2 = detail */ + val logCompressLevel = 1; + + // methods of NodeFactory + + /** logged version of makeNode method */ + override def makeNode(pre:String, label:String, attrSeq:MetaData, scope: NamespaceBinding, children:Seq[Node]): A = { + if(logNode) + log("[makeNode for "+label+"]"); + + val hash = Utility.hashCode(pre, label, attrSeq.hashCode(), scope.hashCode(), children) ; + + /* + if(logCompressLevel >= FULL) { + log("[hashcode total:"+hash); + log(" elem name "+uname+" hash "+ ? )); + log(" attrs "+attrSeq+" hash "+attrSeq.hashCode()); + log(" children :"+children+" hash "+children.hashCode()); + } + */ + if(!cache.get( hash ).isEmpty && (logCompressLevel >= CACHE)) + log("[cache hit !]"); + + super.makeNode(pre, label, attrSeq, scope, children); + } + + override def makeText(s: String) = { + if(logText) + log("[makeText:\""+s+"\"]"); + super.makeText( s ); + } + + override def makeComment(s: String): Seq[Comment] = { + if(logComment) + log("[makeComment:\""+s+"\"]"); + super.makeComment( s ); + } + + override def makeProcInstr(t: String, s: String): Seq[ProcInstr] = { + if(logProcInstr) + log("[makeProcInstr:\""+t+" "+ s+"\"]"); + super.makeProcInstr(t,s); + } + +} diff --git a/src/library/scala/xml/factory/NodeFactory.scala b/src/library/scala/xml/factory/NodeFactory.scala new file mode 100644 index 0000000000..7564659c1a --- /dev/null +++ b/src/library/scala/xml/factory/NodeFactory.scala @@ -0,0 +1,77 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id $ +\* */ + +package scala.xml.factory; + +import scala.collection.Map ; +import scala.collection.mutable ; + +[_trait_] abstract class NodeFactory[A <: Node] { + + val ignoreComments = false; + val ignoreProcInstr = false; + + /* default behaviour is to use hash-consing */ + val cache = new mutable.HashMap[int,List[A]](); + + protected def create(pre: String, name: String, attrs: MetaData, scope: NamespaceBinding, children:Seq[Node]): A; + + protected def construct(hash:Int, old:List[A], pre: String, name: String, attrSeq:MetaData, scope: NamespaceBinding, children:Seq[Node]): A = { + val el = create(pre, name, attrSeq, scope, children); + cache.update( hash, el::old ); + el + } + + /** faster equality, because */ + def eqElements(ch1:Seq[Node], ch2:Seq[Node]): Boolean = { + (ch1.length == ch2.length) && { + val it1 = ch1.elements; + val it2 = ch2.elements; + var res = true; + while(res && it1.hasNext) { + res = it1.next.eq(it2.next); + } + res + } + } + + def nodeEquals(n: Node, pre: String, name: String, attrSeq:MetaData, scope: NamespaceBinding, children:Seq[Node]) = ( + (n.prefix == pre) + &&(n.label == name) + &&(n.attributes == attrSeq) + // scope?? + &&(eqElements(n.child,children))); + + def makeNode(pre: String, name: String, attrSeq:MetaData, scpe: NamespaceBinding, children:Seq[Node]): A = { + //Console.println("NodeFactory::makeNode("+pre+","+name+","+attrSeq+","+scpe+","+children+")"); + val hash = Utility.hashCode( pre, name, attrSeq.hashCode(), scpe.hashCode(), children ) ; + cache.get( hash ) match { + case Some(list) => // find structurally equal + val it = list.elements; + val lookup = it.find { x => nodeEquals(x, pre, name, attrSeq, scpe, children) }; + lookup match { + case Some(x) => + //Console.println("[cache hit !]"+x); + x; // return cached elem + case _ => construct(hash, list, pre, name, attrSeq, scpe, children); + } + case _ => construct(hash, Nil, pre, name, attrSeq, scpe, children) + } + } + + def makeText(s: String) = + Text( s ); + + def makeComment(s: String): Seq[Comment] = + if(ignoreComments) Nil else List(Comment( s )); + + def makeProcInstr(t: String, s: String): Seq[ProcInstr] = + if(ignoreProcInstr) Nil else List(ProcInstr(t, s)); + +} diff --git a/src/library/scala/xml/parsing/ConstructingHandler.scala b/src/library/scala/xml/parsing/ConstructingHandler.scala new file mode 100644 index 0000000000..fac22b7015 --- /dev/null +++ b/src/library/scala/xml/parsing/ConstructingHandler.scala @@ -0,0 +1,24 @@ +package scala.xml.parsing; + +/** implementation of MarkupHandler that constructs nodes */ +abstract class ConstructingHandler extends MarkupHandler { + + val preserveWS: boolean; + + def elem(pos: int, pre: String, label: String, attrs: MetaData, pscope: NamespaceBinding, nodes: NodeSeq): NodeSeq = + Elem(pre, label, attrs, pscope, nodes:_*); + + + def procInstr(pos: Int, target: String, txt: String ) = + ProcInstr(target, txt); + + def comment(pos: Int, txt: String ) = + Comment( txt ); + + def entityRef(pos: Int, n: String) = + EntityRef( n ); + + def text(pos: Int, txt:String) = + Text( txt ); + +} diff --git a/src/library/scala/xml/parsing/ConstructingParser.scala b/src/library/scala/xml/parsing/ConstructingParser.scala new file mode 100644 index 0000000000..fafabc916d --- /dev/null +++ b/src/library/scala/xml/parsing/ConstructingParser.scala @@ -0,0 +1,61 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.parsing ; + +import scala.io.Source; + +object ConstructingParser { + + def fromFile(inp: java.io.File, preserveWS: Boolean) = { + val p = new ConstructingParser(Source.fromFile(inp), preserveWS); + p.nextch; + p + } + + def fromSource(inp: scala.io.Source, preserveWS: Boolean) = { + val p = new ConstructingParser(inp, preserveWS); + p.nextch; + p + } +} + +/** an xml parser. parses XML and invokes callback methods of a MarkupHandler. Don't forget to call next.ch on a freshly + * instantiated parser in order to initialize it. If you get the parser from the object method, initialization is already done for you. + * + *<pre> +object parseFromURL { + def main(args:Array[String]): Unit = { + val url = args(0); + val src = scala.io.Source.fromURL(url); + val cpa = scala.xml.parsing.ConstructingParser.fromSource(src, false); // fromSource initializes automatically + val doc = cpa.document(); + + // let's see what it is + val ppr = new scala.xml.PrettyPrinter(80,5); + val ele = doc.docElem; + Console.println("finished parsing"); + val out = ppr.format(ele); + Console.println(out); + } +} +</pre> + */ +class ConstructingParser(inp: Source, presWS:Boolean) +extends ConstructingHandler +with ExternalSources +with MarkupParser { + + // default impl. of Logged + override def log(msg:String): Unit = {} + + val preserveWS = presWS; + val input = inp; +} + diff --git a/src/library/scala/xml/parsing/DefaultMarkupHandler.scala b/src/library/scala/xml/parsing/DefaultMarkupHandler.scala new file mode 100644 index 0000000000..14fd8ec9a7 --- /dev/null +++ b/src/library/scala/xml/parsing/DefaultMarkupHandler.scala @@ -0,0 +1,16 @@ +package scala.xml.parsing; + +/** default implemenation of markup handler always returns NodeSeq.Empty */ +abstract class DefaultMarkupHandler extends MarkupHandler { + + def elem(pos: int, pre: String, label: String, attrs: MetaData, scope:NamespaceBinding, args: NodeSeq) = NodeSeq.Empty; + + def procInstr(pos: Int, target: String, txt: String) = NodeSeq.Empty; + + def comment(pos: Int, comment: String ): NodeSeq = NodeSeq.Empty; + + def entityRef(pos: Int, n: String) = NodeSeq.Empty; + + def text(pos: Int, txt:String) = NodeSeq.Empty; + +} diff --git a/src/library/scala/xml/parsing/ExternalSources.scala b/src/library/scala/xml/parsing/ExternalSources.scala new file mode 100644 index 0000000000..d4a1511e99 --- /dev/null +++ b/src/library/scala/xml/parsing/ExternalSources.scala @@ -0,0 +1,76 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.parsing ; + +import scala.io.Source; +import java.net._; +import java.io._; + +trait ExternalSources : (ExternalSources with MarkupParser with MarkupHandler) { + + + private def externalSourceFromURL(url:URL): Source = { + val in = + new BufferedReader( + new InputStreamReader( + url.openStream())); + + //@todo: replace this hack with proper Source implementation + + val str = new StringBuffer(); + var inputLine:String = null; + + //while (inputLine = in.readLine()) != null) { + while ({inputLine = in.readLine(); inputLine} != null) { + // Console.println(inputLine); // DEBUG + str.append(inputLine); + str.append('\n'); // readable output + } + in.close(); + + class MyClass extends Source { + + def newIter = new Iterator[Char] { + var i = -1; + private val len = str.length()-1; + def hasNext = i < len; + def next = { + i = i + 1; + str.charAt(i); + } + } + + val iter = newIter; + + def reset: Source = new MyClass; + + override var descr = url.toExternalForm(); + } + + return new MyClass; + } + + def externalSource(systemId: String): Source = { + //Console.println("in external source("+systemId+")"); + if(systemId.startsWith("http:")) { + return externalSourceFromURL(new URL(systemId)); + } + + var fileStr = input.descr; + + if(input.descr.startsWith("file:")) { + fileStr = input.descr.substring(5, input.descr.length()); + } else + fileStr = fileStr.substring(0, + fileStr.lastIndexOf(java.io.File.separator)+1); + Source.fromFile(fileStr + systemId); + } + +} diff --git a/src/library/scala/xml/parsing/FactoryAdapter.scala b/src/library/scala/xml/parsing/FactoryAdapter.scala new file mode 100644 index 0000000000..e23032ca70 --- /dev/null +++ b/src/library/scala/xml/parsing/FactoryAdapter.scala @@ -0,0 +1,344 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.xml.parsing ; + +import java.io._ ; +import scala.collection.mutable.{HashMap,Stack}; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; + +import org.xml.sax.ErrorHandler; +import org.xml.sax.Locator; +import org.xml.sax.InputSource; + +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.SAXParserFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; + + +/** SAX adapter class, for use with Java SAX parser. Keeps track of + * namespace bindings, without relying on namespace handling of the + * underlying SAX parser. + */ +abstract class FactoryAdapter extends DefaultHandler() { + + val buffer = new StringBuffer(); + val attribStack = new Stack[MetaData]; + val hStack = new Stack[Node]; // [ element ] contains siblings + val tagStack = new Stack[String]; + var scopeStack = new Stack[NamespaceBinding]; + + var curTag : String = null ; + var capture:boolean = false; + + // abstract methods + + /** Tests if an XML element contains text. + * @return true if element named <code>localName</code> contains text. + */ + def nodeContainsText( localName:String ):boolean ; // abstract + + /** creates an new non-text(tree) node. + * @param elemName + * @param attribs + * @param chIter + * @return a new XML element. + */ + def createNode(pre: String, elemName: String, attribs: MetaData, scope: NamespaceBinding, chIter: List[Node] ):Node; //abstract + + /** creates a Text node. + * @param text + * @return a new Text node. + */ + def createText( text:String ):Text; // abstract + + // + // ContentHandler methods + // + + val normalizeWhitespace = false; + + /** Characters. + * @param ch + * @param offset + * @param length + */ + override def characters(ch: Array[Char], offset: Int, length: Int): Unit = { + + if (capture) { + if( normalizeWhitespace ) { // normalizing whitespace is not compliant, but useful */ + var i:int = offset; + var ws:boolean = false; + while (i < offset + length) { + if ( Character.isWhitespace( ch(i) ) ) { + if (!ws) { + buffer.append(' '); + ws = true; + } + } else { + buffer.append(ch(i)); + ws = false; + } + i = i+1; + } + } else { // compliant:report every character + + buffer.append( ch, offset, length ); + + } + } + } + + //var elemCount = 0; //STATISTICS + + /* ContentHandler methods */ + + /* Start prefix mapping - use default impl. + def startPrefixMapping( prefix:String , uri:String ):Unit = {} + */ + + + + /* Start element. */ + override def startElement(uri:String, _localName:String, qname:String, attributes:Attributes ):Unit = { + /*elemCount = elemCount + 1; STATISTICS */ + captureText(); + //Console.println("FactoryAdapter::startElement("+uri+","+_localName+","+qname+","+attributes+")"); + tagStack.push(curTag); + curTag = qname; //localName ; + + val colon = qname.indexOf(':'); + val localName = if(-1 == colon) qname else qname.substring(colon+1,qname.length()); + + //Console.println("FactoryAdapter::startElement - localName ="+localName); + + capture = nodeContainsText(localName) ; + + hStack.push( null ); + var m: MetaData = Null; + + var scpe = scopeStack.top; + for( val i <- List.range( 0, attributes.getLength() )) { + //val attrType = attributes.getType(i); // unused for now + val qname = attributes.getQName(i); + val value = attributes.getValue(i); + val colon = qname.indexOf(':'); + if(-1 != colon) { // prefixed attribute + val pre = qname.substring(0, colon); + val key = qname.substring(colon+1, qname.length()); + if("xmlns" == pre) + scpe = value.length() match { + case 0 => new NamespaceBinding(key, null, scpe); + case _ => new NamespaceBinding(key, value, scpe); + } + else + m = new PrefixedAttribute(pre, key, value, m) + } else if("xmlns" == qname) + scpe = value.length() match { + case 0 => new NamespaceBinding(null, null, scpe); + case _ => new NamespaceBinding(null, value, scpe); + } + else + m = new UnprefixedAttribute(qname, value, m) + } + scopeStack.push(scpe); + attribStack.push( m ); + {} + } // startElement(String,String,String,Attributes) + + + /** captures text, possibly normalizing whitespace + */ + def captureText():Unit = { + if (capture == true) { + val text = buffer.toString(); + if(( text.length() > 0 )&&( !( text.equals(" ")))) { + val _ = hStack.push( createText( text ) ); + } + } + buffer.setLength(0); + } + + /** End element. + * @param uri + * @param localName + * @param qname + * @throws org.xml.sax.SAXException if .. + */ + override def endElement(uri:String , _localName:String , qname:String ):Unit = { + captureText(); + + val metaData = attribStack.pop; + + // reverse order to get it right + var v:List[Node] = Nil; + var child:Node = hStack.pop; + while( child != null ) { + v = child::v; + child = hStack.pop; + } + + val colon = qname.indexOf(':'); + val localName = if(-1 == colon) qname else qname.substring(colon+1,qname.length()); + + val scp = scopeStack.pop; + // create element + rootElem = if(-1 == colon) + createNode( null, localName, metaData, scp, v ); + else + createNode( qname.substring(0,colon), localName, metaData, scp, v ); + + hStack.push(rootElem); + + // set + curTag = tagStack.pop; + + if (curTag != null) // root level + capture = nodeContainsText(curTag); + else + capture = false; + + } // endElement(String,String,String) + + // + // ErrorHandler methods + // + + /** Warning.*/ + override def warning(ex:SAXParseException ):Unit = { + // ignore warning, crimson warns even for entity resolution! + //printError("Warning", ex); + } + /** Error. */ + override def error(ex:SAXParseException ):Unit = { + printError("Error", ex); + } + + /** Fatal error.*/ + override def fatalError(ex:SAXParseException ):Unit = { + printError("Fatal Error", ex); + } + + // + // Protected methods + // + + /** Prints the error message */ + protected def printError( errtype:String , ex:SAXParseException ):Unit = { + + System.err.print("["); + System.err.print(errtype); + System.err.print("] "); + + var systemId = ex.getSystemId(); + if (systemId != null) { + val index = systemId.lastIndexOf('/'); + if (index != -1) + systemId = systemId.substring(index + 1); + //System.err.print(systemId); + } + + System.err.print(':'); + System.err.print(ex.getLineNumber()); + System.err.print(':'); + System.err.print(ex.getColumnNumber()); + System.err.print(": "); + System.err.print(ex.getMessage()); + System.err.println(); + System.err.flush(); + + } + + var rootElem : Node = null:Node; + + //FactoryAdapter + // MAIN + // + + /** load XML document + * @param source + * @return a new XML document object + */ + def loadXML( source:InputSource ):Node = { + + // variables + var parser:SAXParser = null; + + // create parser + try { + val f = SAXParserFactory.newInstance(); + f.setNamespaceAware( false ); + parser = f.newSAXParser(); + } catch { + case ( e:Exception ) => { + System.err.println("error: Unable to instantiate parser"); + System.exit(-1); + } + } + + // parse file + try { + //System.err.println("[parsing \"" + source + "\"]"); + scopeStack.push(TopScope); + parser.parse( source, this ); + scopeStack.pop; + } catch { + case ( e:SAXParseException ) => { + // ignore + } + case ( e:Exception ) => { + System.err.println("error: Parse error occurred - " + e.getMessage()); + if (e.isInstanceOf[ SAXException ]) { + (e.asInstanceOf[ SAXException ]) + .getException() + .printStackTrace( System.err ); + } else { + e.printStackTrace(System.err); + } + } + } // catch + //System.err.println("[FactoryAdapter: total #elements = "+elemCount+"]"); + rootElem + + } // loadXML + + + + /** loads XML from given file */ + def loadFile( file:File ):Node = loadXML( new InputSource( + new FileInputStream( file ) + )); + + /** loads XML from given file descriptor */ + def loadFile( fileDesc:FileDescriptor ):Node = loadXML( new InputSource( + new FileInputStream( fileDesc ) + )); + + /** loads XML from given file */ + def loadFile( fileName:String ):Node = loadXML( new InputSource( + new FileInputStream( fileName ) + )); + + /** loads XML from given InputStream */ + def load( is:InputStream ):Node = loadXML( new InputSource( is )); + + /** loads XML from given Reader */ + def load( reader:Reader ):Node = loadXML( new InputSource( reader )); + + /** loads XML from given sysID */ + def load( sysID:String ):Node = loadXML( new InputSource( sysID )); + +} diff --git a/src/library/scala/xml/parsing/FatalError.scala b/src/library/scala/xml/parsing/FatalError.scala new file mode 100644 index 0000000000..bd007d0df9 --- /dev/null +++ b/src/library/scala/xml/parsing/FatalError.scala @@ -0,0 +1,3 @@ +package scala.xml.parsing ; + +case class FatalError(msg:String) extends java.lang.RuntimeException(msg); diff --git a/src/library/scala/xml/parsing/MarkupHandler.scala b/src/library/scala/xml/parsing/MarkupHandler.scala new file mode 100644 index 0000000000..d90dea2752 --- /dev/null +++ b/src/library/scala/xml/parsing/MarkupHandler.scala @@ -0,0 +1,148 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.parsing; + +import scala.io.Source; +import scala.collection.mutable.{ HashMap, Map } +import scala.xml.dtd._ ; + +import scala.util.logging._; + +/** class that handles markup - provides callback methods to MarkupParser. + * the default is nonvalidating behaviour + * + * @todo can we ignore more entity declarations (i.e. those with extIDs)? + * @todo expanding entity references + */ +abstract class MarkupHandler extends AnyRef with Logged { + + // impl. of Logged + //def log(msg:String) = {} + + /** returns true is this markup handler is validing */ + val isValidating: Boolean = false; + + var decls: List[Decl] = Nil; + + var ent: Map[String, EntityDecl] = new HashMap[String, EntityDecl](); + + def lookupElemDecl(Label: String): ElemDecl = { + def lookup(xs:List[Decl]): ElemDecl = xs match { + case (z @ ElemDecl(Label, _)) :: zs => return z; + case _::zs => lookup(zs); + case _ => return null + } + lookup(decls) + } + + def replacementText( entityName: String ): Source = ent.get(entityName) match { + case Some(ParsedEntityDecl(_, IntDef(value))) => + Source.fromString(value); + case Some(ParameterEntityDecl(_, IntDef(value))) => + Source.fromString(" "+value+" "); + case Some(_) => + Source.fromString("<!-- "+entityName+"; -->"); + case None => + Source.fromString("<!-- unknown entity "+entityName+"; -->") + } + + + //def checkChildren(pos:int, pre: String, label:String,ns:NodeSeq): Unit = {} + + def endDTD(n:String): Unit = {} + + /** callback method invoked by MarkupParser after start-tag of element. + * + * @param pos the position in the sourcefile + * @param pre the prefix + * @param label the local name + * @param attrs the attributes (metadata) + */ + def elemStart(pos: int, pre: String, label: String, attrs: MetaData, scope:NamespaceBinding): Unit = {} + + /** callback method invoked by MarkupParser after end-tag of element. + * + * @param pos the position in the sourcefile + * @param pre the prefix + * @param label the local name + * @param attrs the attributes (metadata) + */ def elemEnd(pos: int, pre: String, label: String): Unit = {} + + /** callback method invoked by MarkupParser after parsing an elementm, + * between the elemStart and elemEnd callbacks + * + * + * @param pos the position in the sourcefile + * @param pre the prefix + * @param label the local name + * @param attrs the attributes (metadata) + * @param args the children of this element + */ + def elem(pos: int, pre: String, label: String, attrs: MetaData, scope:NamespaceBinding, args: NodeSeq): NodeSeq; + + /** callback method invoked by MarkupParser after parsing PI. + */ + def procInstr(pos: Int, target: String, txt: String): NodeSeq; + + /** callback method invoked by MarkupParser after parsing comment. + */ + def comment(pos: Int, comment: String ): NodeSeq; + + /** callback method invoked by MarkupParser after parsing entity ref. + * @todo expanding entity references + */ + def entityRef(pos: Int, n: String): NodeSeq; + + /** callback method invoked by MarkupParser after parsing text. + */ + def text(pos: Int, txt:String): NodeSeq; + + // DTD handler methods + + def elemDecl(n: String, cmstr: String): Unit = {} + + def attListDecl(name: String, attList: List[AttrDecl]): Unit = {} + + def parameterEntityDecl(name: String, edef: EntityDef): Unit = { + //log("parameterEntityDecl("+name+","+edef+")"); + edef match { + case _:ExtDef if !isValidating => + ; // ignore (cf REC-xml 4.4.1) + case _ => + val y = ParameterEntityDecl(name, edef); + decls = y :: decls; + ent.update(name, y); + //log("ent.get(..) = "+ent.get(name)); + } + } + + def parsedEntityDecl(name: String, edef: EntityDef): Unit = edef match { + case _:ExtDef if !isValidating => + ; // ignore (cf REC-xml 4.8 and 4.4.1) + case _ => + val y = ParsedEntityDecl(name, edef); + decls = y :: decls; + ent.update(name, y) + } + + def unparsedEntityDecl(name: String, extID: ExternalID, notat: String): Unit = + {} + + def notationDecl(notat: String, extID: ExternalID): Unit = + {} + + def peReference(name: String): Unit = + decls = PEReference( name ) :: decls; + + /** report a syntax error */ + def reportSyntaxError(pos: Int, str: String): Unit; + +} + diff --git a/src/library/scala/xml/parsing/MarkupParser.scala b/src/library/scala/xml/parsing/MarkupParser.scala new file mode 100644 index 0000000000..09569bd106 --- /dev/null +++ b/src/library/scala/xml/parsing/MarkupParser.scala @@ -0,0 +1,1223 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.parsing; + +import scala.io.Source; +import scala.xml.dtd._ ; + +/** + * An XML parser. + * + * Parses XML 1.0, invokes callback methods of a MarkupHandler + * and returns whatever the markup handler returns. Use + * <code>ConstructingParser</code> if you just want to parse XML to + * construct instances of <code>scala.xml.Node</code>. + * + * While XML elements are returned, DTD declarations - if handled - are + * collected using side-effects. + */ +[_trait_] abstract class MarkupParser: (MarkupParser with MarkupHandler) extends AnyRef with TokenTests { + + val input: Source; + + /** if true, does not remove surplus whitespace */ + val preserveWS: Boolean; + + def externalSource(systemLiteral: String): Source; + + // + // variables, values + // + + var curInput: Source = input; + + /** the handler of the markup, returns this */ + private val handle: MarkupHandler = this; + + /** stack of inputs */ + var inpStack: List[Source] = Nil; + + /** holds the position in the source file */ + var pos: Int = _; + + + /* used when reading external subset */ + var extIndex = -1; + + /** holds temporary values of pos */ + var tmppos: Int = _; + + /** holds the next character */ + var ch: Char = _; + + /** character buffer, for names */ + protected val cbuf = new StringBuffer(); + + var dtd: DTD = null; + + var eof: Boolean = false; + + // + // methods + // + + /** <? prolog ::= xml S ... ?> + */ + def xmlProcInstr(): MetaData = { + xToken("xml"); + xSpace; + val Pair(md,scp) = xAttributes(TopScope); + if(scp != TopScope) + reportSyntaxError("no xmlns definitions here, please."); + xToken('?'); + xToken('>'); + md + } + + /** <? prolog ::= xml S + * // this is a bit more lenient than necessary... + */ + def prolog(): Tuple3[Option[String], Option[String], Option[Boolean]] = { + + //Console.println("(DEBUG) prolog"); + var n = 0; + var info_ver: Option[String] = None; + var info_enc: Option[String] = None; + var info_stdl: Option[Boolean] = None; + + var m = xmlProcInstr(); + + xSpace; + + m.getValue("version") match { + case null => ; + case "1.0" => info_ver = Some("1.0"); n = n + 1; + case _ => reportSyntaxError("cannot deal with versions != 1.0"); + } + + m.getValue("encoding") match { + case null => ; + case enc => if (!isValidIANAEncoding(enc.toString())) + reportSyntaxError("\"" + enc + "\" is not a valid encoding"); + else { + info_enc = Some(enc.toString()); + n = n + 1; + } + } + m.getValue("standalone") match { + case null => ; + case "yes" => info_stdl = Some(true); n = n + 1; + case "no" => info_stdl = Some(false); n = n + 1; + case _ => reportSyntaxError("either 'yes' or 'no' expected"); + } + + if(m.length - n != 0) { + reportSyntaxError("VersionInfo EncodingDecl? SDDecl? or '?>' expected!"); + } + //Console.println("[MarkupParser::prolog] finished parsing prolog!"); + Tuple3(info_ver,info_enc,info_stdl) + } + + /** prolog, but without standalone */ + def textDecl(): Tuple2[Option[String],Option[String]] = { + + var info_ver: Option[String] = None; + var info_enc: Option[String] = None; + + var m = xmlProcInstr(); + var n = 0; + + m.getValue("version") match { + case null => ; + case "1.0" => info_ver = Some("1.0"); n = n + 1; + case _ => reportSyntaxError("cannot deal with versions != 1.0"); + } + + m.getValue("encoding") match { + case null => ; + case enc => if (!isValidIANAEncoding(enc.toString())) + reportSyntaxError("\"" + enc + "\" is not a valid encoding"); + else { + info_enc = Some(enc.toString()); + n = n + 1; + } + } + + if(m.length - n != 0) { + reportSyntaxError("VersionInfo EncodingDecl? or '?>' expected!"); + } + //Console.println("[MarkupParser::textDecl] finished parsing textdecl"); + Tuple2(info_ver, info_enc); + } + + /** + *[22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? + *[23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>' + *[24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"') + *[25] Eq ::= S? '=' S? + *[26] VersionNum ::= '1.0' + *[27] Misc ::= Comment | PI | S + */ + + def document(): Document = { + + //Console.println("(DEBUG) document"); + + this.dtd = null; + var info_prolog: Tuple3[Option[String], Option[String], Option[Boolean]] = + Tuple3(None, None, None); + if ('<' != ch) { + reportSyntaxError("< expected"); + return null; + } + + nextch; // is prolog ? + var children: NodeSeq = null; + if ('?' == ch) { + //Console.println("[MarkupParser::document] starts with xml declaration"); + nextch; + info_prolog = prolog(); + children = content(TopScope); // DTD handled as side effect + + } else { + //Console.println("[MarkupParser::document] does not start with xml declaration"); + val ts = new NodeBuffer(); + content1(TopScope, ts); // DTD handled as side effect + ts &+ content(TopScope); + children = NodeSeq.fromSeq(ts); + } + //Console.println("[MarkupParser::document] children now: "+children.toList); + var elemCount = 0; + var theNode: Node = null; + for (val c <- children) c match { + case _:ProcInstr => ; + case _:Comment => ; + case _:EntityRef => // todo: fix entities, shouldn't be "special" + reportSyntaxError("no entity references alllowed here"); + case s:SpecialNode => + if(s.toString().trim().length() > 0) //non-empty text nodes not allowed + elemCount = elemCount + 2; + case m:Node => + elemCount = elemCount + 1; + theNode = m; + } + if (1 != elemCount) { + reportSyntaxError("document must contain exactly one element"); + Console.println(children.toList); + } + + val doc = new Document(); + doc.children = children; + doc.docElem = theNode; + doc.version = info_prolog._1; + doc.encoding = info_prolog._2; + doc.standAlone = info_prolog._3; + doc.dtd = this.dtd; + return doc + } + + /** append Unicode character to name buffer*/ + protected def putChar(c: Char) = cbuf.append(c); + + //var xEmbeddedBlock = false; + + /** this method assign the next character to ch and advances in input */ + def nextch: Unit = { + if (curInput.hasNext) { + ch = curInput.next; + pos = curInput.pos; + } else { + val ilen = inpStack.length; + //Console.println(" ilen = "+ilen+ " extIndex = "+extIndex); + if ((ilen != extIndex) && (ilen > 0)) { + /** for external source, inpStack == Nil ! need notify of eof! */ + pop(); + } else { + eof = true; + ch = 0.asInstanceOf[Char]; + //throw new Exception("this is the end") + } + } + } + + //final val enableEmbeddedExpressions: Boolean = false; + + /** munch expected XML token, report syntax error for unexpected + */ + def xToken(that: Char): Unit = { + if (ch == that) + nextch; + else { + reportSyntaxError("'" + that + "' expected instead of '" + ch + "'"); + error("FATAL"); + } + } + + def xToken(that: Seq[Char]): Unit = { + val it = that.elements; + while (it.hasNext) + xToken(it.next); + } + + /** checks whether next character starts a Scala block, if yes, skip it. + * @return true if next character starts a scala block + def xCheckEmbeddedBlock:Boolean = { + xEmbeddedBlock = + enableEmbeddedExpressions && (ch == '{') && { nextch; ch != '{' }; + return xEmbeddedBlock; + } + */ + + /** parse attribute and create namespace scope, metadata + * [41] Attributes ::= { S Name Eq AttValue } + */ + def xAttributes(pscope:NamespaceBinding): Pair[MetaData,NamespaceBinding] = { + var scope: NamespaceBinding = pscope; + var aMap: MetaData = Null; + while (isNameStart(ch)) { + val pos = this.pos; + + val qname = xName; + val _ = xEQ; + val value = xAttributeValue(); + + Utility.prefix(qname) match { + case Some("xmlns") => + val prefix = qname.substring(6 /*xmlns:*/ , qname.length()); + scope = new NamespaceBinding(prefix, value, scope); + + case Some(prefix) => + val key = qname.substring(prefix.length()+1, qname.length()); + aMap = new PrefixedAttribute(prefix, key, value, aMap); + + case _ => + if( qname == "xmlns" ) + scope = new NamespaceBinding(null, value, scope); + else + aMap = new UnprefixedAttribute(qname, value, aMap); + } + + if ((ch != '/') && (ch != '>') && ('?' != ch)) + xSpace; + } + + if(!aMap.wellformed(scope)) + reportSyntaxError( "double attribute"); + + Pair(aMap,scope) + } + + /** attribute value, terminated by either ' or ". value may not contain <. + * AttValue ::= `'` { _ } `'` + * | `"` { _ } `"` + */ + def xAttributeValue(): String = { + val endch = ch; + nextch; + while (ch != endch) { + if ('<' == ch) + reportSyntaxError( "'<' not allowed in attrib value" ); + putChar(ch); + nextch; + } + nextch; + val str = cbuf.toString(); + cbuf.setLength(0); + + // well-formedness constraint + normalizeAttributeValue(str) + + } + + /** entity value, terminated by either ' or ". value may not contain <. + * AttValue ::= `'` { _ } `'` + * | `"` { _ } `"` + */ + def xEntityValue(): String = { + val endch = ch; + nextch; + while (ch != endch) { + putChar(ch); + nextch; + } + nextch; + val str = cbuf.toString(); + cbuf.setLength(0); + str + } + + + /** parse a start or empty tag. + * [40] STag ::= '<' Name { S Attribute } [S] + * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] + */ + protected def xTag(pscope:NamespaceBinding): Tuple3[String, MetaData, NamespaceBinding] = { + val qname = xName; + + xSpaceOpt; + val Pair(aMap: MetaData, scope: NamespaceBinding) = { + if (isNameStart(ch)) + xAttributes(pscope) + else + Pair(Null, pscope) + } + Triple(qname, aMap, scope); + } + + /** [42] '<' xmlEndTag ::= '<' '/' Name S? '>' + */ + def xEndTag(n: String) = { + xToken('/'); + val m = xName; + if (n != m) + reportSyntaxError("expected closing tag of " + n/* +", not "+m*/); + xSpaceOpt; + xToken('>') + } + + /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' + * + * see [15] + */ + def xCharData: NodeSeq = { + xToken("[CDATA["); + val pos1 = pos; + val sb: StringBuffer = new StringBuffer(); + while (true) { + if (ch==']' && + { sb.append(ch); nextch; ch == ']' } && + { sb.append(ch); nextch; ch == '>' } ) { + sb.setLength(sb.length() - 2); + nextch; + return handle.text( pos1, sb.toString() ); + } else sb.append( ch ); + nextch; + } + throw FatalError("this cannot happen"); + }; + + /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" + * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";" + * + * see [66] + */ + def xCharRef(ch: () => Char, nextch: () => Unit): String = { + val hex = (ch() == 'x') && { nextch(); true }; + val base = if (hex) 16 else 10; + var i = 0; + while (ch() != ';') { + ch() match { + case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => + i = i * base + Character.digit( ch(), base ); + case 'a' | 'b' | 'c' | 'd' | 'e' | 'f' + | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => + if (! hex) + reportSyntaxError("hex char not allowed in decimal char ref\n" + +"Did you mean to write &#x ?"); + else + i = i * base + Character.digit(ch(), base); + case _ => + reportSyntaxError("character '" + ch() + " not allowed in char ref\n"); + } + nextch(); + } + new String(Predef.Array(i.asInstanceOf[char])) + } + + + /** Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' + * + * see [15] + */ + def xComment: NodeSeq = { + val sb: StringBuffer = new StringBuffer(); + xToken('-'); + xToken('-'); + while (true) { + if (ch == '-' && { sb.append(ch); nextch; ch == '-' }) { + sb.setLength(sb.length() - 1); + nextch; + xToken('>'); + return handle.comment(pos, sb.toString()); + } else sb.append(ch); + nextch; + } + throw FatalError("this cannot happen"); + }; + + /* todo: move this into the NodeBuffer class */ + def appendText(pos: Int, ts: NodeBuffer, txt: String): Unit = { + if (preserveWS) + ts &+ handle.text(pos, txt); + else + for (val t <- TextBuffer.fromString(txt).toText) { + ts &+ handle.text(pos, t.text); + } + } + + /** '<' content1 ::= ... */ + def content1(pscope: NamespaceBinding, ts: NodeBuffer): Unit = { + ch match { + case '!' => + nextch; + if ('[' == ch) // CDATA + ts &+ xCharData; + else if ('D' == ch) // doctypedecl, parse DTD // @todo REMOVE HACK + parseDTD(); + else // comment + ts &+ xComment; + case '?' => // PI + nextch; + ts &+ xProcInstr; + case _ => + ts &+ element1(pscope); // child + } + } + + /** content1 ::= '<' content1 | '&' charref ... */ + def content(pscope: NamespaceBinding): NodeSeq = { + var ts = new NodeBuffer; + var exit = eof; + while (! exit) { + //Console.println("in content, ch = '"+ch+"' line="+scala.io.Position.line(pos)); + /* if( xEmbeddedBlock ) { + ts.append( xEmbeddedExpr ); + } else {*/ + tmppos = pos; + exit = eof; + if(!eof) + ch match { + case '<' => // another tag + //Console.println("before ch = '"+ch+"' line="+scala.io.Position.line(pos)+" pos="+pos); + nextch; + //Console.println("after ch = '"+ch+"' line="+scala.io.Position.line(pos)+" pos="+pos); + + if('/' ==ch) + exit = true; // end tag + else + content1(pscope, ts) + //case '{' => +/* if( xCheckEmbeddedBlock ) { + ts.appendAll(xEmbeddedExpr); + } else {*/ + // val str = new StringBuffer("{"); + // str.append(xText); + // appendText(tmppos, ts, str.toString()); + /*}*/ + // postcond: xEmbeddedBlock == false! + case '&' => // EntityRef or CharRef + nextch; + ch match { + case '#' => // CharacterRef + nextch; + val theChar = handle.text( tmppos, + xCharRef ({ ()=> ch },{ () => nextch }) ); + xToken(';'); + ts &+ theChar ; + case _ => // EntityRef + val n = xName ; + xToken(';'); + n match { + case "lt" => ts &+ '<'; + case "gt" => ts &+ '>'; + case "amp" => ts &+ '&'; + case "quote" => ts &+ '"'; + case _ => + /* + ts + handle.entityRef( tmppos, n ) ; + */ + push( n ); + } + } + case _ => // text content + //Console.println("text content?? pos = "+pos); + appendText(tmppos, ts, xText); + // here xEmbeddedBlock might be true + } + /*}*/ + } + val list = ts.toList; + // 2do: optimize seq repr. + new NodeSeq { + val theSeq = list; + } + } // content(NamespaceBinding) + + /** externalID ::= SYSTEM S syslit + * PUBLIC S pubid S syslit + */ + + def externalID(): ExternalID = ch match { + case 'S' => + nextch; + xToken("YSTEM"); + xSpace; + val sysID = systemLiteral(); + new SystemID(sysID); + case 'P' => + nextch; xToken("UBLIC"); + xSpace; + val pubID = pubidLiteral(); + xSpace; + val sysID = systemLiteral(); + new PublicID(pubID, sysID); + } + + + /** parses document type declaration and assigns it to instance variable + * dtd. + * + * <! parseDTD ::= DOCTYPE name ... > + */ + def parseDTD(): Unit = { // dirty but fast + //Console.println("(DEBUG) parseDTD"); + var extID: ExternalID = null; + if (this.dtd != null) + reportSyntaxError("unexpected character (DOCTYPE already defined"); + xToken("DOCTYPE"); + xSpace; + val n = xName; + xSpace; + //external ID + if('S' == ch || 'P' == ch) { + extID = externalID(); + xSpaceOpt; + } + + /* parse external subset of DTD + */ + + if((null != extID)&&(isValidating)) { + + pushExternal(extID.systemId); + //val extSubsetSrc = externalSource( extID.systemId ); + + extIndex = inpStack.length; + /* + .indexOf(':') != -1) { // assume URI + Source.fromFile(new java.net.URI(extID.systemLiteral)); + } else { + Source.fromFile(extID.systemLiteral); + } + */ + //Console.println("I'll print it now"); + //val old = curInput; + //tmppos = curInput.pos; + //val oldch = ch; + //curInput = extSubsetSrc; + //pos = 0; + //nextch; + + extSubset(); + + pop(); + + extIndex = -1; + + //curInput = old; + //pos = curInput.pos; + //ch = curInput.ch; + //eof = false; + //while(extSubsetSrc.hasNext) + //Console.print(extSubsetSrc.next); + + //Console.println("returned from external, current ch = "+ch ) + } + + if ('[' == ch) { // internal subset + nextch; + /* TODO */ + //Console.println("hello"); + intSubset(); + //while(']' != ch) + // nextch; + // TODO: do the DTD parsing?? ?!?!?!?!! + xToken(']'); + xSpaceOpt; + } + xToken('>'); + this.dtd = new DTD { + override var externalID = extID; + override val decls = handle.decls.reverse; + } + //this.dtd.initializeEntities(); + handle.endDTD(n); + } + + def element(pscope: NamespaceBinding): NodeSeq = { + xToken('<'); + element1(pscope); + } + + /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' + */ + def element1(pscope: NamespaceBinding): NodeSeq = { + val pos = this.pos; + val Tuple3(qname, aMap, scope) = xTag(pscope); + val Tuple2(pre, local) = Utility.prefix(qname) match { + case Some(p) => Pair(p,qname.substring(p.length()+1, qname.length())); + case _ => Pair(null,qname); + } + val ts = { + if (ch == '/') { // empty element + xToken('/'); + xToken('>'); + handle.elemStart(pos, pre, local, aMap, scope); + NodeSeq.Empty; + } + else { // element with content + xToken('>'); + handle.elemStart(pos, pre, local, aMap, scope); + val tmp = content(scope); + xEndTag(qname); + tmp; + } + } + val res = handle.elem(pos, pre, local, aMap, scope, ts ); + handle.elemEnd(pos, pre, local); + res + } + + //def xEmbeddedExpr: MarkupType; + + /** Name ::= (Letter | '_' | ':') (NameChar)* + * + * see [5] of XML 1.0 specification + */ + def xName: String = { + if (isNameStart(ch)) { + while (isNameChar(ch)) { + putChar(ch); + nextch; + } + val n = cbuf.toString().intern(); + cbuf.setLength(0); + n + } else { + reportSyntaxError("name expected"); + new String() + } + } + + /** scan [S] '=' [S]*/ + def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt } + + /** skip optional space S? */ + def xSpaceOpt = while (isSpace(ch) && !eof) { nextch; }; + + /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ + def xSpace = { + if (isSpace(ch)) { + nextch; xSpaceOpt + } + else { + reportSyntaxError("whitespace expected"); + } + } + + /** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>' + * + * see [15] + */ + def xProcInstr: NodeSeq = { + val sb:StringBuffer = new StringBuffer(); + val n = xName; + if (isSpace(ch)) { + xSpace; + while (true) { + if (ch == '?' && { sb.append( ch ); nextch; ch == '>' }) { + sb.setLength(sb.length() - 1); + nextch; + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } else + sb.append(ch); + nextch + } + }; + xToken('?'); + xToken('>'); + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } + + /** parse character data. + * precondition: xEmbeddedBlock == false (we are not in a scala block) + */ + def xText: String = { + //if( xEmbeddedBlock ) throw FatalError("internal error: encountered embedded block"); // assert + + /*if( xCheckEmbeddedBlock ) + return "" + else {*/ + //Console.println("in xText! ch = '"+ch+"'"); + var exit = false; + while (! exit) { + //Console.println("LOOP in xText! ch = '"+ch+"' + pos="+pos); + putChar(ch); + val opos = pos; + nextch; + + //Console.println("STILL LOOP in xText! ch = '"+ch+"' + pos="+pos+" opos="+opos); + + + exit = eof || /*{ nextch; xCheckEmbeddedBlock }||*/( ch == '<' ) || ( ch == '&' ); + } + val str = cbuf.toString(); + cbuf.setLength(0); + str + /*}*/ + } + + /** attribute value, terminated by either ' or ". value may not contain <. + * AttValue ::= `'` { _ } `'` + * | `"` { _ } `"` + */ + def systemLiteral(): String = { + val endch = ch; + if (ch != '\'' && ch != '"') + reportSyntaxError("quote ' or \" expected"); + nextch; + while (ch != endch) { + putChar(ch); + nextch; + } + nextch; + val str = cbuf.toString(); + cbuf.setLength(0); + str + } + + + /* [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'" */ + def pubidLiteral(): String = { + val endch = ch; + if (ch!='\'' && ch != '"') + reportSyntaxError("quote ' or \" expected"); + nextch; + while (ch != endch) { + putChar(ch); + //Console.println("hello '"+ch+"'"+isPubIDChar(ch)); + if(!isPubIDChar(ch)) + reportSyntaxError("char '"+ch+"' is not allowed in public id"); + nextch; + } + nextch; + val str = cbuf.toString(); + cbuf.setLength(0); + str + } + + // + // dtd parsing + // + + def extSubset(): Unit = { + var textdecl:Tuple2[Option[String],Option[String]] = null; + if(ch=='<') { + nextch; + if(ch=='?') { + nextch; + textdecl = textDecl() + } else + markupDecl1(); + } + while(!eof) { + markupDecl(); + } + } + + def markupDecl1() = { + def doInclude() = { + xToken('['); while(']' != ch) markupDecl(); nextch // ']' + } + def doIgnore() = { + xToken('['); while(']' != ch) nextch; nextch; // ']' + } + if('?' == ch) { + nextch; + xProcInstr; // simply ignore processing instructions! + } else { + xToken('!'); + ch match { + case '-' => + xComment ; // ignore comments + + case 'E' => + nextch; + if ('L' == ch) { + nextch; + elementDecl() + } else + entityDecl(); + + case 'A' => + nextch; + attrDecl(); + + case 'N' => + nextch; + notationDecl(); + + case '[' if inpStack.length >= extIndex => + nextch; + xSpaceOpt; + ch match { + case '%' => + nextch; + val ent = xName; + xToken(';'); + xSpaceOpt; + /* + Console.println("hello, pushing!"); + { + val test = replacementText(ent); + while(test.hasNext) + Console.print(test.next); + } */ + push(ent); + xSpaceOpt; + //Console.println("hello, getting name"); + val stmt = xName; + //Console.println("hello, got name"); + xSpaceOpt; + //Console.println("how can we be eof = "+eof); + + // eof = true because not external?! + //if(!eof) + // error("expected only INCLUDE or IGNORE"); + + //pop(); + + + //Console.println("hello, popped"); + stmt match { + // parameter entity + case "INCLUDE" => + doInclude(); + case "IGNORE" => + doIgnore() + } + case 'I' => + nextch; + ch match { + case 'G' => + nextch; + xToken("NORE"); + xSpaceOpt; + doIgnore() + case 'N' => + nextch; + xToken("NCLUDE"); + doInclude() + } + } + xToken(']'); + xToken('>'); + + case _ => + curInput.reportError(pos, "unexpected character '"+ch+"', expected some markupdecl"); + while(ch!='>') + nextch; + + } + } + } + + def markupDecl(): Unit = ch match { + case '%' => // parameter entity reference + nextch; + val ent = xName; + xToken(';'); + if(!isValidating) + handle.peReference(ent); // n-v: just create PE-reference + else + push(ent); // v: parse replacementText + + //peReference + case '<' => + nextch; + markupDecl1(); + + case _ if isSpace(ch) => + xSpace; + case _ => + reportSyntaxError("markupdecl: unexpected character '"+ch+"' #" + ch.asInstanceOf[Int]); + nextch; + } + + /** "rec-xml/#ExtSubset" pe references may not occur within markup + declarations + */ + def intSubset(): Unit = { + //Console.println("(DEBUG) intSubset()"); + xSpace; + while (']' != ch) { + markupDecl() + } + } + + /** <! element := ELEMENT + */ + def elementDecl(): Unit = { + xToken("EMENT"); + xSpace; + val n = xName; + xSpace; + while ('>' != ch) { + //Console.println("["+ch+"]"); + putChar(ch); + nextch; + } + //Console.println("END["+ch+"]"); + nextch; + val cmstr = cbuf.toString(); + cbuf.setLength(0); + handle.elemDecl(n, cmstr); + } + + /** <! attlist := ATTLIST + */ + def attrDecl() = { + xToken("TTLIST"); + xSpace; + val n = xName; + xSpace; + var attList: List[AttrDecl] = Nil; + // later: find the elemDecl for n + while ('>' != ch) { + val aname = xName; + //Console.println("attribute name: "+aname); + var defdecl: DefaultDecl = null; + xSpace; + // could be enumeration (foo,bar) parse this later :-/ + while ('"' != ch && '\'' != ch && '#' != ch && '<' != ch) { + if(!isSpace(ch)) + cbuf.append(ch); + nextch; + } + val atpe = cbuf.toString(); + cbuf.setLength(0); + //Console.println("attr type: "+atpe); + ch match { + case '\'' | '"' => + val defValue = xAttributeValue(); // default value + defdecl = DEFAULT(false, defValue); + + case '#' => + nextch; + xName match { + case "FIXED" => + xSpace; + val defValue = xAttributeValue(); // default value + defdecl = DEFAULT(true, defValue); + case "IMPLIED" => + defdecl = IMPLIED + case "REQUIRED" => + defdecl = REQUIRED + } + case _ => + } + xSpaceOpt; + + attList = AttrDecl(aname, atpe, defdecl) :: attList; + cbuf.setLength(0); + } + nextch; + handle.attListDecl(n, attList.reverse); + } + + /** <! element := ELEMENT + */ + def entityDecl() = { + //Console.println("entityDecl()"); + var isParameterEntity = false; + var entdef: EntityDef = null; + xToken("NTITY"); + xSpace; + if ('%' == ch) { + nextch; + isParameterEntity = true; + xSpace; + } + val n = xName; + xSpace; + ch match { + case 'S' | 'P' => //sy + val extID = externalID(); + if(isParameterEntity) { + + xSpaceOpt; + xToken('>'); + handle.parameterEntityDecl(n, ExtDef(extID)) + + } else { // notation? + + xSpace; + if ('>' != ch) { + xToken("NDATA"); + xSpace; + val notat = xName; + xSpaceOpt; + xToken('>'); + handle.unparsedEntityDecl(n, extID, notat); + } else { + nextch; + handle.parsedEntityDecl(n, ExtDef(extID)); + } + } + + case '"' | '\'' => + val av = xEntityValue(); + xSpaceOpt; + xToken('>'); + if (isParameterEntity) + handle.parameterEntityDecl(n, IntDef(av)); + else + handle.parsedEntityDecl(n, IntDef(av)); + } + + {} + } // entityDecl + + /** 'N' notationDecl ::= "OTATION" + */ + def notationDecl(): Unit = { + xToken("OTATION"); + xSpace; + val notat = xName; + xSpace; + val extID = if (ch == 'S') { + externalID(); + } + else if (ch == 'P') { + /** PublicID (without system, only used in NOTATION) */ + nextch; + xToken("UBLIC"); + xSpace; + val pubID = pubidLiteral(); + xSpaceOpt; + val sysID = if (ch != '>') + systemLiteral() + else + null; + new PublicID(pubID, sysID); + } else + error("PUBLIC or SYSTEM expected"); + xSpaceOpt; + xToken('>'); + handle.notationDecl(notat, extID) + } + + /** + * report a syntax error + */ + def reportSyntaxError(pos: int, str: String): Unit = { + curInput.reportError(pos, str); + //error("MarkupParser::synerr"); // DEBUG + } + + def reportSyntaxError(str: String): Unit = reportSyntaxError(pos, str); + + /** + * report a syntax error + */ + def reportValidationError(pos: int, str: String): Unit = { + curInput.reportError(pos, str) + } + + + def push(entityName:String) = { + //Console.println("BEFORE PUSHING "+ch); + //Console.println("BEFORE PUSHING "+pos); + //Console.print("[PUSHING "+entityName+"]"); + if(!eof) + inpStack = curInput :: inpStack; + + curInput = replacementText(entityName); + nextch; + } + + /* + def push(src:Source) = { + curInput = src; + nextch; + } + */ + + def pushExternal(systemId:String) = { + //Console.print("BEFORE PUSH, curInput = $"+curInput.descr); + //Console.println(" stack = "+inpStack.map { x => "$"+x.descr }); + + //Console.print("[PUSHING EXTERNAL "+systemId+"]"); + if(!eof) + inpStack = curInput :: inpStack; + + curInput = externalSource(systemId); + + //Console.print("AFTER PUSH, curInput = $"+curInput.descr); + //Console.println(" stack = "+inpStack.map { x => "$"+x.descr }); + + nextch; + } + def pop() = { + + curInput = inpStack.head; + inpStack = inpStack.tail; + ch = curInput.ch; + pos = curInput.pos; + eof = false; // must be false, because of places where entity refs occur + //Console.println("\n AFTER POP, curInput = $"+curInput.descr); + //Console.println(inpStack.map { x => x.descr }); + } + + /** for the moment, replace only character references + * see spec 3.3.3 + * precond: cbuf empty + */ + def normalizeAttributeValue(attval: String) = { + val s:Seq[Char] = attval; + val it = s.elements; + while(it.hasNext) { + it.next match { + case ' '|'\t'|'\n'|'\r' => + cbuf.append(' '); + case '&' => it.next match { + case '#' => + var c = it.next; + val s = xCharRef ({ () => c }, { () => c = it.next }); + cbuf.append(s); + case nchar => + val nbuf = new StringBuffer(); + var d = nchar; + do { + nbuf.append(d); + d = it.next; + } while(d != ';'); + nbuf.toString() match { + case "lt" => cbuf.append('<'); + case "gt" => cbuf.append('>'); + case "amp" => cbuf.append('&'); + case "quote" => cbuf.append('"'); + case name => + //don't handle entityrefs for now + cbuf.append('&'); + cbuf.append(name); + cbuf.append(';'); + } + } + case c => cbuf.append(c); + } + } + val name = cbuf.toString(); + cbuf.setLength(0); + name + } + +} diff --git a/src/library/scala/xml/parsing/NoBindingFactoryAdapter.scala b/src/library/scala/xml/parsing/NoBindingFactoryAdapter.scala new file mode 100644 index 0000000000..d2400e33d3 --- /dev/null +++ b/src/library/scala/xml/parsing/NoBindingFactoryAdapter.scala @@ -0,0 +1,53 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ +package scala.xml.parsing; + +import scala.xml.factory.NodeFactory ; +import org.xml.sax.InputSource; + +/** nobinding adaptor providing callbacks to parser to create elements. +* implements hash-consing +*/ +class NoBindingFactoryAdapter extends FactoryAdapter with NodeFactory[Elem] { + + // -- FactoryAdapter methods + + /** returns true. Every XML node may contain text that the application needs + **/ + def nodeContainsText( label:java.lang.String ):boolean = true; + + + // methods for NodeFactory[Elem] + + /** constructs an instance of scala.xml.Elem */ + protected def create(pre: String, label: String, attrs: MetaData, scpe: NamespaceBinding, children:Seq[Node]): Elem = { + Elem( pre, label, attrs, scpe, children:_* ); + } + + // -- methods for FactoryAdapter + + /** creates a node. never creates the same node twice, using hash-consing + */ + def createNode(pre:String, label: String, attrs: MetaData, scpe: NamespaceBinding, children: List[Node] ): Elem = { + //Console.println("NoBindingFactoryAdapter::createNode("+pre+","+label+","+attrs+","+scpe+","+children+")"); + Elem( pre, label, attrs, scpe, children:_* ); + //makeNode(pre, label, attrs, scpe, children); + } + + /** creates a text node + */ + def createText( text:String ) = + Text( text ); + + /** loads an XML document, returning a Symbol node. + */ + override def loadXML( source:InputSource ):Elem = + super.loadXML( source ).asInstanceOf[ Elem ]; + +} diff --git a/src/library/scala/xml/parsing/TokenTests.scala b/src/library/scala/xml/parsing/TokenTests.scala new file mode 100644 index 0000000000..86560fc12f --- /dev/null +++ b/src/library/scala/xml/parsing/TokenTests.scala @@ -0,0 +1,145 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml.parsing; + +/** + * Helper functions for parsing XML fragments + */ +trait TokenTests { + + /** (#x20 | #x9 | #xD | #xA) */ + final def isSpace( ch:Char ):Boolean = ch match { + case '\u0009' | '\u000A' | '\u000D' | '\u0020' => true + case _ => false; + } + + /** (#x20 | #x9 | #xD | #xA)+ */ + final def isSpace(cs: Seq[Char]): Boolean = { + val it = cs.elements; + it.hasNext && it.forall { isSpace }; + } + + /** NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' + * | CombiningChar | Extender + * + * see [4] and Appendix B of XML 1.0 specification + */ + def isNameChar(ch: Char) = isNameStart(ch) || (ch match { + case '.' | '-' | ':' => true; + case _ => java.lang.Character.getType( ch ).asInstanceOf[Byte] match { + case java.lang.Character.COMBINING_SPACING_MARK => true; // Mc + case java.lang.Character.ENCLOSING_MARK => true; // Me + case java.lang.Character.NON_SPACING_MARK => true; // Mn + case java.lang.Character.MODIFIER_LETTER => true; // Lm + case java.lang.Character.DECIMAL_DIGIT_NUMBER => true; // Nd + case _ => false; + } + }); + + /** NameStart ::= ( Letter | '_' ) + * where Letter means in one of the Unicode general + * categories { Ll, Lu, Lo, Lt, Nl } + * + * We do not allow a name to start with ':'. + * see [3] and Appendix B of XML 1.0 specification + */ + def isNameStart(ch: Char) = + java.lang.Character.getType(ch).asInstanceOf[Byte] match { + case java.lang.Character.LOWERCASE_LETTER => true; + case java.lang.Character.UPPERCASE_LETTER => true; + case java.lang.Character.OTHER_LETTER => true; + case java.lang.Character.TITLECASE_LETTER => true; + case java.lang.Character.LETTER_NUMBER => true; + case _ => ch match { + case '_' => true + case _ => false; + } + } + + /** Name ::= ( Letter | '_' ) (NameChar)* + * + * see [5] of XML 1.0 specification + */ + def isName(s: String): boolean = { + if( s.length() > 0 ) { + val z:Seq[Char] = s; + val y = z.elements; + if (isNameStart(y.next)) { + while (y.hasNext && isNameChar(y.next)) {}; + !y.hasNext + } else false; + } else false; + } + + def isPubIDChar(ch: Char): boolean = { + //Console.println("char: '" + ch + "'"); + ch match { + case '\u0020' | '\u000D' | '\u000A' => true; + case _ if + (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || + ('A' <= ch && ch <= 'Z')) => true; + case '-' | '\''| '(' | ')' | '+' | ',' | '.' | + '/' | ':' | '=' | '?' | ';' | '!' | '*' | + '#' | '@' | '$' | '_' | '%' => true + case _ => + //Console.println("false: '" + ch + "'"); + false; + } + } + + /** + * Returns true if the encoding name is a valid IANA encoding. + * This method does not verify that there is a decoder available + * for this encoding, only that the characters are valid for an + * IANA encoding name. + * + * @param ianaEncoding The IANA encoding name. + */ + def isValidIANAEncoding(ianaEncoding: Seq[Char]): Boolean = { + val it = ianaEncoding.elements; + if (!it.hasNext) + return false; + + var c = it.next; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + while (it.hasNext) { + c = it.next; + if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && + (c < '0' || c > '9') && c != '.' && c != '_' && + c != '-') { + return false; + } + } + return true; + } else + return false; + } // isValidIANAEncoding(String): Boolean + + def checkSysID( s:String ):boolean = { + s.indexOf('"') == -1 || s.indexOf('\'') == -1 + } + + def checkPubID(s: String): Boolean = { + //Console.println("checkPubID of \""+s+"\""); + if (s.length() > 0) { + val z: Seq[Char] = s; + val y = z.elements; + var c = ' '; + while (y.hasNext && isPubIDChar(c)) { + //Console.println(c); + c = y.next + }; + !y.hasNext + } + else + true + } + +} diff --git a/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala b/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala new file mode 100644 index 0000000000..717ed52d79 --- /dev/null +++ b/src/library/scala/xml/parsing/ValidatingMarkupHandler.scala @@ -0,0 +1,104 @@ +package scala.xml.parsing ; + +import scala.xml.dtd._ ; +import scala.util.logging.Logged ; + +abstract class ValidatingMarkupHandler extends MarkupHandler with Logged { + + var rootLabel:String = _; + var qStack: List[Int] = Nil; + var qCurrent: Int = -1; + + var declStack: List[ElemDecl] = Nil; + var declCurrent: ElemDecl = null; + + final override val isValidating = true; + + override def log(msg:String) = {}; + + /* + override def checkChildren(pos:int, pre: String, label:String,ns:NodeSeq): Unit = { + Console.println("checkChildren()"); + val decl = lookupElemDecl(label); + // @todo: nice error message + val res = decl.contentModel.validate(ns); + Console.println("res = "+res); + if(!res) + error("invalid!"); + } + */ + + override def endDTD(n:String) = { + rootLabel = n; + } + override def elemStart(pos: int, pre: String, label: String, attrs: MetaData, scope:NamespaceBinding): Unit = { + + def advanceDFA(dm:DFAContentModel) = { + val trans = dm.dfa.delta(qCurrent); + log("advanceDFA(dm): "+dm); + log("advanceDFA(trans): "+trans); + trans.get(ContentModel.ElemName(label)) match { + case Some(qNew) => qCurrent = qNew + case _ => reportValidationError(pos, "DTD says, wrong element, expected one of "+trans.keys.toString()); + } + } + // advance in current automaton + log("[qCurrent = "+qCurrent+" visiting "+label+"]"); + + if(qCurrent == -1) { // root + log(" checking root"); + if(label != rootLabel) + reportValidationError(pos, "this element should be "+rootLabel); + } else { + log(" checking node"); + declCurrent.contentModel match { + case ANY => + + case EMPTY => + reportValidationError(pos, "DTD says, no elems, no text allowed here"); + case PCDATA => + reportValidationError(pos, "DTD says, no elements allowed here"); + + case m@MIXED(r) => advanceDFA(m); + case e@ELEMENTS(r) => advanceDFA(e); + } + } + // push state, decl + qStack = qCurrent :: qStack; + declStack = declCurrent :: declStack; + + declCurrent = lookupElemDecl(label); + qCurrent = 0; + log(" done now"); + } + + override def elemEnd(pos: int, pre: String, label: String): Unit = { + log(" elemEnd"); + qCurrent = qStack.head; + qStack = qStack.tail; + declCurrent = declStack.head; + declStack = declStack.tail; + log(" qCurrent now"+qCurrent); + log(" declCurrent now"+declCurrent); + } + + final override def elemDecl(name: String, cmstr: String): Unit = + decls = ElemDecl( name, ContentModel.parse(cmstr)) :: decls; + + final override def attListDecl(name: String, attList: List[AttrDecl]): Unit = + decls = AttListDecl( name, attList) :: decls; + + final override def unparsedEntityDecl(name: String, extID: ExternalID, notat: String): Unit = { + decls = UnparsedEntityDecl( name, extID, notat) :: decls; + } + + final override def notationDecl(notat: String, extID: ExternalID): Unit = + decls = NotationDecl( notat, extID) :: decls; + + final override def peReference(name: String): Unit = + decls = PEReference( name ) :: decls; + + /** report a syntax error */ + def reportValidationError(pos: Int, str: String): Unit; + +} diff --git a/src/library/scala/xml/path/Expression.scala b/src/library/scala/xml/path/Expression.scala new file mode 100644 index 0000000000..cf6c6d552f --- /dev/null +++ b/src/library/scala/xml/path/Expression.scala @@ -0,0 +1,54 @@ +package scala.xml.path; + +object Expression { + + final def testFromString(x: String): Test = { + x.charAt(0) match { + case '*' if( x.length() == 1 ) => WildcardTest; + case _ => NameTest(x); + } + } + + case class FExp(e:Expr, c:Cond) { + def eval(n: Node): NodeSeq = new NodeSeq { val theSeq=Nil}; // @todo + } + + abstract class GenExp ; + case class Attrib(test: NameTest, e: Expr) extends GenExp; + + abstract class Expr extends GenExp { + def \ (x: String) = + if( x=="*") + Child(WildcardTest, this) + else + Child(NameTest(x), this); + + def \\ (x: String) = + if( x=="*") + DescOrSelf(WildcardTest, this) + else + DescOrSelf(NameTest(x), this); + + def apply(c: Cond) = FExp(this, c); + + def eval(n: Node): NodeSeq = new NodeSeq { val theSeq=Nil}; // @todo + } + + case object Root extends Expr; + + case class Child(test: Test, e: Expr) extends Expr; + case class DescOrSelf(test: Test, e: Expr) extends Expr; + + + abstract class Test; + + case object WildcardTest extends Test; // "x \ * " + case class NameTest(label: String) extends Test; // "x \ bar" + + + abstract class Cond; + + case class Exists(p: GenExp) extends Cond ; // "p [ p ]" + case class Equals(p: Expr, c:String) extends Cond ; // "p [ @p == bla ]" + +} diff --git a/src/library/scala/xml/transform/BasicTransformer.scala b/src/library/scala/xml/transform/BasicTransformer.scala new file mode 100644 index 0000000000..56bbc16201 --- /dev/null +++ b/src/library/scala/xml/transform/BasicTransformer.scala @@ -0,0 +1,124 @@ +package scala.xml.transform ; + +/** a trait for XML transformations */ +trait BasicTransformer extends Function1[Node,Node] { + + protected case class NeedsCopy(result:Seq[Node]) extends java.lang.Throwable; + + /** returns a new node buffer with the first pos elements from ns */ + protected def buffer(pos:Int, ns:Seq[Node]): NodeBuffer = { + val nb = new NodeBuffer(); + var jt = ns.elements; + var j = 0; while( j < pos-1 ) { + nb.append(jt.next); + j = j + 1; + } + nb + } + + /** turns a nodebuffer into a sequence, so hashcode works */ + protected def freeze(nb:NodeBuffer):Seq[Node] = { + val arr = new Array[Node](nb.length); + var i = 0; + val it = nb.elements; while( it.hasNext ) { + arr(i) = it.next; + i = i + 1; + } + val seq: Seq[Node] = arr; + seq + } + + protected def single(ns:Seq[Node]) = { + (1 == ns.length) + } + protected def unchanged(n:Node, ns:Seq[Node]) = { + single(ns) && (ns.elements.next.eq(n)) + } + + /** call transform(Node) for each node in ns, append results + * to NodeBuffer */ + def transform(it: Iterator[Node], nb:NodeBuffer): Seq[Node] = { + while( it.hasNext ) + nb ++ transform( it.next ); + freeze(nb); + } + + /** + * call transform(Node) to each node in ns, yield ns if nothing changes, + * otherwise a new sequence of concatenated results + */ + def transform(ns: Seq[Node]): Seq[Node] = { + var i = 0; + val it = ns.elements; + try { + while( it.hasNext ) { + val n = it.next; + val n2 = transform(n); + if(!unchanged(n,n2)) { + throw NeedsCopy(n2) + } + i = i + 1; + } + ns + } catch { + case NeedsCopy(n2) => + val nb = buffer(i, ns); + nb ++ n2; + transform(it, nb); + } + } + + def transform(n: Node): Seq[Node] = { + if (n.typeTag$ < 0) + n + else { + val ch = n.child; + val nch = transform(ch); + if(ch.eq(nch)) + n + else + Elem(n.prefix, n.label, n.attributes, n.scope, nch:_*) + } + } + + def apply(n: Node): Node = { + val seq = transform(n); + if( !single(seq) ) + error("transform must return single node for root"); + else seq.elements.next; + } +} + +/* +class IdentityTransformer extends BasicTransformer { + override def transform(n: Node): Seq[Node] = n.match { + case <world/> => <xml-world/> + case _ => super.transform(n); + } +} + +object Foo with Application { + + val tr = new IdentityTransformer; + val n = tr( <hello><world/></hello> ); + Console.println(n); + + val tr2 = new RewriteRule { + final override val name = "A rule"; + override def transform(n: Node) = n.match { + case <a/> => <b/><c/> + case _ => n + } + } + val tr3 = new RewriteRule { + final override val name = "E rule"; + override def transform(n: Node) = n.match { + case <e/> => <f><f/></f> + case _ => n + } + } + val tr4 = new RuleTransformer(tr2, tr3); + val m = tr4( <hello><a/><e/></hello> ); + Console.println(m); +} +*/ diff --git a/src/library/scala/xml/transform/RewriteRule.scala b/src/library/scala/xml/transform/RewriteRule.scala new file mode 100644 index 0000000000..c00d33aa96 --- /dev/null +++ b/src/library/scala/xml/transform/RewriteRule.scala @@ -0,0 +1,13 @@ +package scala.xml.transform ; + +/** a RewriteRule, when applied to a term, yields either + * the resulting of rewriting or the term itself it the rule + * is not applied + */ +abstract class RewriteRule extends BasicTransformer { + /** a name for this rewrite rule */ + val name = this.toString(); + override def transform(ns:Seq[Node]): Seq[Node] = super.transform(ns); + override def transform(n:Node): Seq[Node] = n; +} + diff --git a/src/library/scala/xml/transform/RuleTransformer.scala b/src/library/scala/xml/transform/RuleTransformer.scala new file mode 100644 index 0000000000..6a967105e7 --- /dev/null +++ b/src/library/scala/xml/transform/RuleTransformer.scala @@ -0,0 +1,14 @@ +package scala.xml.transform; + +class RuleTransformer(rules:RewriteRule*) extends BasicTransformer { + override def transform(n:Node): Seq[Node] = { + var m: Seq[Node] = super.transform(n); + val it = rules.elements; while(it.hasNext) { + val rule = it.next; + val m2 = rule.transform(m); + //if(!m2.eq(m)) Console.println("applied rule \""+rule.name+"\""); + m = m2; + } + m + } +} diff --git a/src/library/scala/xml/xsd/ContentModel.scala b/src/library/scala/xml/xsd/ContentModel.scala new file mode 100644 index 0000000000..11e513d26d --- /dev/null +++ b/src/library/scala/xml/xsd/ContentModel.scala @@ -0,0 +1,39 @@ +package scala.xml.xsd ; + +import scala.util.regexp.WordExp; +import scala.util.automata._; + +object ContentModel extends WordExp { + + type _labelT = ElemRef; + type _regexpT = RegExp; + + object Translator extends WordBerrySethi { + override val lang: ContentModel.this.type = ContentModel.this; + import lang._ ; + } + + case class ElemRef(name: String) extends Label { + override def toString() = name; + } + + def fromSchema(nodes:Seq[Node]): List[RegExp] = + nodes.foldLeft (Nil:List[RegExp]) { (list, n:Node) => fromSchema(n)::list }.reverse; + + def fromSchema(node:Node): RegExp = node.label match { + case "sequence" => Sequ(fromSchema(node.child):_*); + case "choice" => Alt(fromSchema(node.child):_*); + case "group" => Sequ(fromSchema(node.child):_*); + case "element" => + val name = node.attribute("name").toString(); + Letter(ElemRef(name)); // ouch, anonymous? references? + } +} + +sealed abstract class ContentModel ; + +case class ELEMENTS(r:ContentModel.RegExp) extends ContentModel ; + +case class MIXED(r:ContentModel.RegExp) extends ContentModel ; + +case object SimpleContent extends ContentModel ; diff --git a/src/library/scala/xml/xsd/Decl.scala b/src/library/scala/xml/xsd/Decl.scala new file mode 100644 index 0000000000..18a61f3b2b --- /dev/null +++ b/src/library/scala/xml/xsd/Decl.scala @@ -0,0 +1,26 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id$ + +package scala.xml.xsd ; + +abstract class Decl ; + +/** name - label of the element + * typeName - reference to a (possibly generated) type name + */ +case class ElemDecl(name: String, tpe: TypeSymbol) extends Decl; + +abstract class TypeDecl ; + +case class ComplexTypeDecl(name: String, derivedFrom: DerivSym, contentModel: ContentModel) extends TypeDecl; + +case class SimpleTypeDecl(name: String) extends TypeDecl;; + +abstract class xsdBuiltin(name: String) extends SimpleTypeDecl(name); diff --git a/src/library/scala/xml/xsd/XsTypeSymbol.scala b/src/library/scala/xml/xsd/XsTypeSymbol.scala new file mode 100644 index 0000000000..b553ad9f29 --- /dev/null +++ b/src/library/scala/xml/xsd/XsTypeSymbol.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2005, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $id: $ + +package scala.xml.xsd; + +trait XsTypeSymbol extends scala.xml.TypeSymbol ; + +object xsdAny extends XsTypeSymbol ; + +class SimpleTypeSymbol(val name: String) extends XsTypeSymbol { + var decl: SimpleTypeDecl = null; +} + +class ComplexTypeSymbol(val name: String) extends XsTypeSymbol { + var decl: ComplexTypeDecl = null; +} + +trait DerivSym; + +case class Extends(sym:XsTypeSymbol) extends DerivSym; + +case class Restricts(sym:XsTypeSymbol) extends DerivSym; + +object xsBoolean extends SimpleTypeSymbol("boolean") {} +object xsDouble extends SimpleTypeSymbol("double") {} +object xsFloat extends SimpleTypeSymbol("float") {} +object xsInt extends SimpleTypeSymbol("int") {} +object xsLong extends SimpleTypeSymbol("long") {} +object xsShort extends SimpleTypeSymbol("short") {} +object xsString extends SimpleTypeSymbol("string") {} +object xsDate extends SimpleTypeSymbol("date") {} |