require 'cgi'
require 'rexml/document'

class Servlet

  attr_reader :name, :displayName, :className, :mappings

  def initialize(name, displayName, className)
    @name = name
    @displayName = name
    @className = className
    @mappings = []
  end

  def add_mapping (mappingE)
    @mappings.push mappingE
  end
end

class Mindmap

  def initialize(goal)
    @nextId = 1
    @mindmap = REXML::Document.new
    @mindmapE = REXML::Element.new('map')
    @mindmapE.add_attribute 'version', '0.8.0'
    @mindmap.add_element @mindmapE
    @current = nil
    open(goal)
    @mindmapE.add_element @current
  end

  def open(text)
    node = REXML::Element.new('node')
    id = "Webxml_node_#{@nextId}"
    @nextId += 1
    node.add_attributes({ 'ID' => id, 'text' => text })
    @current.add_element node unless @current.nil?
    @current = node
    return self
  end

  def close
    @current = @current.parent
    return self
  end

  def add(text)
    open(text).close
  end

  def font(size)
    fontE = REXML::Element.new('font')
    fontE.add_attributes({ 'NAME' => 'SansSerif', 'SIZE' => size })
    @current.add_element fontE
    return self
  end

  def fold
    @current.add_attribute 'FOLDED', 'true'
    return self
  end

  def write_to(xmlPath)
    out = File.new(xmlPath, File::CREAT|File::TRUNC|File::RDWR)
    begin
      @mindmap.write(out, 2)
    ensure
      out.close
    end
  end

end

class Convertor

  def initialize(webxmlPath, mindmapPath)
    @webxmlPath = webxmlPath
    @mindmapPath = mindmapPath
    @webxml = REXML::Document.new File.open(@webxmlPath)
    @webappE = @webxml.root
    @filterByName = {}
    @listeners = []
    @servletByName = {}
  end

  def execute
    @appname = get_option(@webappE, 'display-name') || 'web.xml'
    REXML::XPath.each(@webappE, 'filter') { |filterE|
      @filterByName[get_option(filterE, 'filter-name')] = get_option(filterE, 'filter-class')
    }
    REXML::XPath.each(@webappE, 'listener') { |listenerE|
      @listeners.push get_option(listenerE, 'listener-class')
    }
    REXML::XPath.each(@webappE, 'servlet') { |servletE|
      name = get_option(servletE, 'servlet-name')
      displayName = get_option(servletE, 'display-name')
      className = get_option(servletE, 'servlet-class')
      @servletByName[name] = Servlet.new(name, displayName, className)
    }
    REXML::XPath.each(@webappE, 'servlet-mapping') { |mappingE|
      name = get_option(mappingE, 'servlet-name')
      servlet = @servletByName[name]
      servlet.add_mapping "#{mappingE}"
    }

    @mindmap = Mindmap.new(@appname)
    write_filters
    write_listeners
    write_servlets
    @mindmap.write_to @mindmapPath
  end

  def write_filters
    @mindmap.open('Filters').font(18)
    @filterByName.each { |name, className|
      @mindmap.open(name).fold.
        add(className).
      close
    }
    @mindmap.close
  end

  def write_listeners
    @mindmap.open('Listeners').font(18)
    @listeners.each { |className|
      @mindmap.add(className)
    }
    @mindmap.close
  end

  def write_servlets
    @mindmap.open('Servlets').font(18)
    @servletByName.each { |name, servlet|
      @mindmap.open(name).fold.
        add(servlet.displayName).
        add(servlet.className)
      @mindmap.open('Mappings')
      servlet.mappings.each { |xmlText|
        escaped = CGI::escapeHTML(CGI::escapeHTML(xmlText))
        @mindmap.add("<html><pre>#{escaped}</pre>")
      }
      @mindmap.close.close
    }
    @mindmap.close
  end

  def get_option(parentE, name)
    optionE = REXML::XPath.first(parentE, name)
    return optionE.nil? ? nil : optionE.text
  end

end

convertor = Convertor.new("web.xml", "web-xml.mm")
convertor.execute