add new codesys support
[chise/ruby.git] / src / chise.rb
index cb547a5..f14b59f 100755 (executable)
@@ -1,13 +1,17 @@
-#!c:/ruby/bin/ruby.exe
-# Ruby/UTF-2000 module by eto 2002-1114
+#!/usr/bin/env ruby
+# Ruby/CHISE module by eto 2002-1114
 
 require 'bdb'
 require 'uconv'
 require 'singleton'
+require 'rbchise'
+require 'db'
+require 'ids'
 
 $KCODE = 'u' #今のところこれ以外では動かない。String.splitが影響大。inspectも影響。
+$debug = false #これはテスト用
 $debug = true #これはテスト用
-#$stdout.binmode if $debug
+$stdout.binmode if $debug
 $stdout.sync = true if $debug
 
 class String #======================================================================
@@ -18,8 +22,8 @@ class String #==================================================================
   def char_at(n) to_a()[n] end
   def char() Character.get(to_a[0]) end
   #alias to_c char #悩み中
-  def char_id() char.char_id() end
-  def get_char_attribute(a) char.get_char_attribute(a) end
+  #def char_id() char.char_id() end #なんとなく廃止
+  #def get_char_attribute(a) char.get_char_attribute(a) end #なんとなく廃止
   #def ucs() char.ucs() end
   def to_utf8()
     return to_a.map {|ch|
@@ -27,6 +31,23 @@ class String #==================================================================
     }.join('')
   end
 
+  def map_char(block = Proc.new)
+    return unless block_given?
+    return self.to_a.map {|ch| (block.call(ch)).to_s }.join("")
+  end
+  def map_char!(block = Proc.new)
+    return unless block_given?
+    self.replace(self.map_char {|ch| block.call(ch)})
+  end
+  def map_character(block = Proc.new)
+    return unless block_given?
+    return self.to_a.map {|ch| (block.call(ch.char)).to_s }.join("")
+  end
+  def map_character!(block = Proc.new)
+    return unless block_given?
+    self.replace(self.map_char {|ch| block.call(ch.char)})
+  end
+
   def method_missing(mid, *args)
     if char_length == 1 #省略形が有効なのは、一文字の時だけ
       char.method_missing(mid, *args)
@@ -50,17 +71,14 @@ class String #==================================================================
   end
   def de_er() return self.dup.de_er!; end
 
-  def map_char(block = Proc.new)
-    return unless block_given?
-    return self.to_a.map {|ch| (block.call(ch)).to_s }.join("")
-  end
-  def map_char!(block = Proc.new)
-    return unless block_given?
-    self.replace(self.map_char {|ch| block.call(ch)})
-  end
   def inspect_all() map_char {|ch| ch.char.inspect_all } end
   def inspect_x()   map_char {|ch| ch.char.inspect_x   } end
 
+  def to_euc()   map_char {|ch| ch.char.to_euc   } end
+  def map_euc()  map_char {|ch| ch.char.map_euc  } end
+  def to_sjis()  map_char {|ch| ch.char.to_sjis  } end
+  def map_sjis() map_char {|ch| ch.char.map_sjis } end
+
   def decompose() map_char {|ch| ch.char.decompose } end
   def decompose!() self.replace(self.decompose); self; end
   def decompose_all_nu(level=nil)
@@ -92,12 +110,12 @@ class String #==================================================================
     }
     str = ""
     h.each {|k, v|
-#      p [k, v]
+      #      p [k, v]
       if length == v #全部に顔を出していたら
        str += k
       end
     }
-#    p str
+    #    p str
     str
   end
   def compose()
@@ -113,15 +131,15 @@ class String #==================================================================
     return "" #attributeを持つものが一つも無かったら、""にする
   end
   def aggregate()
-#selfである文字列をIDSだと仮定し、それを完全にcomposeしきらないで、
-#その部分集合だけをとりだして、compose可能であればできるだけcomposeする。
+    #selfである文字列をIDSだと仮定し、それを完全にcomposeしきらないで、
+    #その部分集合だけをとりだして、compose可能であればできるだけcomposeする。
     tree = IDS_Tree.new(self)
     return self if tree.depth <= 1 #sub_nodesが無い場合はここでさよなら
     tree.sub_nodes.each {|node|
       c = node.compose
       next if c.nil? || c == ""
-#      print "#{self}  #{node} #{c}\n"
-#      p [self, node, c]
+      #      print "#{self}    #{node} #{c}\n"
+      #      p [self, node, c]
       n = self.gsub(node, c)
       return n.aggregate
     }
@@ -136,34 +154,60 @@ module CHISE #==================================================================
   module_function :windows?
   if windows?()
     DB_DIR = 'd:/work/chise/char-db' #この後に/sysmtem-char-id/ucsという感じに続く
+    IDS_DB_DIR = 'd:/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
   else
-    DB_DIR = '/usr/local/lib/xemacs-21.4.10/i686-pc-linux/char-db' #この後に/sysmtem-char-id/ucsという感じに続く
+    DB_DIR = '/usr/local/lib/xemacs-21.4.12/i686-pc-linux/char-db' #この後に/sysmtem-char-id/ucsという感じに続く
+    IDS_DB_DIR = '/home/eto/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
   end
 
   class EntityReference #======================================================================
     #状況によってどのERに変換するかが異なる可能性があるので、普通のclassとして実装したほうがいい?
+#    CODESYS_TABLE = [
+#      %w( chinese-big5-cdp    CDP- 4 X),
+#      %w( ideograph-daikanwa  M-   5 d),
+#      %w( ideograph-cbeta     CB   5 d),
+#      %w( ideograph-gt                GT-  5 d),
+#      %w( ideograph-gt-k      GT-K 5 d),
+#      %w( japanese-jisx0208-1990 J90- 4 X),
+#      %w( japanese-jisx0208   J83- 4 X),
+#      %w( japanese-jisx0213-1 JX1- 4 X),
+#      %w( japanese-jisx0213-2 JX2- 4 X),
+#      %w( japanese-jisx0212   JSP- 4 X),
+#      %w( japanese-jisx0208-1978 J78- 4 X),
+#      %w( chinese-cns11643-1  C1-  4 X),
+#      %w( chinese-cns11643-2  C2-  4 X),
+#      %w( chinese-cns11643-3  C3-  4 X),
+#      %w( chinese-cns11643-4  C4-  4 X),
+#      %w( chinese-cns11643-5  C5-  4 X),
+#      %w( chinese-cns11643-6  C6-  4 X),
+#      %w( chinese-cns11643-7  C7-  4 X),
+#      %w( korean-ksc5601      K0- 4 X),
+#    ]
+#    CODESYS_ORDER = %w(japanese chinese korean ideograph)
     CODESYS_TABLE = [
-      %w( chinese-big5-cdp     CDP- 4 X),
-      %w( ideograph-daikanwa   M-   5 d),
-      %w( ideograph-cbeta      CB   5 d),
-      %w( ideograph-gt         GT-  5 d),
-      %w( ideograph-gt-k       GT-K 5 d),
-      %w( japanese-jisx0208-1990 J90- 4 X),
-      %w( japanese-jisx0208    J83- 4 X),
-      %w( japanese-jisx0213-1  JX1- 4 X),
-      %w( japanese-jisx0213-2  JX2- 4 X),
-      %w( japanese-jisx0212    JSP- 4 X),
-      %w( japanese-jisx0208-1978 J78- 4 X),
-      %w( chinese-cns11643-1   C1-  4 X),
-      %w( chinese-cns11643-2   C2-  4 X),
-      %w( chinese-cns11643-3   C3-  4 X),
-      %w( chinese-cns11643-4   C4-  4 X),
-      %w( chinese-cns11643-5   C5-  4 X),
-      %w( chinese-cns11643-6   C6-  4 X),
-      %w( chinese-cns11643-7   C7-  4 X),
-      %w( korean-ksc5601       K0- 4 X),
+      %w( =jis-x0208-1990      J90- 4 X),
+      %w( =jis-x0208-1983      J83- 4 X),
+      %w( =jis-x0208-1978      J78- 4 X),
+      %w( =jis-x0208           J90- 4 X), #継承のアドホックな実装
+      %w( =jis-x0208           J83- 4 X), #継承のアドホックな実装
+      %w( =jis-x0208           J78- 4 X), #継承のアドホックな実装
+      %w( =jis-x0213-1-2000    JX1- 4 X),
+      %w( =jis-x0213-2-2000    JX2- 4 X),
+      %w( =jis-x0212           JSP- 4 X),
+      %w( =big5-cdp            CDP- 4 X),
+      %w( =cns11643-1          C1-  4 X),
+      %w( =cns11643-2          C2-  4 X),
+      %w( =cns11643-3          C3-  4 X),
+      %w( =cns11643-4          C4-  4 X),
+      %w( =cns11643-5          C5-  4 X),
+      %w( =cns11643-6          C6-  4 X),
+      %w( =cns11643-7          C7-  4 X),
+      %w( =ks-x1001            K0-  4 X),
+      %w( =daikanwa            M-   5 d),
+      %w( =cbeta               CB   5 d),
+      %w( =gt                  GT-  5 d),
+      %w( =gt-k                        GT-K 5 d),
     ]
-    CODESYS_ORDER = %w(japanese chinese korean ideograph)
     REGEXP_PART = "&([-+0-9A-Za-z#]+);"
     REGEXP_ALL = "^#{REGEXP_PART}$"
 
@@ -173,7 +217,7 @@ module CHISE #==================================================================
       return "" unless er =~ Regexp.new(REGEXP_ALL) #なんか、間違ってる?
       er = $1 #ついでに中身の部分を取り出す
       return $1.hex if er =~ /^MCS-([0-9A-Fa-f]+)/ #MCS
-#      if er =~ /^U[-+]?([0-9A-Fa-f]+)/ #Unicode直打ち
+      #      if er =~ /^U[-+]?([0-9A-Fa-f]+)/ #Unicode直打ち
       if er =~ /^U[-+]?([0-9A-Fa-f]+)/ || er =~ /^#x([0-9A-Fa-f]+)/ #Unicode直打ち
        return $1.hex 
       end
@@ -182,13 +226,14 @@ module CHISE #==================================================================
       each_codesys {|codesys, er_prefix, keta, numtype|        #p [codesys, er_prefix, keta, numtype]
        numtyperegex = '\d' #if numtype == 'd'
        numtyperegex = '[0-9A-Fa-f]' if numtype == 'X'
-       regexpstr = "^#{er_prefix}(#{numtyperegex}{#{keta},#{keta}})$"  #p regexpstr
+       regexpstr = "^#{er_prefix}(#{numtyperegex}{#{keta},#{keta}})$"
        if er =~ Regexp.new(regexpstr)
          codestr = $1
          code = codestr.to_i #if numtype == 'd'
          code = codestr.hex if numtype == 'X'
          char_id_u8 = EntityReference.get_database(codesys, code)
          char_id_num = Character.parse_char_id(char_id_u8)
+         next if char_id_num == nil
          return char_id_num
        end
       }
@@ -196,21 +241,24 @@ module CHISE #==================================================================
     end
 
     def self.each_codesys()
-      CODESYS_ORDER.each {|lang|
-       CODESYS_TABLE.each {|codesys, er_prefix, keta, numtype| #普通こういう書き方はしない。ループ一個にする。
-         next unless codesys =~ lang
-         yield(codesys, er_prefix, keta, numtype)
-       }
+#      CODESYS_ORDER.each {|lang|
+#      CODESYS_TABLE.each {|codesys, er_prefix, keta, numtype| #普通こういう書き方はしない。ループ一個にする。
+#        next unless codesys =~ lang
+#        yield(codesys, er_prefix, keta, numtype)
+#      }
+#      }
+      CODESYS_TABLE.each {|codesys, er_prefix, keta, numtype|
+       yield(codesys, er_prefix, keta, numtype)
       }
     end
     def self.get_database(codesys, code)
       c = CodesysDB.instance.get(codesys, code)
       return c if c != nil
-      if codesys =~ /-jisx0208/
-       #return self.get_database("=jis-x0208", code) #再帰でどうだ?
-       c = CodesysDB.instance.get("=jis-x0208", code)
-       return c
-      end
+#      if codesys =~ /-jisx0208/
+#      #return self.get_database("=jis-x0208", code) #再帰でどうだ?
+#      c = CodesysDB.instance.get("=jis-x0208", code)
+#      return c
+#      end
       return nil
     end
   end
@@ -241,11 +289,6 @@ module CHISE #==================================================================
   end
 
   class Character #=============================================================== 文字オブジェクト
-    BASIC_KANJI = "人子女母父王口耳手足力目首毛心犬牛鳥貝角羽虫馬魚羊肉皮米竹木麦豆山川雨風水土石金田穴日月火音糸刀舟門戸衣矢弓車皿一二三四五六七八九十百千万寸尺上中下本玉立回食行止交向歩考入示走生出来書言大小白青多少高長"
-    def is_basic_kanji?
-      BASIC_KANJI.include?(self.to_s)
-    end
-
     def initialize(char_id=nil)
       @char_id = Character.parse_char_id(char_id)
       @attributes = Hash.new
@@ -263,6 +306,7 @@ module CHISE #==================================================================
     def normalize_attribute_name(b)
       a = b.dup
       a.gsub!(/_/, '-') #underlineは-に置換
+      a.sub!(/-at-/,  '@')
       a.sub!(/^map-/,  '=>')
       a.sub!(/^to-/,   '->')
       a.sub!(/^from-/, '<-')
@@ -272,11 +316,14 @@ module CHISE #==================================================================
       a = normalize_attribute_name(b)
       #p [a, b]
       atr = @attributes[a]
-      return atr if atr != nil
+      return atr if atr
       atr = check_database(a)
-      @attributes[a] = atr if atr != nil
-      return get_char_attribute("=jis-x0208") if a =~ /jisx0208/ #ここだけ特殊形
-      return @attributes[a]
+      if atr
+       @attributes[a] = atr
+       return atr
+      end
+      return get_char_attribute("="+a) unless a =~ /^=/ #頭に=がついてない場合はそれが省略されていることを仮定して、再帰する
+      nil
     end
     def put_char_attribute(b,v)
       a = normalize_attribute_name(b)
@@ -380,1031 +427,182 @@ module CHISE #==================================================================
       ar = %w{ucs-jis ucs =>ucs-jis}
       #並び順は恣意的で、ucs-jisを先に出している。本来はこれも指定できるようにするべき。
       ar.each {|a|     #p [a]
-        u = get_char_attribute(a)
-        return u if u != nil
-       }
-       return nil
-     end
-
-     #----------------------------------------------------------------------CCS関係
-     def to_utf8() Uconv.u4tou8(Character.u4itou4(ucs())) end #UTF8文字列を返す
-     #alias to_s to_utf8
-     alias to_s mcs_utf8
-     def map_utf8()
-       u = ucs()
-       if u.nil? || 0xffff < u
-        return to_er()
-       else
-        return to_utf8()
-       end
-     end
-     alias map_ucs map_utf8
-     def map_ucs_er()
-       u = ucs()
-       if u.nil? || 0xffff < u
-        return to_er()
-       else
-        return Character.get(u).to_er()
-       end
-     end
-     def to_euc()
-       u = ucs()
-       return "" if u.nil? || 0xffff < u
-       Uconv.u16toeuc(Uconv.u4tou16(Character.u4itou4(ucs())))
-     end
-     def map_euc()
-       e = to_euc()
-       return e if e != ""
-       return to_er()
-     end
-     def to_sjis()
-       u = ucs()
-       return "" if u.nil? || 0xffff < u
-       Uconv.u16tosjis(Uconv.u4tou16(Character.u4itou4(ucs())))
-     end
-     def map_sjis()
-       e = to_sjis()
-       return e if e != ""
-       return to_er()
-     end
-
-     #----------------------------------------------------------------------
-     def to_er(codesys=nil) #実体参照を返す、希望するcodesysが引数(未実装)
-       return "" if @char_id == nil
-       return sprintf("&#x%04x;", @char_id) if @char_id <= 0xffff
-       return sprintf("&#x%05x;", @char_id) if @char_id <= 0xfffff
-       EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
-        code = self[codesys]
-        next if code == nil
-        return sprintf("&#{er_prefix}%0#{keta}#{numtype};", code)
-       }
-       return sprintf("&MCS-%08X;", @char_id) #本当はこれは無しにしたい
-     end
-     def to_er_list()
-       ar = []
-       EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
-        er = to_er(codesys)
-        ar << er if er != nil
-       }
-       ar
-     end
-
-     def inspect_x()
-       return "<>" if @char_id == nil
-       ar = [to_utf8(), to_er().sub(/^&/,'').chop]
-       "<"+ar.join(',')+">"
-     end
-     alias inspect inspect_x
-     def inspect_all_codesys() #未完成
-       #to_erを全てのcodesysにおいて実行する。その結果をコンパクトにまとめる
-     end
-     def inspect_all()
-       ar = [inspect.chop]
-       alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
-       return ar.join(',')+">"
-     end
-     def get_attributes()
-       str = ""
-       alist.to_a.sort.each {|a, v|
-        str += "#{a}: #{v}\n"
-       }
-       str
-     end
-
-     def inspect_ids(hex_flag=false)
-       ids = decompose
-       ar = []
-       ar << (hex_flag ? "x"+mcs_hex : to_utf8)
-       if to_s != ids #idsが部品そのものだったら部品追加はしない
-        ids.each_char {|ch|
-          char = ch.char
-          next if char.is_ids?
-          if hex_flag then
-            ar << "x"+char.mcs_hex
-          else
-            u = char.to_utf8
-            if u != ""
-              ar << u
-            else
-              ar << char.to_er
-            end
-          end
-         }
-       end
-       return "("+ar.join("\t")+")"
-     end
-
-     #----------------------------------------------------------------------IDS関係
-     def decompose
-       k = self.to_s
-#       idss = self['ids']
-#       return idss if idss
-#       return k if self.is_basic_kanji? #基本漢字はstop kanjiとするぞと。
-       return self['ids-represent'] if self['ids-represent'] #ids_representを持っている場合はその値とする。
-       return self['ids-element'] if self['ids-element'] #ids_elementを持っている場合はその値とする。
-
-       idss = self['ids-meaning']
-       return idss if idss != nil && 0 < idss.length && k != idss
-       idss = self['ids-aggregated']
-       return idss if idss != nil && 0 < idss.length && k != idss
-       idss = self['ids']
-       return idss if idss != nil && 0 < idss.length && k != idss
-       return k
-#       return k if idss.nil? || idss.length == 0 || k == idss
-#       if idss.char_length == 2
-#      p ['What???', k, idss, k.inspect_all]
-#       #return idssx[1] #二個目だけ返すとか?
-#       return k #IDSに展開する方法が無いと。
-#       end
-#       return k if k == idss
-#       if idss.include?(k) #<C5-4C4D><C6-4A37>この二文字のBUG対策
-#       #return idss.sub(k, '')
-#       return k #IDSに展開する方法が無いと。
-#       end
-#       return idss
-     end
-     def decompose_all
-       pde = ""
-       de = self.decompose #出発点
-       level = 0
-       while true
-        pde = de
-        de = pde.decompose #もう一度分解をしてみる。
-        break if pde == de #ループを抜けだす
-        exit if 10 < level #p ['too many recursive', self] 
-        level += 1
-       end
-       return de
-     end
-     def decompose_all_nu(level=nil)
-       level = 0 if level.nil?
-       if 10 < level
-        p ['too many recursive', self] 
-        exit
-       end
-       de = self.decompose
-       return de.decompose_all(level+1) if de != self #なにか変化があったから再帰
-       return de #もうこれ以上変化は無さそうだぞと。
-     end
-     def is_ids?() 0x2ff0 <= @char_id && @char_id <= 0x2fff end
-     def ids_operator_argc()
-       return 0 unless is_ids?
-       return 3 if @char_id == 0x2ff2 || @char_id == 0x2ff3
-       return 2
-     end
-   end
-
-   class DBS #======================================================================複数のDBを集めたclass、未完成
-   end
-
-   class ADB < BDB::Hash #======================================================================一つのDB
-     def initialize(*args)
-       super
-       @modified = false
-       at_exit {
-        if @modified
-          self.close #これがないと、うまくデータベースがセーブされないのです。
-        end
-       }
-     end
-     def self.open_create(filename)
-       ADB.open(filename, nil, BDB::CREATE | BDB::EXCL) #上書きはしない
-     end
-     def mykey(key)
-       if key.is_a?(String)
-        if key.char_length == 1
-          return '?'+key  #Stringだったら引く前に?を足す
-        end
-       end
-       #key = key.to_s if key.is_a?(Numeric) #NumberだったらStringにする。
-       #ここで && key ! =~ /^\?/ をいれると、?自身を検索できなくなってしまう。
-       return key
-     end
-     def myvalue(v)
-       return v if v == nil
-       return v.to_i if v =~ /^\d+$/ #数字だったらここで変換しておく
-       return v.sub(/^\?/, '') if v =~ /^\?/ #冒頭の?は取り除く
-       return $1 if v =~ /^"(.+)"$/ #最初と最後に"がついていたら、取り除く
-       #p ['get', v, t, key, db]
-       #return parse_sexp(v) if v =~ /^\(.+\)$/ #最初と最後が()の時は、S式にparseする
-       return v #それ以外って何?
-     end
-     def myget(key) #keyキーを引いて返す
-       key = mykey(key)
-       v = get(key) #存在しなかったらnilを返すことになる
-       return myvalue(v)
-     end
-     def myput(key, v) #keyにvをいれる
-       key = mykey(key)
-       put(key, v) #putする
-       @modified = true
-     end
-   end
-
-   class DB #======================================================= データベース群のabstract class
-     def self.unix_to_win(unix) #Windowsファイル名制限のため、変換する
-       win = unix.gsub(/</, '(')
-       win.gsub!(/>/, ')')
-       win.gsub!(/\*/, '+')
-       win.gsub!(/\?/, '!')
-       return win
-     end
-     def self.win_to_unix(win)
-       unix = win.gsub(%r|\)|, '>')
-       unix.gsub!(%r|\(|, '<')
-       unix.gsub!(%r|!|, '?')
-       unix.gsub!(%r|\+|, '*')
-       return unix
-     end
-     def get_filename(t)
-       return @pre + DB.unix_to_win(t) + @post if windows?
-       return @pre + t + @post
-     end
-     def get_dirname(t) File.dirname(get_filename(t)) end
-     def open_dbs()
-       @dbs = Hash.new
-       keys = find_keys()
-       keys.each {|key| open_db(key) }
-     end
-     def find_keys()
-       files = []
-       Dir.glob(@glob){|f|
-        next if ! File.file?(f)
-        next if f =~ /.txt$/
-        files << f
-       }
-       keys = []
-       files.each {|f|
-        t = DB.win_to_unix(f)
-        t.sub!(%r|^#{@pre}|, '')
-        t.sub!(%r|#{@post}$|, '') if @post != ""
-        keys << t
-       }
-       return keys
-     end
-     def close_db(t)
-       db = get(t)
-       return nil if db.nil?
-       db.close
-       @dbs.delete(t)
-     end
-     def open_db(t)
-       return nil if get(t) #すでにopenしていたら再openはしない。
-       begin
-        bdb = ADB.open(get_filename(t), nil, 0)
-        @dbs[t] = bdb if bdb != nil
-       rescue
-        p ["open error", get_filename(t)]; return nil
-       end
-       return true
-     end
-     def make_db(t, h=nil) #tという名前でhという中身のデータベースを作る
-       return nil if get(t) #すでにある場合はreturn
-       Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
-       db = nil
-       begin
-        db = ADB.open_create(get_filename(t)) #上書きはしない
-        if h != nil
-          h.each {|k, v|
-            k = '?'+k if k.is_a?(String)
-            db[k] = v
-          }
-        end
-        db.close
-       rescue
-        p ["make error", get_filename(t)]; return nil
-       end
-       return true
-     end
-     def make_db_no_question_mark(t, h=nil) #tという名前でhという中身のデータベースを作る
-       return nil if get(t) #すでにある場合はreturn
-       Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
-       db = nil
-       begin
-        db = ADB.open_create(get_filename(t)) #上書きはしない
-        if h != nil
-          h.each {|k, v|
-#           k = '?'+k if k.is_a?(String)
-            db[k] = v
-          }
-        end
-        db.close
-       rescue
-        p ["make error", get_filename(t)]; return nil
-       end
-       return true
-     end
-     def remove_db(t) #tという名前のデータベースを消去する
-       db = get(t)
-       if db
-        db.close
-        @dbs.delete(t)
-       end
-       begin
-        File.unlink(get_filename(t)) if FileTest.file?(get_filename(t))
-       rescue
-        p ["unlink error", get_filename(t)]; return nil
-       end
-       dn = get_dirname(t)
-       Dir.rmdir(dn) if FileTest.directory?(dn) && Dir.entries(dn).length <= 2 #空directoryだったら消す
-       return true
-     end
-     def to_num(s)
-       return s.to_i if s =~ /^\d+$/
-       return s
-     end
-     def dump_db(t)
-       db = get(t)
-       return nil unless db
-       file = get_filename(t)
-       open("#{file}.txt", "w"){|out|
-#       out.binmode.sync = true
-        ar = db.to_a
-        ar.map! {|k, v| [to_num(k), to_num(v)] }
-        ar.sort.each {|k, v|
-          out.printf("%s\t%s\n", k, v)
-        }
-       }
-       return true
-     end
-     def each_db()  @dbs.to_a.sort.each {|t, db| yield(t, db) } end
-     def dump_all()  each_db {|t, db| dump_db(t) } end
-     def close_all() each_db {|t, db| db.close   } end
-     def keys() @dbs.keys end
-     def each(t)
-       return unless block_given?
-       db = @dbs[t]
-       return nil unless db
-       db.each {|k, v|
-        k = to_num(k)
-        v = to_num(v)
-        k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
-        vv = get(t, k) #p ['each', t, k, v, vv]
-        yield(k, vv)
-       }
-     end
-     def each_sort(t)
-       return unless block_given?
-       db = @dbs[t]
-       return nil unless db
-       ar = db.to_a
-       ar.map! {|k, v| [to_num(k), to_num(v)] }
-       ar.sort.each {|k, v|
-        k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
-        vv = get(t, k) #p ['each', t, k, v, vv]
-        yield(k, vv)
-       }
-     end
-     #----------------------------------------------------------------------
-     def get(t, key=nil) #tというデータベースのkeyキーを引いて返す
-       db = @dbs[t]
-       return db if key.nil?
-       return nil unless db
-       return db.myget(key)
-     end
-     def put(t, key, v) #tというデータベースのkeyにvをいれる
-       db = @dbs[t]
-       if db == nil
-        db = make_db(t) 
-        db = open_db(t) 
-        db = @dbs[t]
-       end
-       db.myput(key, v) #putする
-     end
-   end
-
-   class CharDB < DB #------------------------------------ MCS-UTF8をキーとした属性へのデータベース
-     include Singleton
-     def initialize()
-       super
-       @glob, @pre, @post = "#{DB_DIR}/system-char-id/*", "#{DB_DIR}/system-char-id/", ""
-       open_dbs()
-     end
-     def get_all(u8) #全データベースのu8キーを引いてHashにまとめて返す
-       atrs = Hash.new
-       @dbs.each {|t, db|
-        v = get(t, u8)
-        atrs[t] = v if v != nil
-       }
-       return atrs
-     end
-   end
-
-   class CodesysDB < DB #----------------------------------------------------------------------
-     include Singleton
-     def initialize()
-       super
-       @glob, @pre, @post = "#{DB_DIR}/*/system-char-id", "#{DB_DIR}/", "/system-char-id"
-       open_dbs()
-     end
-     #def keys() @dbs.keys.sort end #どんなCodesysの情報を持っているかの一覧
-     def keys() @dbs.keys end #どんなCodesysの情報を持っているかの一覧
-     def get_codesys(t)
-       db = get(t)
-       return nil unless db
-       return Codesys.new(t)
-     end
-   end
-
-   class Codesys < DB #======================================================================
-     def initialize(name)
-#       super
-       @name = name
-       @dbs = CodesysDB.instance
-     end
-     def keys() #どんなコードポイントの情報を持っているかの一覧
-       ks = @dbs.get(@name).keys
-       if @name =~ /jisx0208/ #特別処理
-        n = @dbs.get('=jis-x0208').keys 
-        #       p ['keys', @name, ks, n]
-        ks += n
-       end
-       ks.map! {|k| to_num(k) }
-       ks
-     end
-     def get(key)
-       v = @dbs.get(@name, key)
-       return v if v
-       if @name =~ /jisx0208/ #jisx0208が含まれている場合だけ特別処理する
-        return @dbs.get('=jis-x0208', key)
-       end
-       return nil
-     end
-     def each()
-       return unless block_given?
-       db = @dbs.get(@name)
-       return nil unless db
-       db.each {|k, v|
-        k = to_num(k)
-        v = to_num(v)
-        k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
-        vv = @dbs.get(@name, k)        #p ['each', t, k, v, vv]
-        yield(k, vv)
-       }
-     end
-     def each_sort()
-       return unless block_given?
-       db = @dbs.get(@name)
-       return nil unless db
-       ar = db.to_a
-       ar.map! {|k, v| [to_num(k), to_num(v)] }
-       ar.sort.each {|k, v|
-        k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
-        vv = @dbs.get(@name, k)        #p ['each', t, k, v, vv]
-        yield(k, vv)
-       }
-     end
-   end
-
-  class IDS_TEXT_DB < DB #======================================================================
-    include Singleton
-    if CHISE.windows?()
-      IDS_DB_DIR = 'd:/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
-    else
-      IDS_DB_DIR = '/home/eto/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
-    end
-    IDS_LIST = "
-IDS-UCS-Basic.txt
-#IDS-UCS-Compat-Supplement.txt
-#IDS-UCS-Compat.txt
-IDS-UCS-Ext-A.txt
-IDS-UCS-Ext-B-1.txt
-IDS-UCS-Ext-B-2.txt
-IDS-UCS-Ext-B-3.txt
-IDS-UCS-Ext-B-4.txt
-IDS-UCS-Ext-B-5.txt
-IDS-UCS-Ext-B-6.txt
-IDS-JIS-X0208-1990.txt
-IDS-Daikanwa-01.txt
-IDS-Daikanwa-02.txt
-IDS-Daikanwa-03.txt
-IDS-Daikanwa-04.txt
-IDS-Daikanwa-05.txt
-IDS-Daikanwa-06.txt
-IDS-Daikanwa-07.txt
-IDS-Daikanwa-08.txt
-IDS-Daikanwa-09.txt
-IDS-Daikanwa-10.txt
-IDS-Daikanwa-11.txt
-IDS-Daikanwa-12.txt
-IDS-Daikanwa-dx.txt
-IDS-Daikanwa-ho.txt
-IDS-CBETA.txt
-".split
-    def initialize()
-      super
-      @ids_list = IDS_LIST
-      @chars = []
-      @glob, @pre, @post = "#{IDS_DB_DIR}/db/*", "#{IDS_DB_DIR}/db/", ""
-      dir = File.dirname(@pre)
-      Dir.mkdir(dir) unless FileTest.exist?(dir)
-      open_dbs()
-    end
-    def each_file()
-      return unless block_given?
-      @ids_list.each {|file|
-       next if file =~ /^#/
-       yield(IDS_DB_DIR+file)
-      }
-    end
-    def each_line(file)
-      open(file){|f|
-       while line = f.gets
-         next if line =~ /^;/ #コメントはとばす
-         line.chomp!
-         code, char, ids = line.split
-         yield(code, char, ids)
-       end
-      }
-    end
-    def dump_text_all
-      each_file {|file|
-       dir = File.dirname(file) + '/../ids-new/'
-       Dir.mkdir(dir) if ! FileTest.directory?(dir)
-       newfile = dir + File.basename(file)
-       p [file, newfile]
-       open(newfile, "w"){|out|
-         out.binmode.sync = true
-         each_line(file){|code, ch, ids|
-           char = Character.get(ch)
-           ids = char.decompose
-           out.print "#{code}  #{ch}   #{ids}\n"
-         }
-       }
+       u = get_char_attribute(a)
+       return u if u != nil
       }
+      return nil
     end
-    def make_ids_error
-      each_file {|file|
-       dir = File.dirname(file) + '/../ids-error'
-       Dir.mkdir(dir) unless FileTest.exist?(dir)
-       errfile = dir + '/' + File.basename(file)
-#      p [file, errfile]
-       open(errfile, "w"){|out|
-         out.binmode.sync = true
-         each_line(file){|code, ch, ids|
-           char = Character.get(ch)
-           ids_error = char['ids-error']
-           next if ids_error.nil?
-           out.print "#{code}  #{ch}   #{ids}  #{ids_error}\n"
-         }
-       }
-      }
-    end
-  end
-
-  class IDS_DB < DB #======================================================================BDB化したIDS DBを扱う
-    include Singleton
-    def initialize
-      @dbs = CharDB.instance
-    end
-    def make_ids_db
-      db = IDS_TEXT_DB.instance
-      db.each_file {|file|
-       @char_counter = 0
-       @same_ids_counter = 0
-       @good_ids_counter = 0
-       @conflict_ids_counter = 0
-       db.each_line(file){|code, ch, ids|
-         @char_counter += 1
 
-         ids = "" if ids == nil
-         next if ids == "" #IDSが定義されていない場合は、さっくりと無視するべしよ。
-
-         charimg = Character.get(ch) #実体参照である可能性がある
-
-         next if code =~ /'$/ || code =~ /"$/ #大漢和番号のダッシュ付きは無視する
-         char = Character.get("&"+code+";") #code表記を元に実体参照を作って解釈する
-         if char.nil? || char.to_s == "" #うまく文字にならなかった
-           print "char == null #{char.inspect} #{code} #{ch}   #{ids}\n" unless code =~ /^M-/ || code =~ /^CB/
-           #大漢和、CBETA以外の場合は、エラーメッセージ。
-           next
-         end
-         if char != charimg #code表記と文字が一致していない?
-           unless code =~ /^M-/ || code =~ /^MH-/ || code =~ /^CB/ #食い違っていて当然であるので何もしない
-             print "unknown char       #{char.inspect} #{code} #{ch}   #{ids}\n"
-             next #それ以外の場合はエラーメッセージをだして、次へ。
-           end
-         end
-         #next if !char.has_attribute? #isolated characterはまぎれこませない。
-
-         ids.de_er! #実体参照を解除する
-         next if ids == char.to_s #もし文字とまったく一緒なら、意味が無いので情報を持たない
-         next if ids.char_length == 1
-
-         idstree = IDS_Tree.new(ids)
-         c = idstree.check_integrity
-         c = "contains self" if ids.include?(char.to_s)
-         if c #ちょっとでもエラーがある場合は、
-           char['ids-error'] = c #エラーを記録して、データとしては保持しない
-           next
-         end
-
-         if char['ids'].nil? || char['ids'] == "" #元々IDSが無かった場合は、
-           char['ids'] = ids #普通に代入すればそれでいいです。
-           @good_ids_counter += 1
-         else #しかしいままでにすでにIDSが定義されていた場合は?
-           if char['ids'] == ids #新しいIDSと古いIDSが完全に一致するなら無視しましょう。
-             @same_ids_counter += 1
-           else #しかしいままでのIDSと新しいIDSが食い違った場合は?
-             @conflict_ids_counter += 1
-#            print "conflict   #{char.inspect} #{code} #{ids}  #{char['ids']}\n"
-           end
-         end
-       }
-       print "#{file}  #{@char_counter}        #{@same_ids_counter}    #{@conflict_ids_counter}        #{@good_ids_counter}\n"
-       CharacterFactory.instance.reset()
-      }
-      @dbs.dump_db('ids-error') #テキスト化する
-      @dbs.dump_db('ids') #テキスト化する
-    end
-    def make_ids_reverse
-      h = Hash.new
-      @dbs.each('ids') {|k, v|
-       char = k.char
-       ids = char.decompose
-       h[ids] = "" if h[ids].nil?
-       h[ids] += k #追加する
-      }
-      h.each {|k, v|
-       h[k] = char_sort(v) #文字の順番を、よく使うっぽいものからの順番にする
-      }
-      h.delete_if {|k, v| #h[k]が""になる可能性もあるが、それはkeyとして入れないことにする。
-       v == ""
-      }
-      print "length    #{h.length}\n"
-      cdb = CodesysDB.instance
-      cdb.make_db_no_question_mark('ids', h)
-      cdb.open_db('ids') #これが無いと、dump_dbされません。
-      cdb.dump_db('ids')
-    end
-    def char_sort(composed)
-      return composed if composed.char_length == 1
-      ar = composed.to_a
-      arorg = ar.dup
-      ar2 = []
-      ar.dup.each {|ch|
-       char = ch.char
-       if char.char_id < 0xfffff #Unicodeっぽい?
-         ar2 << ch
-         ar.delete(ch)
-       end
-      }
-      if 0 < ar.length
-       EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
-         ar.each {|ch|
-           char = ch.char
-           v = char[codesys]
-#          p [codesys, v] if v
-           if v #EntityReferenceの順番に準拠する。
-             ar2 << ch
-             ar.delete(ch)
-           end
-         }
-       }
-      end
-      if 0 < ar.length
-#      p ['yokuwakaran character', ar, ar[0].inspect_all, arorg]
-       EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
-         ar.dup.each {|ch|
-           char = ch.char
-           v = char[codesys]
-#          p [codesys, v] if v
-         }
-       }
+    #----------------------------------------------------------------------CCS関係
+    def to_utf8() Uconv.u4tou8(Character.u4itou4(ucs())) end #UTF8文字列を返す
+    #alias to_s to_utf8
+    alias to_s mcs_utf8
+    def map_utf8()
+      u = ucs()
+      if u.nil? || 0xffff < u
+       return to_er()
+      else
+       return to_utf8()
       end
-      return ar2.join("")
     end
-    def dump_ids_duplicated
-      open('ids-duplicated.txt', 'w'){|out|
-       #out.binmode
-       CodesysDB.instance.each('ids') {|k, v|
-         if v.nil?
-           out.print "nil      #{k}    #{v}\n"
-           next
-         end
-         n = v.char_length
-         next if n == 1
-         out.print "#{n}       #{k}    #{v}"
-         v.each_char {|ch|
-           char = ch.char
-           out.print " #{char.inspect}"
-         }
-         out.print "\n"
-       }
-      }
-    end
-    def make_ids_aggregated
-      @dbs.each('ids') {|k, v|
-       char = k.char
-       ids = char.decompose
-       ag = ids.aggregate
-       char['ids-aggregated'] = ag
-      }
-      @dbs.dump_db('ids-aggregated')
+    alias map_ucs map_utf8
+    def map_ucs_er()
+      u = ucs()
+      if u.nil? || 0xffff < u
+       return to_er()
+      else
+       return Character.get(u).to_er()
+      end
     end
-    def dump_ids_aggregated
-      open('ids-aggregated.txt', 'w'){|out|
-       #out.binmode
-       @dbs.each('ids') {|k, v|
-         char = k.char
-         ids = char['ids']
-         ag  = char['ids-aggregated']
-         out.print "#{char.to_s}       #{ag}   #{ids}\n" if ids != ag
-       }
-      }
+    def to_euc()
+      u = ucs()
+      return "" if u.nil? || 0xffff < u
+      Uconv.u16toeuc(Uconv.u4tou16(Character.u4itou4(ucs())))
     end
-    def make_ids_parts
-      @dbs.each('ids') {|k, v|
-       char = k.char
-       pids = char.to_s
-       ar = []
-       counter = 0
-       loop {
-         ids = pids.decompose
-         break if ids == pids #これ以上分割できないようだったら終了〜。
-         ar += ids.to_a
-         counter += 1
-         p [char.to_s, pids, ids, ar] if 10 < counter #これは何かおかしいぞと
-         pids = ids
-       }
-       ar.sort!
-       ar.uniq!
-#やっぱりIDS文字も加えることにする. by eto 2003-02-05
-#      ar.delete_if {|ch|
-#        ch.char.is_ids? #IDS文字はまぎれこませない。
-#      }
-       str = ar.join('')
-       char['ids-parts'] = str
-      }
-      @dbs.dump_db('ids-parts')
+    def map_euc()
+      e = to_euc()
+      return e if e != ""
+      return to_er()
     end
-    def make_ids_contained
-      h = Hash.new
-      @dbs.each('ids-parts') {|k, v|
-       char = k.char
-       parts = char.ids_parts
-       parts.each_char {|ch|
-#        part = ch.char
-         h[ch] = [] if h[ch].nil?
-         h[ch] << k
-#        h[ch] += k
-#        part['ids-contained'] = "" if part['ids-contained'].nil?
-#        part['ids-contained'] += k
-       }
-      }
-      h.each {|k, v|
-       char = k.char
-       v.sort!
-       char['ids-contained'] = v.join('')
-       
-      }
-      @dbs.dump_db('ids-contained')
+    def to_sjis()
+      u = ucs()
+      return "" if u.nil? || 0xffff < u
+      Uconv.u16tosjis(Uconv.u4tou16(Character.u4itou4(ucs())))
     end
-    def make_ids_decomposed
-      @dbs.each('ids') {|k, v|
-       char = k.char
-       de= char.decompose_all
-       char['ids-decomposed'] = de
-      }
-      @dbs.dump_db('ids-decomposed')
+    def map_sjis()
+      e = to_sjis()
+      return e if e != ""
+      return to_er()
     end
-  end
 
-  class Node < Array #=======================================================木構造の中の一つの枝
-    def initialize(nodeleaf=nil, nodenum=nil)
-      super()
-      @nodeleaf = nodeleaf
-      @nodenum = nodenum
-      if @nodeleaf
-       original_add(@nodeleaf)
-      end
-    end
-    attr_reader :nodenum
-    alias original_add <<
-    private :original_add
-    def <<(obj)
-      original_add(obj)
-      @nodenum -= 1 if @nodenum
+    #----------------------------------------------------------------------
+    def to_er(codesys=nil) #実体参照を返す、希望するcodesysが引数(未実装)
+      return "" if @char_id == nil
+      return sprintf("&#x%04x;", @char_id) if @char_id <= 0xffff
+      return sprintf("&#x%05x;", @char_id) if @char_id <= 0xfffff
+      EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
+       code = self[codesys]
+       next if code == nil
+       return sprintf("&#{er_prefix}%0#{keta}#{numtype};", code)
+      }
+      return sprintf("&MCS-%08X;", @char_id) #本当はこれは無しにしたい
     end
-    def nodes
+    def to_er_list()
       ar = []
-      ar << self.to_s
-      self.each {|n|
-       ar += n.nodes if n.is_a? Node
+      EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
+       er = to_er(codesys)
+       ar << er if er != nil
       }
-      return ar
+      ar
     end
-  end
 
-  class Tree #======================================================================木構造を扱う
-    def initialize()
-      @root = Node.new()
-      @stack = [@root]
-      @leafnum = 0
-      @depth = 1 #stackの深さが最大になったところの値、木構造が無いときは1となる
-    end
-    def depth() @depth - 1 end
-    def add_node(nodeleaf=nil, nodenum=nil) #枝を追加
-      new_node = Node.new(nodeleaf, nodenum)
-      @stack.last << new_node
-      @stack << new_node
-      if @depth < @stack.length
-       @depth = @stack.length
-      end
-      self
-    end
-    def end_node() #この枝は終り
-      @stack.pop
-      self
+    def inspect_x()
+      return "<>" if @char_id == nil
+      ar = [to_utf8(), to_er().sub(/^&/,'').chop]
+      "<"+ar.join(',')+">"
     end
-    def add_leaf(a) #葉を追加
-      @stack.last << a
-      end_check()
-      self
+    alias inspect inspect_x
+    def inspect_all_codesys() #未完成
+      #to_erを全てのcodesysにおいて実行する。その結果をコンパクトにまとめる
     end
-    def end_check()
-      n = @stack.last.nodenum
-      if n && n == 0
-       end_node()
-       end_check() #再帰
-      end
-    end
-    def check_integrity
-      n = @stack.last.nodenum
-      return nil if @root.length == 0 #no tree is good tree
-      return "unmatch leaves" if n && n != 0
-      return "extra nodes" if @root.first.is_a?(Node) && @root.length != 1
-      return "extra leaves" if @root.length != 1
-      return nil
-    end
-    def nodes
-      r = @root.nodes
-      r.shift
-      r
-    end
-    def sub_nodes
-      r = nodes
-      r.shift
-      r
+    def inspect_all()
+      ar = [inspect.chop]
+      alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
+      return ar.join(',')+">"
     end
-    def to_s()    @root.to_s    end
-    def inspect() @root.inspect end
-  end
-
-  class IDS_Tree < Tree #======================================================================
-    def initialize(str)
-      @str = str
-      super()
-      parse()
+    def dump_all()
+      ar = [inspect]
+      alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
+      return ar.join('\n')+'\n'
     end
-    def parse()
-      @str.each_char {|ch|
-       char = Character.new(ch)
-       if is_ids?(char)
-         add_node(char, ids_operator_argc(char))
-       else
-         add_leaf(char)
-       end
+    def get_attributes()
+      str = ""
+      alist.to_a.sort.each {|a, v|
+       str += "#{a}: #{v}\n"
       }
+      str
     end
-    def is_ids?(obj)
-      return true if "+*".include?(obj.to_s) #テスト用ですかね
-      return true if obj.is_ids?
-      return false
-    end
-    def ids_operator_argc(obj)
-      return obj.ids_operator_argc if 0 < obj.ids_operator_argc
-      return 2 #テスト用ってことで
-    end
-    def check_integrity
-      r = super
-      return r if r #不完全がすでにわかっているならreturn
-      return "contains ques" if @str =~ /\?/ #?が含まれている?
-      return nil
-    end
-
-  end
-
-  class IDS #======================================================================IDSそのものを扱うclass
-    def initialize(str) #IDS文字列をうけとる。
-      @str = str
-    end
-    def parse
-    end
-    def parse_x #柔軟型のParse. IDSキャラクターが前にきてなくてもよい。などなど。
-    end
-  end
 
-  class Counter #======================================================================
-    #使い方
-    #counter = Counter.new(50) { exit }
-    #counter.count
-    def initialize(max)
-      @max = max
-      @count = 0
-      @proc = proc
-    end
-    def count
-      @count += 1
-      if @max <= @count
-       @proc.call
+    def inspect_ids(hex_flag=false)
+      ids = decompose
+      ar = []
+      ar << (hex_flag ? "x"+mcs_hex : to_utf8)
+      if to_s != ids #idsが部品そのものだったら部品追加はしない
+       ids.each_char {|ch|
+         char = ch.char
+         next if char.is_ids?
+         if hex_flag then
+           ar << "x"+char.mcs_hex
+         else
+           u = char.to_utf8
+           if u != ""
+             ar << u
+           else
+             ar << char.to_er
+           end
+         end
+       }
       end
+      return "("+ar.join("\t")+")"
+    end
+
+    #----------------------------------------------------------------------IDS関係
+    def decompose
+      k = self.to_s
+      #       idss = self['ids']
+      #       return idss if idss
+      #       return k if self.is_basic_kanji? #基本漢字はstop kanjiとするぞと。
+      return self['ids-represent'] if self['ids-represent'] #ids_representを持っている場合はその値とする。
+      return self['ids-element'] if self['ids-element'] #ids_elementを持っている場合はその値とする。
+
+      idss = self['ids-meaning']
+      return idss if idss != nil && 0 < idss.length && k != idss
+      idss = self['ids-aggregated']
+      return idss if idss != nil && 0 < idss.length && k != idss
+      idss = self['ids']
+      return idss if idss != nil && 0 < idss.length && k != idss
+      return k
+      #       return k if idss.nil? || idss.length == 0 || k == idss
+      #       if idss.char_length == 2
+      #        p ['What???', k, idss, k.inspect_all]
+      #         #return idssx[1] #二個目だけ返すとか?
+      #         return k #IDSに展開する方法が無いと。
+      #       end
+      #       return k if k == idss
+      #       if idss.include?(k) #<C5-4C4D><C6-4A37>この二文字のBUG対策
+      #         #return idss.sub(k, '')
+      #         return k #IDSに展開する方法が無いと。
+      #       end
+      #       return idss
+    end
+    def decompose_all
+      pde = ""
+      de = self.decompose #出発点
+      level = 0
+      while true
+       pde = de
+       de = pde.decompose #もう一度分解をしてみる。
+       break if pde == de #ループを抜けだす
+       exit if 10 < level #p ['too many recursive', self] 
+       level += 1
+      end
+      return de
     end
-  end
-
-  class DBS_Management #======================================================================ファイル管理
-    OBSOLETE_ATTRIBUTES = "
-cns-radical
-cns-radical?
-kangxi-radical
-daikanwa-radical
-unicode-radical
-
-cns-strokes
-kangxi-strokes
-daikanwa-strokes
-shinjigen-1-radical
-gb-original-radical
-japanese-strokes
-jis-strokes-a
-jis-strokes-b
-jisx0208-strokes
-jis-x0213-strokes
-jisx0213-strokes
-unicode-strokes
-
-totalstrokes
-cns-total-strokes
-jis-total-strokes-b
-
-non-morohashi
-
-=>ucs*
-#=>mojikyo
-#=mojikyo
-->identical
-
-ancient-ideograph-of
-ancient-char-of-shinjigen-1
-original-ideograph-of
-original-char-of-shinjigen-1
-simplified-ideograph-of
-vulgar-ideograph-of
-vulgar-char-of-shinjigen-1
-ideograph=
-ideographic-variants
-variant-of-shinjigen-1
-
-iso-10646-comment
-".split
-    def initialize
-      @odir = DB_DIR+"/system-char-id/obsolete" #直打ちしている。
-    end
-    def move_obsolete_files # 廃止予定のbdbファイルをobsoleteディレクトリーにつっこむ
-      db = CharDB.instance
-      db.close_all
-      Dir.mkdir(@odir) unless FileTest.directory? @odir
-      OBSOLETE_ATTRIBUTES.each {|attr|
-       next if attr =~ /^#/
-       filename = db.get_filename(attr)
-       move_to_obsolete(filename)
-       move_to_obsolete(filename+".txt")
-      }
-    end
-    def move_to_obsolete(file)
-      cmd = "mv #{file} #{@odir}"
-#      p cmd
-      system cmd
-    end
-  end
-
-  class JoyoList #======================================================================
-    include Singleton
-    #JP_JOYO_FILE = DB_DIR+"/../jp-joyo.txt" #EUC-jisx0213
-    JP_JOYO_FILE = DB_DIR+"/../joyo-ucs.txt" #UCS
-    COMPOSIT_KANJI = "鳴名加品古知問間聞取兄見切分粉貧林森校東明住位好岩砂里男畑習休短空坂島倉美孝赤看光初努協解新歌語話張強忘悲答晴現正字安守灰秋秒困国医包同合舌居右左受友反道返迷花菜集机主太氷州点店庫仕帳幼防引配早直班筆重番北化比死夏後進酒福私家世内谷半原前寺思電雲気布旅衆泣"
-#    COMPOSIT_KANJI = "鳴名加品古"
-    def initialize
-      @nchars = []
-      read_file
-    end
-    attr_reader :nchars
-    def read_file
-      open(JP_JOYO_FILE) {|f|
-       while line = f.gets
-         next if line =~ /^;/ #コメントはとばす
-         line.chomp!
-         #stroke, nchar, ochar = line.split #new char, old char, old charはnilが多い
-         stroke, nchar = line.split
-         @nchars << nchar
-       end
-      }
-    end
-    def dump_ids(ar)
-      ar.each {|ch|
-       char = ch.char
-       print char.inspect_ids(true), "\t;", char.inspect_ids(false), "\n"
-      }
+    def decompose_all_nu(level=nil)
+      level = 0 if level.nil?
+      if 10 < level
+       p ['too many recursive', self] 
+       exit
+      end
+      de = self.decompose
+      return de.decompose_all(level+1) if de != self #なにか変化があったから再帰
+      return de #もうこれ以上変化は無さそうだぞと。
+    end
+    def is_ids?() 0x2ff0 <= @char_id && @char_id <= 0x2fff end
+    def ids_operator_argc()
+      return 0 unless is_ids?
+      return 3 if @char_id == 0x2ff2 || @char_id == 0x2ff3
+      return 2
     end
   end