#!/usr/bin/ruby -w
# implementation of Composite, Visitor and Value patterns
# TODO apply Value pattern to Component

class Module
  def abstract(*ids)
    for id in ids
      name = id.id2name # 1.4.x specific
      class_eval %Q{
        def #{name}(*a)  
          raise NotImplementError, "#{name} not implemented"
        end
      }
    end
  end

  alias implements include
end

# interface definitions
module Component
  abstract :components
end

module Visitor
  abstract :visit, :visitComposite, :visitLeaf
end

module Visitee
  abstract :accept
end

module Value
  abstract :value, :value=
end

# concrete implementations
class Composite; implements Component, Visitee
  attr :name

  def initialize name
    @name = name
    @components = []
  end

  def add item
    @components << item
  end

  # Component interface
  def components
    @components
  end

  # Visitee interface
  def accept visitor
    visitor.visitComposite(self)
  end

  def inspect
    s = @name
    @components.each{ |c|
      if c.type == Leaf
        s += " " + c.name
      else
        items = c.components
        if items
          items.each{ |i|
            s += " " + i.inspect
          }
        end
      end
    }
    s
  end
end

class Leaf; implements Component, Visitee
  attr :name

  def initialize name
    @name = name
  end

  # Component interface
  def components
    nil
  end

  # Visitee interface
  def accept visitor
    visitor.visitLeaf(self)
  end

  def inspect
    @name
  end
end

class StringVisitor; implements Visitor
  attr :result

  def initialize item=nil
    @result = ""
    if item
      item.accept(self)
    end
  end

  def visit item
    item.accept(self)
  end

  def visitComposite item
    @result += "( #{item.name} "
    sep = ""
    item.components.each{ |c|
      @result += sep
      c.accept(self)
      sep = " "
    }
    @result += " )"
  end

  def visitLeaf item
    @result += item.name
  end
end

root = Composite.new("root")
root.add(Leaf.new("l1"))
root.add(Leaf.new("l2"))
c = Composite.new("c1")
c.add(Leaf.new("l3"))
c.add(Leaf.new("l4"))
root.add(c)
#p root
v = StringVisitor.new
v.visit(root)
puts v.result
v = StringVisitor.new root
puts v.result
