
module ConfigParser
  class Section
    attr_reader :name
    def initialize( section_name )
      @name = section_name
      @items = Hash.new
    end
    def has_item?( item_name )
      @items.has_key?( item_name )
    end
    def item( item_name )
      @items[ item_name ]
    end
    def add_item( item_name, item_value )
      @items[ item_name ] = item_value
    end
    def size
      @items.size
    end
  end

  class Configuration
    def initialize
      @sections = Hash.new
    end
    def has_section?( section_name )
      @sections.has_key?( section_name )
    end
    def has_item?( section_name, item_name )
      @sections.has_key?( section_name ) && @sections[section_name].has_item?( item_name )
    end
    def section( section_name )
      @sections[ section_name ]
    end
    def sections
      @sections.values
    end
    def add_section( section_name )
      @sections[section_name] = Section.new( section_name )
    end
    def empty?
      @sections.empty?
    end
  end
  
  def self.parse( io )
    config = Configuration.new
    
    last_section = nil
    last_value = nil
    io.each do |line|
      case line
      when /^\s*((#|;).*)?$/
        next
      when /^\[(.+?)\]$/
        last_section = config.add_section($1)
      when /^(\S+?)\s*=\s*$/
        last_value = last_section.add_item( $1, '' ) if last_section
      when /^(\S+?)\s*=\s*(.+?)\s*$/
        last_value = last_section.add_item($1, $2 ) if last_section
      when /^\s+(.+?)\s*$/
        last_value << (last_value.empty? ? '' : ' ') << $1 if last_value
      else
        puts line
      end
    end
    return config
  end
  
  def self.get_svn_version
    version = nil
    File.open( File.join( File.dirname(__FILE__), ['..']*5, 'subversion',  'include',  'svn_version.h' ) ) do |f|
      f.find{|l| version = $1 if /^\s*#\s*define\s+SVN_VER_MAJOR\s+(\d+)/ =~ l }
    end
    $1
  end
end

if( __FILE__ == $0 )
  require 'test/unit'
    
  class ConfigParserTest < Test::Unit::TestCase

    def test_empty
      cfg = ConfigParser.parse( <<-EOF )
      EOF
      assert cfg.empty?
    end

    def test_ignore_not_in_section
      cfg = ConfigParser.parse( <<-EOF )
test1 = 1
test2 = two hundred
      EOF
      assert cfg.empty?
    end

    def test_sections
      cfg = ConfigParser.parse( <<-EOF )
test1 = eins
[section1]
test1 = 1
test2 = two hundred
[section2]
test1 = one
test2 = 2 cents
test3 = drei
      EOF
      assert cfg.has_section?('section1')
      assert cfg.has_section?('section2')
      assert_equal 2, cfg.sections.size
      
      assert_equal 2, cfg.section('section1').size
      assert_equal '1', cfg.section('section1').item('test1')
      assert_equal 'two hundred', cfg.section('section1').item('test2')
      assert cfg.has_item?('section1', 'test2')
      
      section2 = cfg.section('section2')
      assert_equal 'section2', section2.name
      assert_equal 3, section2.size
      assert_equal 'one', section2.item('test1')
      assert_equal '2 cents', section2.item('test2')
      assert_equal 'drei', section2.item('test3')
    end

    def test_multiline
      cfg = ConfigParser.parse( <<-EOF )
test1 =
   eins
   zwei
[section1]
test1 =
   1
   2
test2 = 
   two
   thousand and 1
  EOF

      assert_equal 2, cfg.section('section1').size
      assert_equal '1 2', cfg.section('section1').item('test1')
      assert_equal 'two thousand and 1', cfg.section('section1').item('test2')
    end

    def test_comments
      cfg = ConfigParser.parse( <<-EOF )
# this is a first line comment
test1 =
   eins
   
   zwei
   # this is a comment
[section1]
;this too
test1 = 1
   2
   
test2 =  two thousand and 1
# more comments and blanks

  EOF

      assert cfg.has_section?('section1')
      assert_equal 2, cfg.section('section1').size
      assert_equal '1 2', cfg.section('section1').item('test1')
      assert_equal 'two thousand and 1', cfg.section('section1').item('test2')
    end
    
    def test_version
      assert_equal '1', ConfigParser.get_svn_version
    end
  end
end
