//############################################################################
// Serialization
//############################################################################

object Serialize {
  @throws(classOf[java.io.IOException])
  def write[A](o: A): Array[Byte] = {
    val ba = new java.io.ByteArrayOutputStream(512)
    val out = new java.io.ObjectOutputStream(ba)
    out.writeObject(o)
    out.close()
    ba.toByteArray()
  }
  @throws(classOf[java.io.IOException])
  @throws(classOf[ClassNotFoundException])
  def read[A](buffer: Array[Byte]): A = {
    val in =
      new java.io.ObjectInputStream(new java.io.ByteArrayInputStream(buffer))
    in.readObject().asInstanceOf[A]
  }
  def check[A, B](x: A, y: B) {
    println("x = " + x)
    println("y = " + y)
    println("x equals y: " + (x equals y) + ", y equals x: " + (y equals x))
    println()
  }
}
import Serialize._

//############################################################################
// Test classes in package "scala"

object Test1_scala {

  private def arrayToString[A](arr: Array[A]): String =
    arr.mkString("Array[",",","]")

  private def arrayEquals[A, B](a1: Array[A], a2: Array[B]): Boolean =
    (a1.length == a2.length) &&
    (Iterator.range(0, a1.length) forall { i => a1(i) == a2(i) })

  @serializable
  object WeekDay extends Enumeration {
    type WeekDay = Value
    val Monday, Tuesday, Wednesday, Thusday, Friday, Saturday, Sunday = Value
  }
  import WeekDay._, BigDecimal._, RoundingMode._

  // in alphabetic order
  try {
    // Array
    val a1 = Array(1, 2, 3)
    val _a1: Array[Int] = read(write(a1))
    println("a1 = " + arrayToString(a1))
    println("_a1 = " + arrayToString(_a1))
    println("arrayEquals(a1, _a1): " + arrayEquals(a1, _a1))
    println()

    // Cell
    val c1 = new Cell('a')
    val _c1: Cell[Char] = read(write(c1))
    println("c1 = " + c1)
    println("_c1 = " + _c1)
    println("c1 eq _c1: " + (c1 eq _c1) + ", _c1 eq c1: " + (_c1 eq c1))
    println("c1 equals _c1: " + (c1 equals _c1) + ", _c1 equals c1: " + (_c1 equals c1))
    println()

    // Either
    val e1 = Left(1)
    val _e1: Either[Int, String] = read(write(e1))
    println("e1 = " + e1)
    println("_e1 = " + _e1)
    println("e1 eq _e1: " + (e1 eq _e1) + ", _e1 eq e1: " + (_e1 eq e1))
    println("e1 equals _e1: " + (e1 equals _e1) + ", _e1 equals e1: " + (_e1 equals e1))
    println()

    // Enumeration
    val x7 = BigDecimal.RoundingMode
    val y7: RoundingMode.type = read(write(x7))
    println("x7 = " + x7)
    println("y7 = " + y7)
    println("x7 eq y7: " + (x7 eq y7) + ", y7 eq x7: " + (y7 eq x7))
    println("x7 equals y7: " + (x7 equals y7) + ", y7 equals x7: " + (y7 equals x7))
    println()

    val x8 = WeekDay
    val y8: WeekDay.type = read(write(x8))
    println("x8 = " + x8)
    println("y8 = " + y8)
    println("x8 eq y8: " + (x8 eq y8) + ", y8 eq x8: " + (y8 eq x8))
    println("x8 equals y8: " + (x8 equals y8) + ", y8 equals x8: " + (y8 equals x8))
    println()

    val x9 = UP
    val y9: RoundingMode = read(write(x9))
    println("x9 = " + x9)
    println("y9 = " + y9)
    println("x9 eq y9: " + (x9 eq y9) + ", y9 eq x9: " + (y9 eq x9))
    println("x9 equals y9: " + (x9 equals y9) + ", y9 equals x9: " + (y9 equals x9))
    println()

    val x10 = Monday
    val y10: WeekDay = read(write(x10))
    println("x10 = " + x10)
    println("y10 = " + y10)
    println("x10 eq y10: " + (x10 eq y10) + ", y10 eq x10: " + (y10 eq x10))
    println("x10 equals y10: " + (x10 equals y10) + ", y10 equals x10: " + (y10 equals x10))
    println()

    println("x9 eq x10: " + (x9 eq x10) + ", x10 eq x9: " + (x10 eq x9))
    println("x9 equals x10: " + (x9 equals x10) + ", x10 equals x9: " + (x10 equals x9))
    println("x9 eq y10: " + (x9 eq y10) + ", y10 eq x9: " + (y10 eq x9))
    println("x9 equals y10: " + (x9 equals y10) + ", y10 equals x9: " + (y10 equals x9))
    println()

    // Function
    val f1 = { x: Int => 2 * x }
    val _f1: Function[Int, Int] = read(write(f1))
    println("f1 = <na>")
    println("_f1 = <na>")
    println("f1(2): " + f1(2) + ", _f1(2): " + _f1(2))
    println()

    // List
    val xs0 = List(1, 2, 3)
    val _xs0: List[Int] = read(write(xs0))
    println("xs0 = " + xs0)
    println("_xs0 = " + _xs0)
    println("xs0 eq _xs0: " + (xs0 eq _xs0) + ", _xs0 eq xs0: " + (_xs0 eq xs0))
    println("xs0 equals _xs0: " + (xs0 equals _xs0) + ", _xs0 equals xs0: " + (_xs0 equals xs0))
    println()

    val xs1 = Nil
    val _xs1: List[Nothing] = read(write(xs1))
    println("xs1 = " + xs1)
    println("_xs1 = " + _xs1)
    println("xs1 eq _xs1: " + (xs1 eq _xs1) + ", _xs1 eq xs1: " + (_xs1 eq xs1))
    println()

    // Option
    val o1 = None
    val _o1: Option[Nothing] = read(write(o1))
    println("o1 = " + o1)
    println("_o1 = " + _o1)
    println("o1 eq _o1: " + (o1 eq _o1) + ", _o1 eq o1: " + (_o1 eq o1))
    println()

    val o2 = Some(1)
    val _o2: Option[Int] = read(write(o2))
    println("o2 = " + o2)
    println("_o2 = " + _o2)
    println("o2 eq _o2: " + (o2 eq _o2) + ", _o2 eq o2: " + (_o2 eq o2))
    println("o2 equals _o2: " + (o2 equals _o2) + ", _o2 equals o2: " + (_o2 equals o2))
    println()
/*
    // Responder
    val r1 = Responder.constant("xyz")
    val _r1: Responder[String] = read(write(r1))
    check(r1, _r1)
*/
    // Symbol
    val s1 = 'hello
    val _s1: Symbol = read(write(s1))
    println("s1 = " + s1)
    println("_s1 = " + _s1)
    println("s1 eq _s1: " + (s1 eq _s1) + ", _s1 eq s1: " + (_s1 eq s1))
    println("s1 equals _s1: " + (s1 equals _s1) + ", _s1 equals s1: " + (_s1 equals s1))
    println()

    // Tuple
    val t1 = ("BannerLimit", 12345)
    val _t1: (String, Int) = read(write(t1))
    println("t1 = " + t1)
    println("_t1 = " + _t1)
    println("t1 eq _t1: " + (t1 eq _t1) + ", _t1 eq t1: " + (_t1 eq t1))
    println("t1 equals _t1: " + (t1 equals _t1) + ", _t1 equals t1: " + (_t1 equals t1))
    println()
  }
  catch {
    case e: Exception =>
      println("Error in Test1_scala: " + e)
      throw e
  }
}

//############################################################################
// Test classes in package "scala.collection.immutable"

@serializable
object Test2_immutable {
  import scala.collection.immutable.{
    BitSet, HashMap, HashSet, ListMap, ListSet, Queue, Range, SortedMap,
    SortedSet, Stack, Stream, TreeMap, TreeSet, Vector}

  // in alphabetic order
  try {
    // BitSet
    val bs1 = BitSet.empty + 1 + 2
    val _bs1: BitSet = read(write(bs1))
    check(bs1, _bs1)

    val bs2 = {
      val bs = new collection.mutable.BitSet()
      bs += 2; bs += 3
      bs.toImmutable
    }
    val _bs2: BitSet = read(write(bs2))
    check(bs2, _bs2)

    // HashMap
    val hm1 = new HashMap[Int, String] + (1 -> "A", 2 -> "B", 3 -> "C")
    val _hm1: HashMap[Int, String] = read(write(hm1))
    check(hm1, _hm1)

    // HashSet
    val hs1 = new HashSet[Int] + 1 + 2
    val _hs1: HashSet[Int] = read(write(hs1))
    check(hs1, _hs1)

    // List
    val xs1 = List(("buffers", 20), ("layers", 2), ("title", 3))
    val _xs1: List[(String, Int)] = read(write(xs1))
    check(xs1, _xs1)

    // ListMap
    val lm1 = new ListMap[String, Int] + ("buffers" -> 20, "layers" -> 2, "title" -> 3)
    val _lm1: ListMap[String, Int] = read(write(lm1))
    check(lm1, _lm1)

    // ListSet
    val ls1 = new ListSet[Int] + 3 + 5
    val _ls1: ListSet[Int] = read(write(ls1))
    check(ls1, _ls1)

    // Queue
    val q1 = Queue("a", "b", "c")
    val _q1: Queue[String] = read(write(q1))
    check(q1, _q1)

    // Range
    val r1 = 0 until 10
    val _r1: Range = read(write(r1))
    check(r1, _r1)

    val r2 = Range.Long(0L, 10L, 1)
    val _r2: r2.type = read(write(r2))
    check(r2, _r2)

    // SortedMap
    val sm1 = SortedMap.empty[Int, String] + (2 -> "B", 3 -> "C", 1 -> "A")
    val _sm1: SortedMap[Int, String] = read(write(sm1))
    check(sm1, _sm1)

    // SortedSet
    val ss1 = SortedSet.empty[Int] + 2 + 3 + 1
    val _ss1: SortedSet[Int] = read(write(ss1))
    check(ss1, _ss1)

    // Stack
    val s1 = new Stack().push("a", "b", "c")
    val _s1: Stack[String] = read(write(s1))
    check(s1, _s1)

    // Stream
    val st1 = Stream.range(0, 10)
    val _st1: Stream[Int] = read(write(st1))
    check(st1, _st1)

    // TreeMap
    val tm1 = new TreeMap[Int, String] + (42 -> "FortyTwo")
    val _tm1: TreeMap[Int, String] = read(write(tm1))
    check(tm1, _tm1)

    // TreeSet
    val ts1 = new TreeSet[Int]() + 2 + 0
    val _ts1: TreeSet[Int] = read(write(ts1))
    check(ts1, _ts1)

    // Vector
    val v1 = Vector('a, 'b, 'c)
    val _v1: Vector[Symbol] = read(write(v1))
    check(v1, _v1)
  }
  catch {
    case e: Exception =>
      println("Error in Test2_immutable: " + e)
      throw e
  }
}

//############################################################################
// Test classes in package "scala.collection.mutable"

object Test3_mutable {
  import scala.reflect.ClassManifest
  import scala.collection.mutable.{
    ArrayBuffer, ArrayBuilder, ArrayStack, BitSet, DoubleLinkedList,
    HashMap, HashSet, History, LinkedList, ListBuffer, Publisher, Queue,
    Stack, StringBuilder, WrappedArray}

  // in alphabetic order
  try {
    // ArrayBuffer
    val ab1 = new ArrayBuffer[String]
    ab1 ++= List("one", "two")
    val _ab1: ArrayBuffer[String] = read(write(ab1))
    check(ab1, _ab1)

    // ArrayBuilder
    val abu1 = ArrayBuilder.make[Long]
    val _abu1: ArrayBuilder[ClassManifest[Long]] = read(write(abu1))
    check(abu1, _abu1)

    val abu2 = ArrayBuilder.make[Float]
    val _abu2: ArrayBuilder[ClassManifest[Float]] = read(write(abu2))
    check(abu2, _abu2)

    // ArrayStack
    val as1 = new ArrayStack[Int]
    as1 ++= List(20, 2, 3).iterator
    val _as1: ArrayStack[Int] = read(write(as1))
    check(as1, _as1)

    // BitSet
    val bs1 = new BitSet()
    bs1 += 0
    bs1 += 8
    bs1 += 9
    val _bs1: BitSet = read(write(bs1))
    check(bs1, _bs1)
/*
    // DoubleLinkedList
    val dl1 = new DoubleLinkedList[Int](2, null)
    dl1.append(new DoubleLinkedList(3, null))
    val _dl1: DoubleLinkedList[Int] = read(write(dl1))
    check(dl1, _dl1)
*/
    // HashMap
    val hm1 = new HashMap[String, Int]
    hm1 ++= List(("A", 1), ("B", 2), ("C", 3)).iterator
    val _hm1: HashMap[String, Int] = read(write(hm1))
    check(hm1, _hm1)

    // HashSet
    val hs1 = new HashSet[String]
    hs1 ++= List("layers", "buffers", "title").iterator
    val _hs1: HashSet[String] = read(write(hs1))
    check(hs1, _hs1)

    // History
    @serializable
    class Feed extends Publisher[String]

    val h1 = new History[String, Int]
    val _h1: History[String, Int] = read(write(h1))
    check(h1, _h1)
/*
    // LinkedList
    val ll1 = new LinkedList[Int](2, null)
    ll1.append(new LinkedList(3, null))
    val _ll1: LinkedList[Int] = read(write(ll1))
    check(ll1, _ll1)
*/
    // ListBuffer
    val lb1 = new ListBuffer[String]
    lb1 ++= List("white", "black")
    val _lb1: ListBuffer[String] = read(write(lb1))
    check(lb1, _lb1)

    // Publisher

    // Queue
    val q1 = new Queue[Int]
    q1 ++= List(20, 2, 3).iterator
    val _q1: Queue[Int] = read(write(q1))
    check(q1, _q1)

    // Stack
    val s1 = new Stack[Int]
    s1 pushAll q1
    val _s1: Stack[Int] = read(write(s1))
    check(s1, _s1)

    // StringBuilder
    val sb1 = new StringBuilder
    sb1 append "abc"
    val _sb1: StringBuilder = read(write(sb1))
    check(sb1, _sb1)

    // WrappedArray
    val wa1 = WrappedArray.make(Array(1, 2, 3))
    val _wa1: WrappedArray[Int] = read(write(wa1))
    check(wa1, _wa1)
  }
  catch {
    case e: Exception =>
      println("Error in Test3_mutable: " + e)
      throw e
  }
}

//############################################################################
// Test classes in package "scala.xml"

object Test4_xml {
  import scala.xml.{Attribute, Document, Elem, Null, PrefixedAttribute, Text}

  case class Person(name: String, age: Int)

  try {
    // Attribute
    val a1 = new PrefixedAttribute("xml", "src", Text("hello"), Null)
    val _a1: Attribute = read(write(a1))
    check(a1, _a1)

    // Document
    val d1 = new Document
    d1.docElem = <title></title>
    d1.encoding = Some("UTF-8")
    val _d1: Document = read(write(d1))
    check(d1, _d1)

    // Elem
    val e1 = <html><title>title</title><body></body></html>;
    val _e1: Elem = read(write(e1))
    check(e1, _e1)

    class AddressBook(a: Person*) {
      private val people: List[Person] = a.toList
      def toXHTML =
      <table cellpadding="2" cellspacing="0">
        <tr>
          <th>Last Name</th>
          <th>First Name</th>
        </tr>
        { for (p <- people) yield
        <tr>
          <td> { p.name } </td>
          <td> { p.age.toString() } </td>
        </tr> }
      </table>;
    }

    val people = new AddressBook(
      Person("Tom", 20),
      Person("Bob", 22),
      Person("James", 19))

    val e2 =
      <html>
      <body>
        { people.toXHTML }
      </body>
      </html>;
    val _e2: Elem = read(write(e2))
    check(e2, _e2)
  }
  catch {
    case e: Exception =>
      println("Error in Test4_xml: " + e)
      throw e
  }
}

//############################################################################
// Test user-defined classes WITHOUT nesting

@serializable
class Person(_name: String) {
  private var name = _name
  override def toString() = name
  override def equals(that: Any): Boolean =
    that.isInstanceOf[Person] &&
    (name == that.asInstanceOf[Person].name)
}

@serializable
class Employee(_name: String) {
  private var name = _name
  override def toString() = name
}
@serializable
object bob extends Employee("Bob")

object Test5 {
  val x1 = new Person("Tim")
  val x2 = bob

  try {
    val y1: Person   = read(write(x1))
    val y2: Employee = read(write(x2))

    check(x1, y1)
    check(x2, y2)
  }
  catch {
    case e: Exception =>
      println("Error in Test5: " + e)
  }
}

//############################################################################
// Test user-defined classes WITH nesting

@serializable
object Test6 {
  @serializable
  object bill extends Employee("Bill") {
    val x = paul
  }
  @serializable
  object paul extends Person("Paul") {
    val x = 4  //  bill; => StackOverflowException !!!
  }
  val x1 = new Person("John")
  val x2 = bill
  val x3 = paul

  try {
    val y1: Person   = read(write(x1))
    val y2: Employee = read(write(x2))
    val y3: Person   = read(write(x3))

    check(x1, y1)
    check(x2, y2)
    check(x3, y3)
  }
  catch {
    case e: Exception =>
      println("Error in Test6: " + e)
  }
}

//############################################################################
// Test code

object Test {
  def main(args: Array[String]) {
    Test1_scala
    Test2_immutable
    Test3_mutable
    Test4_xml
    Test5
    Test6
  }
}

//############################################################################

