<%

    ###
    ### $Rev$
    ### $Release: 0.7.2 $
    ### copyright(c) 2005-2010 kuwata-lab all rights reserved.
    ###

    require 'fileutils'

    if @describe
      sb = []
      sb << "  --package=name        :  package name\n"
      sb << "  --extends=name        :  class name to extend\n"
      sb << "  --implements=name,... :  interface names to implement\n"
      sb << "  --dir=path            :  directory to locate output file\n"
      sb << "  --basedir=path        :  base directory to locate output file\n"
      sb << "  --constructor=false   :  not print initialize() method\n"
      #sb << "  --ggap                :  use generation gap pattern\n"
      return sb.join
    end


    def java_type(rulehash)
      return rulehash['class'] if rulehash['class']
      imports = []
      case rulehash['type']
      when nil                 ;  return 'String'
      when 'str', 'text'       ;  return 'String'
      when 'char'              ;  return 'char'
      when 'int'               ;  return 'int'
      when 'float'             ;  return 'float'
      when 'bool'              ;  return 'boolean'
      when 'date'              ;  return 'Date'
      when 'timestamp'         ;  return 'Date'
      when 'seq'               ;  return 'List'
      when 'map'               ;  return 'Map'
      when 'any'               ;  return 'Object'
      else
        raise "*** genclass-java.eruby: '#{rulehash['type']}': unknown type.\n"
      end
    end


    #def primitive?(type)
    #   @primitives ||= %w[int char float boolean double long byte]
    #   return @primitives.include?(type)
    #end


    def camelize(str, all=true)
      s = str.split(/[^\w]/).collect { |w| w.capitalize }.join()
      s[0,1] = s[0].chr.lower unless all
      return s
    end


    def write_file(classname, classdef)
      if @properties[:dir]
        dir = @properties[:dir]
      else
        basedir = @properties[:basedir] || '.'
        dir = basedir.dup
        dir << '/' << @properties[:package].gsub(/\./, '/') if @properties[:package]
      end
      FileUtils.mkdir_p(dir) unless test(?d, dir)
      filename = "#{dir}/#{classname}.java"
      print "generating #{filename}..."
      File.open(filename, 'w') { |f| f.write(classdef) }
      print "done.\n"
    end


    def generate_classdef(schema)
      hash = Kwalify::Util::OrderedHash.new
      Kwalify::Util.traverse_schema(schema) do |rulehash|
        if rulehash['class']
          classdef = _generate(rulehash)
          classname = rulehash['class']
          hash[classname] = classdef if !hash[classname] || classdef.length > hash[classname].length
        end
      end
      hash.each do |classname, classdef|
        write_file(classname, classdef)
      end
      print ""
    end


    def _generate(rulehash)
      classname = rulehash['class']
      return unless classname
      assert unless rulehash['mapping']

      ## preamble
      schema_filename = @properties[:schema_filename]
      sb = ''
      sb       << "// generated by kwalify from #{schema_filename}\n"
      sb       << "\n"

      ## package and import classes
      package = @properties[:package]
      sb       << "package #{package};\n" if package
      sb       << "import java.util.*;\n"
      sb       << "\n"

      ## start class declaration
      extends = @properties[:extends] ? " extends #{@properties[:extends] % classname}" : nil
      implements = @properties[:implements] ? " implements #{@properties[:implements]}" : nil
      classname = classname + '_' if @properties[:ggap]
      sb        << "/**\n"
      sb        << " *  #{rulehash['desc']}\n"
      sb        << " */\n"
      sb        << "public class #{classname}#{extends}#{implements} {\n"
      sb        << "\n"

      ## instance variables
      rulehash['mapping'].each do |name, map_rulehash|
        next unless name =~ /\A[a-zA-Z_][-\w]*\z/
        name2 = name.gsub(/-/, '_')
        type = java_type(map_rulehash)
        if map_rulehash['default'].nil?
          sb    << "    private #{type} _#{name2};\n"
        else
          sb    << "    private #{type} _#{name2} = #{map_rulehash['default'].inspect};\n"
        end
      end
      sb        << "\n"

      ## constructors
      if @properties[:constructor] != false
        sb      << _generate_constructor(rulehash)
        sb      << "\n"
      end #if

      ## setter/getter
      rulehash['mapping'].each do |name, map_rulehash|
        next unless name =~ /\A[a-zA-Z_][-\w]*\z/
        name2 = name.gsub(/-/, '_')
        type = java_type(map_rulehash)
        get = type == 'boolean' ? 'is' : 'get'
        sb      << "    public #{type} #{get}#{camelize(name)}() { return _#{name2}; }\n"
        sb      << "    public void set#{camelize(name)}(#{type} #{name2}_) { _#{name2} = #{name2}_; }\n"
      end

      ## end of class declaration
      if @properties[:ggap]
        sb      << "}\n"
        sb      << "public class #{rulehash['class']} < #{classname}\n"
      end
      sb        << "}\n"

      ##
      classdef = sb
      return classdef
    end


    def _generate_constructor(rulehash)
      classname = rulehash['class']
      sb = ''
      ## default constructor
      sb        << "    public #{classname}() {}\n"
      sb        << "\n"
      ## constructor
      sb        << "    public #{classname}(Map map) {\n"
      objdecl   =  "        Object obj;\n"
      seqdecl   =  "        List seq;\n"
      rulehash['mapping'].each do |name, map_rulehash|
        next unless name =~ /\A[a-zA-Z_][-\w]*\z/
        name2 = name.gsub(/-/, '_')
        if map_rulehash['class']
          cname = map_rulehash['class']
          type  = java_type(map_rulehash)
          sb    << objdecl;  objdecl = ''
          sb    << "        if ((obj = map.get(\"#{name}\")) != null && obj instanceof Map) {\n"
          sb    << "            _%-8s = new #{cname}((Map)obj);\n" % name2
          sb    << "        } else {\n"
          sb    << "            _%-8s = (#{type})obj;\n" % name2
          sb    << "        }\n"
        elsif map_rulehash['sequence'] && map_rulehash['sequence'][0]['class']
          item_rulehash = map_rulehash['sequence'][0]
          cname = item_rulehash['class']
          type  = java_type(item_rulehash)
          sb    << seqdecl;  seqdecl = ''
          sb    << objdecl;  objdecl = ''
          sb    << "        if ((seq = (List)map.get(\"#{name}\")) != null) {\n"
          sb    << "            for (int i = 0; i < seq.size(); i++) {\n"
          sb    << "                if ((obj = seq.get(i)) instanceof Map) {\n"
          sb    << "                    seq.set(i, new #{cname}((Map)obj));\n"
          sb    << "                }\n"
          sb    << "            }\n"
          sb    << "        }\n"
          sb    << "        _%-12s = seq;\n" % name2
        else
          type = java_type(map_rulehash)
          @wrapper_classes ||= {
            'int'   => 'Number',  'short'  => 'Number',
            'float' => 'Number',  'double' => 'Number',
            'long'  => 'Number',  'byte'   => 'Number',
            'char'  => 'Character',
            'boolean'  => 'Boolean',
          }
          if (wrapper = @wrapper_classes[type])
            sb  << "        if (map.get(\"#{name}\") != null) {\n"
            sb  << "            _%-8s = ((#{wrapper})map.get(\"#{name}\")).#{type}Value();\n" % name2
            sb  << "        }\n"
          elsif map_rulehash.key?('default')
            sb  << "        if (map.get(\"#{name}\") != null)\n"
            sb  << "            _%-8s = (#{type})map.get(\"#{name}\");\n" % name2
          else
            sb  << "        _%-12s = (#{type})map.get(\"#{name}\");\n" % name2
          end
        end #if
      end #each
      sb        << "    }\n"
      return sb
    end


%>
<%= generate_classdef(@schema) %>
