<%

###
### $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) %>