/*  Engr 691-6, Special Topics, Software Language Engineering
    Fowler's Computer Configuration DSL using Function Sequences
      in Nested Closures 
    H. C. Cunningham
    Version 1:  11 April 2009
    Version 1a: 12 April 2009
    Version 1b: 13 May 2009   Minor Scala programming style updates.

123456789012345678901234567890123456789012345678901234567890123456789012345678

This is a Scala reimplementation of Martin Fowler's Computer
Configuration DSL given in the Nested Closures chapter of his
in-progress DSL book under the topic "Wrapping a Function Sequence in
a Nested Closure" subsection.  This example uses the Object Scoping
and Nested Closures (as well as Expression Builder and Context
Variable) DSL patterns.  It also required a modifed semantic model
code from that needed for the previous Computer Configuration DSL
(from the Method Chaining chapter) that used method chaining without
closures.

Fowler's program is in Ruby and given in the document
http://www.martinfowler.com/dslwip/NestedClosure.html.


The overall implementation in Scala can probably be redesigned to
take better advantage of Scala features.


Here is Fowler's example DSL program rewritten in the Scala syntax:

    computer { 
      processor {
        cores(2)
        i386
        processorSpeed(2.2)
      }
      disk {
        size(150)
      }
      disk {
        size(75)
        diskSpeed(7200)
        sata
      }
    }
*/

/* The abstract class ComputerBuilder implements an Expression Builder
   for the configuration of Computers that include a single processor
   and multiple disks.  It is designed according to Object Scoping and
   Nested Closures DSL patterns.
*/

abstract class ComputerBuilder {
 
  // Context variables
  protected var currentComputer:  Computer  = null
  protected var currentProcessor: Processor = null
  protected var currentDisk:      Disk      = null

  // Deferred method to hold DSL program in subclass
  def doBuild: Computer

  // DSL statement "computer".  Unlike the other DSL statements,
  // "computer" returns the "Computer" object it creates.
  def computer(compBlock: => Unit): Computer = {
    currentComputer = new Computer()
    compBlock
    currentComputer
  }

  // DSL statement "processor".
  def processor(procBlock: => Unit) { 
    currentProcessor = new Processor()
    currentComputer.setProcessor(currentProcessor)
    procBlock
  }

  // DSL statement "cores".
  def cores(arg: Int) { currentProcessor.setCores(arg) }

  // DSL statement "i386".
  def i386 { currentProcessor.setType(Processor.Type.i386) }

  // DSL statement "processorSpeed".
  def processorSpeed(arg: Double) { currentProcessor.setSpeed(arg) }

  // DSL statement "disk".
  def disk(diskBlock: => Unit) {
    currentDisk = new Disk()
    currentComputer.addDisk(currentDisk)
    diskBlock
  }

  // DSL statement "size".
  def size(arg: Int) { currentDisk.setSize(arg) }

  // DSL statement "diskSpeed".
  def diskSpeed(arg: Int) { currentDisk.setSpeed(arg) }

  // DSL statement "sata".
  def sata { currentDisk.setIface(Disk.Interface.SATA) }

  // Accessor methods
  def getComputer: Computer = currentComputer
}


/* Test the DSL with Fowler's example DSL program.  */

class BasicComputerBuilder extends ComputerBuilder {
  def doBuild = {
    computer { 
      processor {
        cores(2)
        i386
        processorSpeed(2.2)
      }
      disk {
        size(150)
      }
      disk {
        size(75)
        diskSpeed(7200)
        sata
      }
    }
  }
}

object CompConfigClosure {

  def main(args: Array[String]) {
    println("\nComputer Configuration DSL test beginning.\n")

    val builder = new BasicComputerBuilder()
    println(builder.doBuild)

    println("Computer Configuration DSL Test ending.")
  }
}
