require 'hpricot'
require 'erb'
require 'enumerator'

module Amrita2
 module ScalarData
    def amrita_value
      Amrita2::Util::sanitize_text(to_s)
    end
  end

  module DictionaryData
    def amrita_value(name)
      self.__send__(name)
    end

    def self.===(other)
      if other == nil
        true
      else
        super
      end
    end
  end

  module Enum
  end

  module NullObject
    def amrita_value(key=nil)
      nil
    end

    def each
    end
  end

  module ElementHelper
  end
end

class NilClass
  include Amrita2::NullObject
end

class FalseClass
  include Amrita2::NullObject
end

class String
  include Amrita2::ScalarData
  def amrita_value
    Amrita2::Util::sanitize_text(self)
  end
end

class Integer
  include Amrita2::ScalarData
end

class BicDecimal
  include Amrita2::ScalarData
end

class Float
  include Amrita2::ScalarData
end

class Time
  include Amrita2::ScalarData
end

class Array
  include Amrita2::Enum
end

class Range
  include Amrita2::Enum
end

class Hash
  include Amrita2::DictionaryData
  def amrita_value(name)
    self[name.to_sym]
  end
end

class Struct
  include Amrita2::DictionaryData
end

class Binding
  include Amrita2::DictionaryData

  def amrita_value(name)
    eval "begin; #{name}; rescue(NameError); @#{name};end;", self
  end
end

class Hpricot::Elem
  include Amrita2::ElementHelper
end

module Amrita2
  FilterMethods = [
    :filter_element,
    :parse_element,
    :parse_node,
    :setup_type_renderer,
    :generate_dynamic_element,
    :define_element_method,
    :loop_check_code,
    :method_body_code,
    :value_filter_code,
    :renderer_code,
    :element_render_code
  ]

  module Core
  end

  module Util
  end

  module Runtime
    include Amrita2
    include Util
  end

  module Filters
    class Base
    end
  end

  module Renderers  #:nodoc: all
    class Base
    end
  end

  module Macro  #:nodoc: all
    module Standard
    end
    include Standard

    class Base
    end
  end

  module CompileTimeContext  #:nodoc: all
    include Filters
    include Renderers
    include Macro
    include Standard
  end

  class TemplateError < RuntimeError
  end

  module Util # :nodoc: all

    # Amrita2 sanitize anything except for SanitizedString
    # If you want to sanitize yourself and don't want to Amrita2 sanitize your object,
    # pass SanitizedString[x] as model data.
    class SanitizedString < String
      def SanitizedString::[](s)
        new(s.to_s).freeze
      end

      def amrita_value
        self
      end

      def to_s
        self
      end

      def *(n)
        SanitizedString[super]
      end

      def inspect
        %[SanitizedString[#{super}]]
      end
    end

    # This module provide methods for avoid XSS vulnerability
    # taken from IPA home page(Japanese)
    # http://www.ipa.go.jp/security/awareness/vendor/programming/a01_02.html
    NAMECHAR = '[-\w\d\.:]'
    NAME = "([\\w:]#{NAMECHAR}*)"
    NOT_REFERENCE = "(?!#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)" # borrowed from rexml
    AMP_WITHOUT_REFRENCE = /&#{NOT_REFERENCE}/
    # escape &<>

    def self.sanitize_text(text)
      return nil unless text
      s = text.dup
      s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
      s.gsub!("<", '&lt;')
      s.gsub!(">", '&gt;')
      s
    end

    # escape &<>"'
    def self.sanitize_attribute_value(text)
      return nil unless text
      s = text.dup
      s.gsub!(AMP_WITHOUT_REFRENCE, '&amp;')
      s.gsub!("<", '&lt;')
      s.gsub!(">", '&gt;')
      s.gsub!('"', '&quot;')
      #s.gsub!("'", '&#39;')
      s
    end

    DefaultAllowedScheme = {
      'http' => true,
      'https' => true,
      'ftp' => true,
      'mailto' => true,
    }

    #UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%]|)
    UrlInvalidChar = Regexp.new(%q|[^;/?:@&=+$,A-Za-z0-9\-_.!~*'()%#]|) #'

    # +sanitize_url+ accepts only these characters
    #     --- http://www.ietf.org/rfc/rfc2396.txt ---
    #     uric = reserved | unreserved | escaped
    #     reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
    #     unreserved = alphanum | mark
    #     mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
    #     escaped = "%" hex hex
    #
    # +sanitize_url+ accepts only schems specified by +allowd_scheme+
    #
    # The default is http: https: ftp: mailt:

    def self.sanitize_url(text, allowd_scheme = DefaultAllowedScheme)
      # return nil if text has characters not allowd for URL

      return nil if text =~ UrlInvalidChar

      # return '' if text has an unknown scheme
      # --- http://www.ietf.org/rfc/rfc2396.txt ---
      # scheme = alpha *( alpha | digit | "+" | "-" | "." )

      if text =~ %r|^([A-Za-z][A-Za-z0-9+\-.]*):|
          return nil unless allowd_scheme[$1]
      end

      # escape HTML
      # special = "&" | "<" | ">" | '"' | "'"
      # But I checked  "<" | ">" | '"' before.
      s = text.dup
      #s.gsub!("&", '&amp;')
      s.gsub!("'", '&#39;')

      s
    end

    module OptionSupport # :nodoc: all
      attr_reader :opt
      def parse_opt(*args)
        @opt = {}
        hash = args.last
        if hash.kind_of?(Hash)
          @opt.merge!(hash)
          args.pop
        end

        args.each do |key|
          @opt[key] = key
        end
        @opt
      end

      def [](key)
        @opt[key]
      end

      def method_missing(sym, *args, &block)
        if @opt and @opt.has_key?(sym)
          @opt[sym]
        else
          super
        end
      end
    end
    class Option
      include OptionSupport
      def initialize(*args)
        parse_opt(*args)
      end
    end
    class Tuple < Array # :nodoc: all
      include ScalarData
      def self.[](*args)
        self.new(args)
      end
    end
  end


  module Runtime # :nodoc: all
    def amrita_set_context_value(v)
      Thread::current[:amrita_context_value] = v
    end

    def amrita_get_context_value
      Thread::current[:amrita_context_value]
    end

    def new_element(tag, attrs={}, &block)
      a = {}
      attrs.each { |k,v| a[k.to_s] = v }
      ret = Hpricot::Elem.new(Hpricot::STag.new(tag.to_s, a))
      ret.instance_eval(&block) if block_given?
      ret
    end

    def start_tag(e, out="")
      out << "<#{e.stag.name}"
      e.attributes.each do |k, v|
        next unless v
        out << " "
        out << "#{k}=\"#{Util::sanitize_attribute_value(v.to_s)}\""
        #attr.write(out)
      end unless e.attributes.empty?
      out << ">"
      out
    end

    def end_tag(e, out="")
      out << "</#{e.stag.name}>"
      out
    end
  end

  module ElementHelper # :nodoc: all
    def elements
      children.select do |c|
        c.kind_of?(Hpricot::Elem)
      end
    end

    def contents
      if children.size > 0
        SanitizedString[children.to_s]
      else
        nil
      end
    end

    def set_attribute(key, val)
      case val
      when nil, ""
        delete_attribute(key)
      else
        super
      end
    end

    def delete_attribute(key)
      raw_attributes.delete(key)
    end

    def add(elem)
      insert_after(elem, nil)
    end

    def add_text(text)
      insert_after(Hpricot::Text.new(text), nil)
    end

    def as_amrita_dictionary(opt={})
      ret = {}
      attributes.each do |k, v|
        ret[k.intern] = v
      end
      if opt[:use_contents]
        ret[opt[:use_contents]] =
          Amrita2::SanitizedString[
                                   children.collect do |c|
                                     c.to_s
                                   end.join
                                  ]
      end

      if opt[:use_tag]
        { name.intern => ret }
      else
        ret
      end
    end
  end

  module Core
    class CodeGenerator # :nodoc: all
      def initialize
        @iehack = true
        @lines = []
        @indent = 0
        init_strbuf
        @module_stack = [CGModule.new('')]
        @current_stream = "__stream__"
      end

      def init_strbuf
        @strbuf = ""
      end

      def code(line)
        flush
        @lines << [@indent, line]
      end

      def comment(comments)
        comments.each do |c|
          @lines << [@indent, "# #{c}"]
        end
      end

      def result
        flush
        m = @module_stack.shift
        if m
          m.constants.each do |name, defs|
            code("#{name} = #{defs}")
          end
        end
        @lines.collect do |indent, l|
          "  " * indent + l
        end.join("\n")
      end

      def put_constant(c)
        @strbuf.concat c.to_s
      end

      def flush
        if @strbuf.size > 0
          @lines << [@indent, "#{@current_stream}.concat(#{@strbuf.inspect})"]
          init_strbuf
        end
      end

      def put_hpricot_node(node)
        s = ""
        node.output(s)
        put_constant s
      end

      def level_up
        @indent += 1
        yield
        flush
        @indent -= 1
      end

      def if_(cond, &block)
        code("if #{cond}")
        level_up do
          yield
        end
        code("end")
      end

      def case_(obj, &block)
        code("case #{obj}")
        yield
        code("end")
      end

      def when_(obj, &block)
        code("when #{obj}")
        level_up do
          yield
        end
      end

      def else_(&block)
        code("else")
        level_up do
          yield
        end
      end

      def with_stream(new_stream_name=nil)
        if new_stream_name
          old_stream = @current_stream
          code("before_#{new_stream_name} = #@current_stream")
          @current_stream = new_stream_name.to_s
        end
        code("#@current_stream = '' ")
        yield
        flush
      ensure
        if new_stream_name
          @current_stream = old_stream
          code %[Thread::current[:amrita_stream] ||= {} ]
          code %[Thread::current[:amrita_stream][#{new_stream_name.inspect}] = #{new_stream_name}]
          code("#@current_stream = before_#{new_stream_name}")
        end
        code("#@current_stream")
      end

      def put_stream(name)
        code %[#@current_stream.concat(Thread::current[:amrita_stream][#{name.inspect}])]
      end

      def def_method(name, *args)
        if args.size > 0
          code("def #{name}(#{args.join(',')})")
        else
          code("def #{name}")
        end
        level_up do
          with_stream do
            yield
            flush
          end
        end
        code("end")
      end

      def put_static_element(e)
        attr = e.attributes.collect do |name, value|
          "#{name} = #{value.inspect}"
        end
        if attr.size > 0
          put_constant("<#{e.name} #{attr.join(' ')}>")
        else
          put_constant("<#{e.name}>")
        end
        yield
        put_constant("</#{e.name}>")
      end

      def put_expression(exp)
        code("#{@current_stream}.concat((#{exp}).to_s)")
      end

      def put_string_expression(exp)
        code("#{@current_stream}.concat(#{exp})")
      end

      def define_class(name, &block)
        define_module_or_class("class", name, &block)
      end

      def define_module_or_class(typ, name, &block)
        @module_stack.unshift CGModule.new(name)
        code("#{typ} #{name}")
        level_up do
          yield
          m = @module_stack.shift
          m.end_of_module(self)
        end
        code("end")
      end

      def define_constant(const_def)
        @module_stack.first.define_constant(const_def)
      end

      def eval(src)
        code "eval(#{src}, __binding__)"
      end

      def eval_and_print(src)
        put_string_expression "eval(#{src}, __binding__)"
      end

      class CGModule # :nodoc:
        attr_reader :name
        attr_reader :constants
        attr_reader :methods

        def initialize(name)
          @name = name
          @constants = []
          @methods = []
        end

        def define_constant(const_def)
          name = nil
          @constants.each do |n, cdef|
            if cdef == const_def
              name = n
              break
            end
          end
          unless name
            name = "C#{sprintf("%03d", @constants.size)}"
            @constants << [name, const_def]
          end
          name
        end

        def define_method(vars, &block)
          name = "m#{sprintf("%03d", @methods.size)}"
          @methods << [name, vars, block]
          name
        end

        def end_of_module(cg)
          @constants.each do |name, defs|
            cg.code("#{name} = #{defs}")
          end
          @methods.each do |name, vars, body|
            cg.define_method(name, vars) do
              body.call
            end
          end
        end
      end
    end

    class BaseNode # :nodoc: all
      attr_reader :parent

      def initialize(parent)
        @parent = parent
      end

      def render_me(cg)
      end

      def module_src(cg)
      end

      def root
        parent.root
      end

      def parent_de
        parent
      end

      def dynamic?
        false
      end

      def has_ruby?
        false
      end
    end

    class StaticNode < BaseNode # :nodoc: all
      def initialize(parent, node)
        super(parent)
        @node = node
      end

      def render_me(cg)
        #cg.put_string_expression(cg.define_constant(@node.to_s.inspect))

        # to keep &nbsp;
        s = ""
        @node.output(s, :preserve=>true)
        cg.put_string_expression(cg.define_constant(s.inspect))
      end
    end

    class CommentNode < BaseNode # :nodoc: all
      def initialize(parent, node)
        super(parent)
        @node = node
      end

      def render_me(cg)
        #cg.put_string_expression(cg.define_constant(@node.to_s.inspect))
        # to keep &nbsp;
        s = ""
        @node.output(s, :preserve=>true)
        cg.put_string_expression(cg.define_constant(s.inspect))
      end
    end

    class ErbNode < BaseNode # :nodoc: all
      include Util
      attr_reader :node
      def initialize(parent, node)
        super(parent)
        @node = node
        @erb_trim_mode = nil
      end

      def dynamic?
        true
      end

      def render_me_old(cg)
        src = @node.inner_text
        src.each do |s|
          cg.code("# #{s.chomp}")
        end
        erb = cg.define_constant %[ERB.new(#{with_context_value_erb(src).inspect}, nil, #{@erb_trim_mode.inspect})]
        cg.code("amrita_set_context_value($_)")
        cg.put_string_expression %[#{erb}.result(__binding__)]
        cg.code("$_ = amrita_get_context_value")
      end

      def render_me(cg)
        src = @node.inner_text
        src.each do |s|
          cg.code("# #{s.chomp}")
        end
        erb = cg.define_constant %[ERB.new(#{with_context_value_erb(src).inspect}, nil, #{@erb_trim_mode.inspect}).src]
        cg.code("amrita_set_context_value($_)")
        cg.eval_and_print(erb)
        cg.code("$_ = amrita_get_context_value")
      end

      def has_ruby?
        true
      end

      private
      def with_context_value_erb(src)
        [
          "<% $_ = amrita_get_context_value %>",
          src,
          "<% amrita_set_context_value($_) %>",
        ].join("")
      end
    end

    class ParentNode < BaseNode # :nodoc: all
      attr_reader :element, :parent, :children
      def initialize(parent, element)
        super(parent)
        @element = element
        @children = parent_de.parse_element(@element)
      end

      def dynamic?
        children.any? { |c| c.dynamic? }
      end

      def has_dynamic?
        children.any? { |c| c.dynamic? }
      end

      def has_ruby?
        children.any? { |c| c.has_ruby? }
      end

      def each(&block)
        children.each(&block)
      end

      def module_src(cg)
        each do |c|
          c.module_src(cg)
        end
      end

    end

    class CompoundElement < ParentNode # :nodoc: all
      def render_me(cg)
        if @element.empty?
          cg.put_constant(@element.to_s)
        else
          @parent.element_render_code(cg, @element) do
            each do |c|
              c.render_me(cg)
            end
          end
        end
      end
    end

    class DynamicElement < ParentNode # :nodoc: all
      include Util
      include OptionSupport

      attr_reader :parent, :name, :children, :filter, :renderers, :element, :attrs
      def self.delegate_to_filter(name)
        module_eval <<-END
        def #{name}(*args,&block)
          @filter.#{name}(self, *args,&block)
        end
        END
      end

      FilterMethods.each do |m|
        delegate_to_filter(m)
      end

      def initialize(parent, name, element, filters=[])
        @parent,@name,@element = parent, name, element
        parse_opt(root.opt)
        @attrs = {}
        delete_am_attrs
        setup_filter(filters)
        @element = filter_element(@element)
        @renderers = []
        super(parent, element)

        setup_type_renderer
      end

      def [](key)
        @attrs[key]
      end

      def parent_de
        self
      end

      def dynamic?
        true
      end

      def has_ruby?
        super or am_for_value or am_skipif_value or am_v_value
      end

      def setup_filter(filters)
        #default_filter = @parent ? @parent.filter : DefaultFilter.new
        default_filter =  DefaultFilter.new
        default_filter.parse_opt(opt)
        if filters.size > 0
          @filter = current = filters.first
          for f in filters do
            current.next_ = f
            current = f
          end
          current.next_ = default_filter
        else
          @filter = default_filter
        end
      end

      def class_name
        if name
          "XX" + name.capitalize.gsub(/[^\w\d]/, "_")
        else
          "XX%d" % [object_id.abs]
        end
      end

      def instance_name
        class_name + "Instance"
      end

      def compile(cg = CodeGenerator.new)
        define_element_method(cg) do
          cg.code("$_ = value")
          loop_check_code(cg)
          method_body_code(cg)
        end
      end

      def render_me(cg, n = name)
        if (n)
          cg.put_string_expression("#{instance_name}.render_with($_.amrita_value(#{n.inspect}), __binding__)")
        else
          cg.put_string_expression("#{instance_name}.render_with($_, __binding__)")
        end
      end

      def module_src(cg)
        cg.define_class(class_name) do
          cg.code <<-END
          include Amrita2
          include Amrita2::Runtime
        END
          compile(cg)
          super
        end
        cg.code("#{instance_name} = #{class_name}.new")
      end

      def am_for_value
        @attrs[am_for.intern]
      end

      def am_skipif_value
        @attrs[am_skipif.intern]
      end

      def am_v_value
        @attrs[am_v.intern]
      end

      private

      def delete_am_attrs
        [
          am_src,
          am_filter,
          am_skipif,
          am_for,
          am_v,
        ].each do |key_s|
          key = key_s.intern
          @attrs[key] = @element.attributes[key_s]
          @element.delete_attribute(key_s)
        end
      end
    end

    class RootElement < DynamicElement # :nodoc: all
      include Filters

      attr_reader :xml_decl, :doctype

      def initialize(elements, opt={}, &block)
        @xml_decl = @doctype = nil
        parse_opt(opt)
        if block_given?
          @filter_proc = block
        else
          @filter_proc = proc do |e, src, filters|
          end
        end
        e = Hpricot.make("<root />", :xml=>true).first
        elements.each do |c|
          case c
          when Hpricot::Elem, Hpricot::Text, Hpricot::Comment, Hpricot::BogusETag
            e.insert_after(c, nil)
          when Hpricot::XMLDecl
            @xml_decl = c
          when Hpricot::DocType
            @doctype = c
          else
            raise "#{c.class}"
          end
        end
        filters = []
        @filter_proc.call(e,nil, filters)
        super(nil, nil, e, filters)
      end

      def root
        self
      end

      def compile(cg)
        cg.code <<-END
        include Amrita2
        include Amrita2::Runtime
      END
        super(cg)
        @children.each do |c|
          c.module_src(cg)
        end
      end

      def compile_filters(element, name, *args)
        src = args.compact.join('|')
        filters = parse_filter(src)
        @filter_proc.call(element, name, filters)
        filters
      end

      def parse_filter(src)
        src.gsub!("\n", " ")
        case src
        when "",nil
          []
        else
          case ret = eval(src, @opt[:compiletime_binding])
          when Class
            [ret.new]
          when Module
            [ModuleExtendFilter[ret]]
          when Filters::Base
            [ret]
          when Array
            case ret.first
            when Symbol
              [FunctionFilter[*ret]]
            else
              ret
            end
          when Symbol
            [FunctionFilter[ret]]
          when nil
            []
          else
            raise TemplateError, "unknown Filter type #{ret.class}"
          end
        end
      rescue ScriptError, NameError
        raise TemplateError, "error in filters #{src.inspect} #$!"
      end
    end

    class DefaultFilter < Filters::Base
      include Renderers
      include Filters
      include Util::OptionSupport

      def filter_element(de, element)
        element
      end

      def parse_element(de, element)
        element.enum_for(:each_child).collect do |node|
          de.parse_node(node)
        end
      end

      def parse_node(de, node)
        case node
        when Hpricot::Elem
          de.generate_dynamic_element(node) || CompoundElement.new(de, node)
        when Hpricot::CData
          ErbNode.new(de, node)
        when Hpricot::Text
          StaticNode.new(de, node)
        when Hpricot::Comment
          CommentNode.new(de, node)
        when Hpricot::BogusETag
          StaticNode.new(de, node)
        else
          raise "not implemented #{node.class}"
        end
      end

      def generate_dynamic_element(de, e)
        src = e.attributes[de.am_src]
        filters_src = e.attributes[de.am_filter]
        if src
          name, fs = src.split('|', 2)
          name.strip!
          filters = de.root.compile_filters(e, name, fs, filters_src)
          DynamicElement.new(de, name , e, filters)
        elsif filters_src
          filters = de.root.compile_filters(e, nil, filters_src)
          DynamicElement.new(de, nil , e, filters)
        else
          nil
        end
      end

      def setup_type_renderer(de)
        if de.has_dynamic?
          de.renderers << HashRenderer.new unless de.renderers.find {|r| r.kind_of?(HashRenderer) }
        else
          de.renderers << ScalarRenderer.new
        end
        de.renderers << ElseRenderer.new
      end

      def define_element_method(de, cg, &block)
        cg.def_method("render_with", "value", "__binding__", "__cnt__ = -1 ", &block)
      end

      def loop_check_code(de, cg)
        cg.if_ "$_.kind_of?(Amrita2::Enum) and not $_.kind_of?(ScalarData)" do
          cg.code("$_.each_with_index  do |v, i| ")
          cg.level_up do
            cg.put_string_expression("render_with(v, __binding__, i) ")
          end
          cg.code("end")
          cg.code("return __stream__")
        end
      end

      def method_body_code(de, cg)
        de.value_filter_code(cg, "value")
        #cg.code("p #{de.element.name.inspect}, $_, stream")
        de.renderer_code(cg, de.element)
        #cg.code("p #{de.element.name.inspect}, $_, stream")
      end

      def value_filter_code(de, cg, value_name)
      end

      def renderer_code(de, cg, element)
        r = de.renderers
        case r.size
        when 0
        when 1
          r.first.generate_body(cg, de, element)
        else
          cg.case_("$_") do
            r.each do |r1|
              r1.generate_when(cg) do
                r1.generate_body(cg, de, element)
              end
            end
          end
        end
      end

      def element_render_code(de, cg, element, &block)
        if ((element.name == 'span' or element.name == '_' or element.name == 'root') and element.attributes.size == 0)
          yield
        else
          cg.put_static_element(element, &block)
        end
      end
    end

    #
    # = using Amrita2::Template
    #
    #   require "amrita2/template"
    #   include Amrita2
    #   tmpl = Template.new <<-END
    #     <<html<
    #       <<body<
    #         <<h1 :title>>
    #         <<p :body<
    #           <<:template>> is a html template libraly for <<:lang>>
    #   END
    #
    #   puts tmpl.render_with(:title=>"hello world", :body=>{ :template=>"Amrita2", :lang=>"Ruby" })

    class Template
      include Amrita2
      include Util
      include OptionSupport
      include Filters
      include CompileTimeContext
      include Runtime

      # Specify attribute prefix for dynamic element. Defaults to 'am:'.
      attr_accessor :amrita_prefix
      # Specify weather use inline ruby. Defaults to +true+
      attr_accessor :inline_ruby

      attr_reader :tracer
      attr_reader :root

      # Initialize Template object using +text+ and +opts+.
      # Optionaly specify block for setting filters.
      #
      #   t = Amrita2::Template.new(text) do |e, src, filters|
      #     if src == "aaa"
      #       filters << Amrita2::Filters::Default[456]
      #     end
      #   end

      def initialize(text, *opts, &block)
        parse_opt(*opts)
        @text = text.dup
        @setuped = false
        @filter_proc = block
        @text_domain = nil
        @inline_ruby = true
        @amrita_prefix = @opt[:amrita_prefix] || "am:"
      end

      def amrita_src=(v)
        @opt[:amrita_src] = v
      end

      def amrita_filter=(v)
        @opt[:amrita_filter] = v
      end

      def compiletime_binding=(b)
        @opt[:compiletime_binding] = b
      end

      # Print compiled ruby code and runtime trace to
      # +io_or_type+
      #
      #   tmpl = Template.new "...."
      #   tmpl.set_trace(STDOUT)
      #   puts tmpl.render_with(...)

      def set_trace(io_or_type, &block)
        @tracer = Tracer.new(io_or_type, &block)
      end

      def setup
        setup_opt
        @preprocessor = PreProcessor.new(@opt)
        cg = CodeGenerator.new
        preprocessed_text = @preprocessor.process(@text)

        if bc = @preprocessor.sections[:BeforeCompile]
          self.instance_eval do
            eval bc
          end
        end

        filter_setup do |e, src, filters|
          filters.unshift Trace.new if @tracer and @tracer.auto_trace?
          filters.unshift InlineRuby.new if @inline_ruby
        end

        compile(cg, preprocessed_text)
        @setuped = true
        cg.result
      ensure
        @tracer.code(cg) if @tracer and @tracer.code_trace?
      end

      # render template with +value+ and +binding+.
      # use TOPLEVEL_BINDING if +binding+ is nil.
      #
      def render_with(value, binding_=nil)
        setup unless @setuped
        unless binding_
          if value.kind_of?(Binding)
            binding_ = value
          else
            binding_ = binding
          end
        end

        ret = nil

        if @opt[:process_xhtml]
          ret = ""
          ret << @root.xml_decl.to_s << "\n" if @root.xml_decl
          ret << @root.doctype.to_s << "\n"  if @root.doctype
          ret << @cls.new.render_with(value, binding_)
        else
          ret = @cls.new.render_with(value, binding_)
        end
        SanitizedString[ret]
      end

      private
      def setup_opt
        @opt[:am_src] = @opt[:amrita_src] || @amrita_prefix + "src"
        @opt[:am_filter] = @opt[:amrita_filter] || @amrita_prefix + "filter"
        @opt[:am_skipif] = @opt[:amrita_skipif] || @amrita_prefix + "skipif"
        @opt[:am_for] = @opt[:amrita_for] || @amrita_prefix + "for"
        @opt[:am_v] = @opt[:amrita_v] || @amrita_prefix + "v"
        @opt[:tracer] = @tracer
        @opt[:compiletime_binding] = @opt[:compiletime_binding] || binding
      end

      def compile(cg, text)
        @elements = Hpricot.make(text, :xml=>true)
        @root = RootElement.new @elements, opt, &@filter_proc
        @root.text_domain = @text_domain if @text_domain
        @root.compile(cg)

        @cls = Class.new

        ##############################################################################
        # very dirty hack
        # ruby-gettext does not work well with class without a name
        #
        #const_name = "T%08x" % Thread.current.object_id
        const_name = "T%08x" % @cls.object_id
        Amrita2.module_eval { remove_const(const_name) } if Amrita2.const_defined?(const_name)
        Amrita2.const_set(const_name, @cls)
        # This should be modified later
        ##############################################################################

        @cls.module_eval cg.result
        @cls.const_set(:Tracer, @tracer) if @tracer
      rescue SyntaxError
        raise RuntimeError,$!
      end

      def setup_filter_proc
        old_proc = @filter_proc
        @filter_proc = proc do |e, name ,filters|
          filters.unshift Trace.new if @tracer and @tracer.auto_trace?
          filters.unshift InlineRuby.new if @inline_ruby
          old_proc.call(e, name ,filters) if old_proc
        end
      end

      # ment to be used in macro.rb and BeforeCompile section
      def filter_setup(&block)
        old_proc = @filter_proc
        @filter_proc = proc do |e, name ,filters|
          block.call(e, name, filters)
          old_proc.call(e, name, filters) if old_proc
        end
      end

      class Tracer
        def initialize(io_or_type, &block)
          @auto_trace = false
          @trace_proc = block
          case io_or_type
          when :code, :element, :all
            @type = io_or_type
            raise "need block" unless @trace_proc
          when :all_element
            @type = :element
            @auto_trace = true
            raise "need block" unless @trace_proc
          else
            if io_or_type.respond_to?(:<<)
              @type = :all
              @auto_trace = true
              @trace_proc = proc do |msg|
                io_or_type << msg << "\n"
              end
            else
              raise "unknown type for trace #{io_or_type.inspect}"
            end
          end
        end

        def auto_trace?
          @auto_trace
        end

        def code_trace?
          @type == :code or @type == :all
        end

        def code(cg)
          @trace_proc.call(cg.result) if code_trace?
        end

        def <<(msg)
          @trace_proc.call(msg) if @type == :all or @type == :element
        end
      end
    end

    class PreProcessor # :nodoc: all
      attr_reader :sections
      CDataStart = %r[(.*?)(<!\[CDATA\[)(.*)]m
      CDataEnd = %r[(.*?)(\]\]>)(.*)]m

      def initialize(opt={})
        @opt = opt
        init()
        @trace = false
        #@trace = true
      end

      def process(s)
        init
        ret = process_block(s)
        ret = process_erb_and_inline_amxml(ret)
        ret << pop_tag(0)
        ret
      end

      def process_erb_and_inline_amxml(s)
        if s =~ CDataStart
          out_of_cdata, cdata, in_cdata = $1, $2, $3
          process_out_of_cdata(out_of_cdata) + cdata + parse_in_cdata(in_cdata)
        else
          process_out_of_cdata(s)
        end
      end

      def process_block(s)
        ret = ""
        lines = s.to_a
        while line = lines.shift
          next if line =~ /^\s*$/
          current_indent = line[/^ */].size
          ret << pop_tag(current_indent)

          case line
          when %r[^\s*#]
            # It's a comment. do nothing
          when AmXml::R_AMXML_BLOCK_START
            amxml = $1
            while true
              l = lines.shift
              case l
              when %r[(.*)<(?: |-)*$]
                amxml << $1
                break
              when nil
                raise "incomplete amxml block start #{amxml}"
              else
                amxml << l
              end
            end
            a = AmXml.parse(amxml, @opt)
            raise "illeagal amxml multi line #{amxml.inspect}" unless a
            push_tag(current_indent, a.tag)
            ret << a.result(false)
          when AmXml::R_AMXML_ERB_LINE
            mark, src =  $1,$2
            ret << "<![CDATA[<% "
            while true
              case mark
              when '='
                ret << "\n _erbout.concat(eval(%{#{src}}).to_s) "
              when String
                ret << "\n#{src}"
              else
                break
              end
              case l = lines.shift
              when AmXml::R_AMXML_ERB_LINE
                mark, src =  $1,$2
              else
                mark = nil
                lines.unshift l
              end
            end
            ret << "\n%>]]>\n"
          when AmXml::R_AMXML_BLOCK_LINE
            a = AmXml.parse($1, {:trline=>$2.include?("-")}.merge(@opt))
            raise "illeagal amxml block line #{line.inspect}" unless a
            ret << a.result(false)
            push_tag(current_indent, a.tag)
          when %r[(.*)^\s*>>>\s*$]m
            ret << $1
            raise "unmatched >>> " unless @stack.size > 0
            ret << pop_tag(current_indent)
          when %r[^\s*\|\s*([\w:]*)\s*\|\|]
            lp = LineProcessor.new do |cell_text|
              PreProcessor.new.process_block(cell_text)
            end
            while lp.parse_line(line)
              line = lines.shift
            end
            lines.unshift(line) unless line =~ /^( |-)*$/
            ret << lp.get_result_xml
            ret
          else
            ret << line
          end
        end
        ret << pop_tag(0)
        ret
      end

      private

      def init
        @sections = {}
        @stack = []
      end

      def process_out_of_cdata(s)
        erb_nest=0
        ret = ""
        while s.size > 0
          puts "process_out_of_cdata:#{erb_nest}:#{@stack.join('/')}:#{s}" if @trace
          case s
          when %r[\A(.*?)(<%|%>)((?=.?))]m
            case $2
            when "<%" ; erb_nest +=1
            when "%>" ; erb_nest -= 1
            end
            if $2 == "<%" and erb_nest == 1
              if Regexp.last_match.post_match[0] == "("[0] and
                  s =~ %r[\A(.*?)(<%\((\w+?)\))]m
                ret << process_inline_amxml($1)
                s = process_section($3.intern, Regexp.last_match.post_match, 1)
                erb_nest -= 1
              else
                ret << process_inline_amxml($1) << "<![CDATA[<%"
                s = Regexp.last_match.post_match
              end
            elsif $2 == "%>" and erb_nest == 0
              ret << $1 << $2 << "]]>"
              s = Regexp.last_match.post_match
            else
              ret << $1 << $2
              s = Regexp.last_match.post_match
            end
          else
            ret << process_inline_amxml(s)
            s = ""
          end
        end
        ret
      end

      def parse_in_cdata(s)
        if s =~ CDataEnd
          in_cdata, cdata_end, out_of_cdata = $1, $2, $3
          in_cdata + cdata_end + process_erb_and_inline_amxml(out_of_cdata)
        else
          raise "end of cdata not found in #{s}"
        end
      end

      def process_section(section_id, s, nest)
        puts "%s:%d:%s" % [section_id, nest, s]  if @trace
        @sections[section_id] ||= ""
        case s
        when %r[\A(.*?)(<%|%>)(.*)\Z]m
          case $2
          when "<%"
            @sections[section_id] << $1 << $2
            s = process_section(section_id, $3, nest+1)
          when "%>"
            @sections[section_id] << $1
            if nest == 1
              $3
            else
              @sections[section_id] << $2
              s = process_section(section_id, $3, nest-1)
            end
          else
            raise "can't happen"
          end
        else
          raise "can't happen"
        end
      end

      def process_inline_amxml(s)
        puts "process_inline_amxml:%s" % s if @trace
        ret = ""
        s.to_s.scan(%r[(.*?)<<(.*?)>>]m) do |pre, amxml|
          ret << $1 << parse_inline_amxml(amxml)
        end

        if Regexp.last_match
          ret + Regexp.last_match.post_match
        else
          s
        end
      end

      def parse_inline_amxml(s)
        a = AmXml.parse(s, @opt)
        raise TemplateError, "illeagal inline amxml #{s}" unless a
        a.result(true)
      end


      def push_tag(indent, tag)
        @stack.unshift([indent, tag])
      end

      def pop_tag(indent)
        ret = ""
        while @stack.size > 0 and @stack.first[0] >= indent
          tag = @stack.shift[1]
          if tag == :cdata
              ret << "]]>"
          else
              ret << "</#{tag}>"
          end
        end
        ret
      end

      class AmXml
        attr_reader :tag, :attr, :src_name, :filters
        attr_reader :skipif, :skipunless, :for, :value

        # Regexps borrowed from REXML rexml/baseparser.rb
        NCNAME_STR= '[\w:][\-\w\d.#]*?'
        NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"

        R_TAG_ATTR = %r[\s*(#{NAME_STR})((?:\s*#{NAME_STR}\s*=\s*(["'])(?:.*?)\3)*)]um #"
        S_NO_SRC = %[\s*]
        S_SRC_AND_FILTERS = %q[(:([\\w\\d]*))\\s*(\\|.*?)*]
        S_FOR = %q[( \\[(.+)\\] )]
        S_COND = %q[\\?(\\!)?\\[(.+)\\]]
        S_VALUE = %q[\\{(.+)\\}]
        S_TRAILER = %q[((?: |\\-)*)]
        S_ERB_IN_AMXML = %q[%=(.*)]
        S_AMVARS = %[(#{S_FOR}|#{S_COND}|#{S_VALUE}|#{S_ERB_IN_AMXML})?]
        R_AMXML_WITHOUT_TAG = %r[\A\s*(?:\/)?\s*(?:#{S_NO_SRC}|#{S_SRC_AND_FILTERS})*\s*#{S_AMVARS}\s*\Z]um
        R_AMXML = %r[\A#{R_TAG_ATTR}\s*(?:\/)?\s*(?:#{S_NO_SRC}|#{S_SRC_AND_FILTERS})*\s*#{S_AMVARS}\s*\Z]um
        R_AMXML_BLOCK_LINE = %r[^\s*<<\s*?(.*)\s*?<#{S_TRAILER}$]
        R_AMXML_BLOCK_START = %r[^\s*<<\s*((#{R_TAG_ATTR})?[^<>]*)\s*$]
        R_AMXML_ERB_LINE = %r[^\s*\%([ =])(.*)$]

        def self.parse(s, opt={})
          self.new(opt).parse(s, opt[:trline])
        end

        def initialize(opt)
          @opt = opt
          @tag = "_"
          @attr = ""
          @src_name = @filters = nil
          @skipif = @skipunless = @for = @value = nil
        end

        def has_amxml_attrs?
          @src_name or @filters or @skipif or @skipunless or @for or @value
        end

        def parse(s, trline=false)
          case s
          when  R_AMXML_WITHOUT_TAG
            if trline
              @tag = "tr"
            else
              @tag = "_"
            end
            @src_name = $2.strip if $2
            @filters = $3[1..-1].strip if $3
            parse_am_vars($4)
          when %r[\A\s*([\w]+):([\-\w\d.]*)\s*(/?)\s*\Z] # <<aaa:xxx>>
            if $3 == "/"
              @tag = "#$1:#$2"
            else
              @tag, @src_name = $1, $2
            end
            @tag = "_" if @tag == nil or @tag == ""
            @attr = ""
          when R_AMXML
            @tag, @attr = $1.to_s, $2.to_s
            src_start = $4
            @src_name = $5
            @filters = $6[1..-1].strip if $6
            parse_am_vars($7)
          when "%"
            @tag = :cdata
          when /=\s*(\w+)\s*/
            @tag = [:put_stream,$1.intern]
          when /\s*(\w+)\s*=/
            @tag = [:change_stream,$1.intern]
          else
            return nil
          end
          parse_css_selecter_like
          self
        end

        def parse_css_selecter_like
          while true
            case @tag
            when /^(\w+)([#\.])([\w-]+)(.*)$/
              @tag, cls = $1 + $4, $3
              case $2
              when "."
                @attr += " class=\"#{$3}\""
              when "#"
                @attr += " id=\"#{$3}\""
              else
                break
              end
            else
              break
            end
          end
        end

        def parse_am_vars(s)
          case s
          when nil
            # do nothing
          when %r[#{S_FOR}]m
            @for = $2.strip
          when %r[#{S_COND}]m
            if $1 == "!"
              @skipif = $2.strip
            else
              @skipunless = $2.strip
            end
          when %r[#{S_VALUE}]m
            @value = $1.strip
          when %r[#{S_ERB_IN_AMXML}]m
            @erb = $1.strip
          else
            raise "can't happen #{s}"
          end
        end

        def result(single_tag)
          case @tag
          when :cdata
            return "<![CDATA["
          when Array
            case @tag[0]
            when :change_stream
              ret = "<_ am:filter='ChangeStream[#{@tag[1].inspect}]'"
            when :put_stream
              ret = "<_ am:filter='PutStream[#{@tag[1].inspect}]'"
            else
              raise "can't happen #{@tag.inspect}"
            end
            if single_tag
              ret << " />"
            else
              ret << " >"
              @tag = "_"
            end
            return ret
          end
          ret = "<"
          if @tag
            ret << @tag
          else
            ret << "_"
          end
          if @src_name and @src_name.size > 0
            am_src = @opt[:am_src] || "am:src"
            if @filters
              ret << make_attr(am_src, "#@src_name|#@filters")
            else
              ret << make_attr(am_src, @src_name)
            end
          else
            if @filters
              am_filter = @opt[:am_filter] || "am:filter"
              ret << make_attr(am_filter, @filters)
            end
          end
          if @for
            am_for = @opt[:am_for] || "am:for"
            ret << make_attr(am_for, @for)
          end
          if @skipif
            am_skipif = @opt[:am_skipif] || "am:skipif"
            ret << make_attr(am_skipif, @skipif)
          end
          if @skipunless
            am_skipif = @opt[:am_skipunless] || "am:skipif"
            ret << make_attr(am_skipif, "not(#@skipunless)")
          end
          if @value
            am_v = @opt[:am_v] || "am:v"
            ret << make_attr(am_v, "HashDelegator.new($_) { {#@value} }")
          end
          ret << @attr.to_s
          if @erb
            if single_tag
              ret << "><![CDATA[<%= #{@erb} %>]]></#@tag>"
            else
              raise "not implemented"
            end
          else
            if single_tag
              ret << " />"
            else
              ret << " >"
            end
          end
          ret
        end

        def make_attr(key, val)
          if val.include?(?')
            " #{key}=\"#{val}\""
          else
            " #{key}='#{val}'"
          end
        end
      end

      class LineProcessor
        attr_reader :cells

        def initialize(&block)
          @cells = []
          @cell_proc = block
        end

        def parse_line(line)
          case line
          when %r[^\s*#]
            true
          when %r[^\s*\|\s*([\w:]*)\s*\|\|]
            parse_cells($1.strip, Regexp.last_match.post_match)
            true
          else
            false
          end
        end

        def parse_cells(attr_key, cells)
          attr_key = :text if attr_key == ""
          cnt = 0
          splited = ""
          cells.chomp.scan(%r[([^\|]+?)((?!r\|)\|(\|?)|$)]) do |s, th_flag|
            if s[-1] == ?\\
              splited = s[0..-2] + "|"
              next
              else
              s = splited.to_s + s
              splited = ""
            end

            @cells[cnt] ||= {}
            cell = @cells[cnt]
            if cell[attr_key]
              cell[attr_key] << "\n" << s
            else
              cell[attr_key] = s
            end

            cell[:tag] = (th_flag == "||" ? :th : :td)
            cnt += 1
          end
        end

        def get_result_xml
          @cells.collect do |c|
            tag, text = c[:tag], @cell_proc.call(c[:text])
            c.delete(:tag)
            c.delete(:text)
            if c.size == 0
              if text == nil or text.strip == ""
                ""
              else
                "<#{tag}>#{text}</#{tag}>"
              end
            else
              a = attr = c.collect do |k, v|
                next unless v
                v.strip!
                next if v == ""
                if v.include?(?')
                  "#{k}=\"#{v}\""
                else
                  "#{k}='#{v}'"
                end
              end.join(" ")
              "<#{tag} #{a}>#{text}</#{tag}>"
            end
          end.join("")
        end
      end
    end

    class Hook
      attr_reader :stream, :cnt, :element_s

      def initialize(&block)
        @hook_proc = block
      end

      def call(stream, cnt, element_s,&block)
        @stream = stream
        @cnt = cnt
        @element_s = element_s
        @render_me_proc = block
        instance_eval(&@hook_proc)
      end

      def render_me_with(it)
        stream << @render_me_proc.call(nil, it)
      end

      def render_child(name, it)
        stream << @render_me_proc.call(name, it)
      end

      class Renderer < Renderers::Base # :nodoc: all
        def initialize
          super("Hook")
        end

        def generate_body(cg, de, element)
          cg.code("e = #{ cg.define_constant(element.to_s.inspect)}")
          cg.code("$_.call(__stream__, __cnt__, e) do |name, it| ")
          cg.level_up do
            cg.case_('name') do
              cg.when_("nil") do
                cg.code("render_with(it, __binding__)")
              end
              de.children.each do |c|
                next unless c.kind_of?(DynamicElement)
                cg.when_(c.name.intern.inspect) do
                  cg.code("#{c.instance_name}.render_with(it, __binding__)")
                end
              end
            end
          end
          cg.code("end")
        end
      end
    end
  end

  module Renderers # :nodoc: all
    class Base
      def initialize(type_name)
        @type_name = type_name
      end

      def generate_when(cg)
        cg.when_(@type_name) do
          yield
        end
      end
    end

    class ScalarRenderer < Renderers::Base
      def initialize
        super("ScalarData")
      end

      def generate_body(cg, de, element)
        de.element_render_code(cg, element) do
          cg.put_string_expression("$_.amrita_value")
        end
      end
    end

    class NilRenderer < Base
      def initialize
        super("nil")
      end

      def generate_body(cg, de, element)
      end
    end

    class TrueRenderer < Base
      def initialize
        super("true")
      end

      def generate_body(cg, de, element)
        cg.put_hpricot_node(element)
      end
    end

    class HashRenderer < Base
      def initialize
        super("DictionaryData")
      end


      def generate_body(cg, de, element)
        de.element_render_code(cg, element) do
          de.children.each do |c|
            c.render_me(cg)
          end
        end
      end
    end

    class ElseRenderer < Base
      def initialize
        super("Object")
      end

      def generate_when(cg)
        cg.else_ do
          yield
        end
      end

      def generate_body_new(cg, de, element)
        de.element_render_code(cg, element) do
          de.children.each do |c|
            c.render_me(cg)
          end
        end
      end

      def generate_body(cg, de, element)
        if de.has_ruby?
          de.element_render_code(cg, element) do
            de.children.each do |c|
              c.render_me(cg)
            end
          end
        else
          cg.put_expression('raise %[type mismatch, got (#{$_.inspect}) for ' + element.to_s.inspect + ' ]')
        end
      end
    end
  end

  module Filters
    include Renderers

    class FilterArray < Array # :nodoc: all
      def initialize(*args)
        args.each { |a| self << a }
      end

      def |(other)
        case other
        when Class
          a = self + [other.new]
          FilterArray.new(*a)
        when Filters::Base
          a = self + [other]
          FilterArray.new(*a)
        when Symbol
          self + FilterArray.new(FunctionFilter.new(other))
        when Array
          case other.first
          when Symbol
            self + FilterArray.new(FunctionFilter.new(*other))
          else
            self + other
          end
        else
          raise "not filter #{other.inspect}"
        end
      end
    end

    class Base # :nodoc: all
      attr_accessor :next_
      def self.filter_method(*m)
        m.each do |name|
          module_eval <<-END
            def #{name}(*args,&block)
              next_.#{name}(*args,&block)
            end
          END
        end
      end

      FilterMethods.each do |m|
        filter_method(m)
      end

      def self.inherited(cls)
        super
        def cls.[](*args, &block)
          new(*args, &block)
        end

        def cls.|(other)
          FilterArray.new(self.new) | other
        end
      end

      def |(other)
        FilterArray.new(self) | other
      end

      def parse_filter_a(f)
        case f = eval(f)
        when Class
          f.new
        else
          f
        end
      end
    end

    class Attr < Base
      class Renderer < Renderers::Base
        include Util
        include OptionSupport
        def initialize(*args)
          parse_opt(*args)
        end

        def generate_body(cg, de, element)
          use_body = (not de.has_dynamic?) && @opt.has_key?(:body)
          body_key = nil
          if use_body
            body_key = @opt[:body]
            body_key = :body unless body_key.kind_of?(Symbol)
            cg.code("body = $_.amrita_value(#{body_key.inspect})")
          end
          if @opt.size > 0
            cg.code("a = {}")
            @opt.each do |key, v|
              next if key == :body
              if element.get_attribute(key)
                cg.case_("v = $_.amrita_value(#{v.inspect})") do
                  cg.when_("true") do
                    cg.code("a[#{key.inspect}] = #{element.get_attribute(key).inspect}")
                  end
                  cg.when_("false, nil") do
                    cg.code("a.delete(#{key.inspect})")
                  end
                  cg.else_ do
                    cg.code("a[#{key.inspect}] = $_.amrita_value(#{v.inspect})")
                  end
                end
              else
                cg.code("v = $_.amrita_value(#{v.inspect})")
                cg.code("a[#{key.inspect}] = v if v")
              end
            end
          else
            cg.code("a = $_ ")
          end

          a = { }
          element.attributes.each do |k, v|
            next if @opt[k.intern]
            a[k] = v
          end
          cg.code("e = new_element(#{element.name.inspect}, #{a.inspect}.merge(a))")
          if use_body
            body_for_static(cg)
          else
            body_for_dynamic(cg, de, element)
          end
        end

        def body_for_static(cg)
          cg.if_("body") do
            cg.put_string_expression("start_tag(e)")
            cg.put_string_expression("body.amrita_value")
            cg.put_string_expression("end_tag(e)")
            cg.else_ do
              cg.put_string_expression("e.to_s")
            end
          end
        end

        def body_for_dynamic(cg, de, element)
          cg.put_string_expression("start_tag(e)")
          de.children.each do |c|
            c.render_me(cg)
          end
          cg.put_string_expression("end_tag(e)")
        end
      end

      def initialize(*args)
        @args = args
      end

      def setup_type_renderer(de)
        de.renderers.clear
        de.renderers << Renderer.new(*@args)
      end
    end


    class Default < Base
      def initialize(default_value_src)
        @default_value_src = default_value_src
      end

      def value_filter_code(de, cg, value)
        cg.if_("$_") do
          super
          cg.else_ do
            cg.code("$_ = (#{@default_value_src.inspect})")
          end
        end
      end

      def value_filter_code_old(de, cg, value)
        cg.code("$_ = $_ || (#{@default_value_src.inspect})")
        super
      end
    end

    class Repeat < Base
      def initialize(cnt)
        @cnt = cnt.to_i
      end

      def value_filter_code(de, cg, value)
        cg.code("$_ = $_ * #@cnt  ")
        super
      end
    end

    class Format < Base
      def initialize(format)
        @format = format
      end
      def value_filter_code(de, cg, value)
        cg.code("$_ = #{@format.inspect} % $_")
        super
      end
    end

    class AcceptData < Base
      include Renderers
      def initialize(*types)
        @types = types
      end

      def setup_type_renderer(de)
        nil_processed = true_processed = hook_processed = false
        @types.each do |t|
          case t
          when nil, NilClass
            de.renderers.unshift NilRenderer.new unless nil_processed
            nil_processed = true
          when true, TrueClass
            de.renderers << TrueRenderer.new unless true_processed
            true_processed = true
          when :hook
            de.renderers << Core::Hook::Renderer.new unless hook_processed
            hook_processed = true
          else
            raise ArgumentError,"unknown type for AcceptData #{t.inspect}"
          end
        end
        super
      end
    end

    class NoSanitize < Base
      def value_filter_code(de, cg, value)
        super
        cg.code("$_ = SanitizedString[$_]")
      end
    end

    class Each < Base
      include Util::OptionSupport
      def initialize(*args)
        parse_opt(*args)
      end

      def value_filter_code(de, cg, value)
        var = "__cnt__#{de.class_name}"
        @opt.each do |k,va|
          cg.code("#{var} = __cnt__%#{va.size}")
          va.each_with_index do |v, n|
            case v
            when Symbol
              cg.code("$_[#{k.inspect}] = $_[#{v.inspect}] if #{var} == #{n}")
            when String
              cg.code("$_[#{k.inspect}] = #{v.inspect} if #{var} == #{n}")
            else
              raise("unknown Each type #{v.inspect}")
            end
          end
        end
        super
      end
    end

    class ToHash < Base
      include Util::OptionSupport
      def initialize(key)
        @key = key
      end

      def value_filter_code(de, cg, value)
        case @key
        when Symbol
          cg.code("$_ = { #{@key.inspect} => $_ }")
        when Hash
          cg.code("$_ = { ")
          @key.each do |k,v|
            cg.code(" #{k.inspect} => $_.amrita_value(#{v.inspect}),")
          end
          cg.code("}")
        end

        super
      end
    end

    class InlineRuby < Base # :nodoc: all
      include Runtime
      include Renderers
      include Util

      def setup_type_renderer(de)
        de.renderers << HashRenderer.new unless de.renderers.find {|r| r.kind_of?(HashRenderer) }
        super
      end

      def generate_dynamic_element(de, e)
        a = e.attributes
        ret = super
        return ret if ret

        # check if element has any of inlineruby attributes
        if  %w(am_skipif am_for am_v).any? { |k| a[de.__send__(k)] }
          filters_src = e.attributes[de.am_filter]
          filters = de.root.compile_filters(e, nil, filters_src)
          Core::DynamicElement.new(de, nil , e, filters)
        else
          nil
        end
      end

      def loop_check_code(de, cg)
        foreach_ = de.am_for_value
        if foreach_
          cg.if_("__cnt__ < 0") do
            #cg.code("$_ = eval(#{foreach_.inspect}, __binding__)")
            cg.code("$_ = ")
            cg.eval(foreach_.inspect)
            super
          end
        else
          super
        end
      end

      def value_filter_code(de, cg, value)
        cond = de.am_skipif_value
        value = de.am_v_value

        if cond
          cg.code("amrita_set_context_value($_)")
          #cg.code("return '' if eval(#{with_context_value_expression(cond).inspect},__binding__)")
          cg.code("return '' if \\")
          cg.eval(with_context_value_expression(cond).inspect)
        end

        if value
          cg.code("amrita_set_context_value($_)")
          #cg.code("$_ = eval(#{with_context_value_expression(value).inspect},__binding__)")
          cg.code("$_ = ")
          cg.eval(with_context_value_expression(value).inspect)
        end
        super
      end

      private
      def with_context_value_expression(src)
        [
          "$_ = amrita_get_context_value",
          "ret = (#{src})",
          "amrita_set_context_value($_)",
          "ret"
        ].join(";")
      end
    end

    module NVarMixin
      private

      def make_tupple_code(cg)
        init_code = @names.collect do |n|
          "$_.amrita_value(#{n.inspect})"
        end.join(",")
        cg.code("$_ = Tuple[#{init_code}]")
      end

      def replace_args(element)
        element.attributes.each do|k,v|
          element.set_attribute(k, replace_attr_args(v))
        end
        children = element.children.collect do |c|
          case c
          when Hpricot::Elem
            replace_args(c)
          when Hpricot::Text
            Hpricot::Text.new(replace_text_args(c.to_s))
          else
            raise "not implemented #{c.class}"
          end
        end
        element.children.clear
        children.each do |c|
          element.children << c
        end
        element
      end

      def replace_attr_args(s)
        s.gsub(/(.)?\$(\d)/) do |ss|
          if $1 == "$"
            "$#$2"
          else
            $1.to_s + '#{Util::sanitize_attribute_value($_[' + ($2.to_i-1).to_s + '].to_s)}'
          end
        end
      end

      def replace_text_args(s)
        s.gsub(/(.)?\$(\d)/) do |ss|
          if $1 == "$"
            "$#$2"
          else
            $1.to_s + '#{$_[' + ($2.to_i-1).to_s + '].amrita_value}'
          end
        end
      end
    end

    class NVar < Filters::Base
      include NVarMixin
      def initialize(*names)
        @names = names
      end

      def renderer_code(de, cg, element)
        make_tupple_code(cg) if @names.size > 0
        s = replace_args(element).to_s
        cg.put_string_expression(s.inspect.gsub(/\\#/, "#"))
      end
    end

    class Eval < NVar
      def initialize(*names)
        @names = names
      end

      private

      def make_tupple_code(cg)
        cg.code("amrita_set_context_value($_)")
        init_code = @names.collect do |n|
          case n
          when Symbol
            "$_.amrita_value(#{n.inspect})"
          else
            "eval(#{with_context_value_expression(n).inspect},__binding__)"
          end
        end.join(",")
        cg.code("$_ = Tuple[#{init_code}]")
      end

      def with_context_value_expression(src)
        [
          "$_ = amrita_get_context_value",
          "ret = (#{src})",
          "amrita_set_context_value($_)",
          "ret"
        ].join(";")
      end
    end

    class NVarForAttr < Filters::Base
      def initialize(*names)
        @names = names
      end

      def renderer_code(de, cg, element)
        make_tupple_code(cg) if @names.size > 0
        super
      end

      def element_render_code(de, cg, element, &block)
        if (element.name == 'span' && element.attributes.size == 0)
          yield
        else
          replace_args(element)
          if element.children.size > 0
            cg.put_string_expression(element.stag.output('').inspect.gsub(/\\#/, "#"))
            yield
            cg.put_constant(element.etag.output(''))
          else
            cg.put_string_expression(%["#{element.to_s}"])
          end
        end
      end

      private
      def make_tupple_code(cg)
        init_code = @names.collect do |n|
          "$_.amrita_value(#{n.inspect})"
        end.join(",")
        cg.code("a = [#{init_code}]")
      end

      def replace_args(element)
        element.attributes.each do|k,v|
          element.set_attribute(k, replace_attr_args(v))
        end
      end
      def replace_attr_args(s)
        s.gsub(/\$(\d)/) do |ss|
          '#{Util::sanitize_attribute_value(a[' + ($1.to_i-1).to_s + '].to_s)}'
        end
      end
    end

    class FunctionFilter < Filters::Base
      def initialize(sym, *args)
        @sym, @args = sym, args
      end

      def value_filter_code(de, cg, element)
        cg.code("$_ = $_.send(#{@sym.inspect}, *#{@args.inspect})")
        super
      end
    end

    class CommandFilter < Filters::Base
      def initialize(*args)
        @args = args.collect do |a|
          a.inspect
        end.join(' ')
      end

      def define_element_method(de, cg, &block)
        super do
          block.call
          cg.code "pipe = IO.popen(#@args, 'r+')"
          cg.code "pipe.write __stream__; pipe.close_write"
          cg.code "__stream__ = pipe.read ; pipe.close "
        end
      end
    end

    class Trace < Filters::Base
      SEP = "\n" + "-" * 80 + "\n"
      def filter_element(de, element)
        if de.tracer
          old_s = element.to_s
          new_element = super
          new_s = new_element.to_s
          if old_s != new_s
            de.tracer << SEP << "before filter:\n" <<
              old_s << SEP << "after filter :\n" << new_s <<  SEP
          end
          new_element
        else
          super
        end
      end

      def value_filter_code(de, cg, value)
        s = de.element.stag.output("")
        cg.code(%[Tracer << "%s:%s:%s" % [#{s.inspect},"in", $_.inspect] ])
        super
      end

      def method_body_code(de, cg)
        super
        s = de.element.stag.output("")
        cg.code(%[Tracer << "%s:%s:%s" % [#{s.inspect},"out", __stream__] ])
      end
    end

    class Join < Base
      def initialize(sep)
        @sep = case sep
               when :br ; "<br />"
               when :nbsp ; "&#160;"
               else ; sep
               end
      end

      def filter_element(de, e)
        a = e.children.dup
        a.each do |n|
          case n
          when Hpricot::CData, Hpricot::Elem
            e.insert_after(Hpricot::Text.new(@sep), n) unless n == a.last
          when Hpricot::Text
            e.insert_after(Hpricot::Text.new(@sep), n) unless n == a.last
            n.content.strip!
            n.content.gsub!(/\s*\n\s*/, @sep)
          end
        end
        e
      end
    end

    class ChangeStream < Base
      def initialize(name)
        @name = name
      end

      def method_body_code(de, cg)
        cg.code("#ChangeStream")
        cg.with_stream(@name) do
          super
        end
      end
    end

    class PutStream < Base
      def initialize(name)
        @name = name
      end

      def method_body_code(de, cg)
        cg.code("#PutStream")
        cg.put_stream(@name)
      end
    end

    class ModuleExtendFilter< Base
      def initialize(mod)
        @mod = mod
      end

      def value_filter_code(de, cg, element)
        cg.code("$_.extend(#@mod)")
        super
      end
    end
  end

  class HashDelegator
    include DictionaryData
    def initialize(parent, &block)
      @parent = parent
      @added_data = block.call
      raise 'block did not return a Hash' unless @added_data.kind_of?(DictionaryData)
    end

    def [](key)
      self.send(key)
    rescue NoMethodError
      nil
    end

    def id
      @parent.id
    end

    def method_missing(sym, *args, &block)
      if @added_data.has_key?(sym)
        @added_data[sym]
      else
        if @parent.kind_of?(DictionaryData)
          @parent.amrita_value(sym)
        else
          @parent.send(sym)
        end
      end
    end
  end

  SanitizedString = Util::SanitizedString
  Template = Core::Template
  Hooki = Core::Hook
end
