n.c.
[chise/ruby.git] / chise / character.rb
index b2d0972..c133c53 100755 (executable)
@@ -2,6 +2,10 @@
 
 require "singleton"
 require "chise/parser"
+require "chise/chisedb"
+require "chise/iconv"
+require "chise/utf8"
+require "chise/ids"
 
 module CHISE
   class CharacterFactory # generate Character object and cache them
@@ -18,332 +22,121 @@ module CHISE
       @chars = {}
     end
 
-    def get(char_id)
+    def get(s)
       check_max
-      n = @parser.parse(char_id)
-      @chars[n] = Character.new(n) if @chars[n].nil?
-      @chars[n]
+      mcs = @parser.parse(s)
+      @chars[mcs] = Character.new(mcs) if @chars[mcs].nil?
+      @chars[mcs]
     end
 
     def check_max
-      clear if MAX_CACHE_CHARACTER < @chars.length # clear all caches.
-    end
-
-    def nu_length
-      @chars.length
+      clear if MAX_CACHE_CHARACTER < @chars.length # clear all cache
     end
   end
 
   class Character
-    def initialize(char_id=nil)
-      @parser = CharacterParser.new
-      @char_id = @parser.parse(char_id)
-      @attributes = Hash.new
-      @check_all_database = false
+    include UTF8Value
+    include IDS_Module
+
+    def initialize(char_id)
+      raise if char_id.nil?
+      raise unless char_id.kind_of?(Integer) # make sure char_id is Integer.
+      raise if char_id < 0 # make sure char_id is positive.
+      @char_id = char_id
+      @char_id.freeze
+      # @utf8_mcs = CHISE.i_tou8(@char_id)
+      @utf8_mcs = itou8(@char_id)
+      @utf8_mcs.freeze
+      @feature = {}
+      @check_all_done = nil
     end
     attr_reader :char_id
+    attr_reader :utf8_mcs
 
-    def to_i() @char_id end
-    def mcs_utf8() Character.u4itou8(@char_id) end
-    def mcs_hex() sprintf("%x", @char_id) end
-
-    def self.get(char_id) # flyweight pattern
-      CharacterFactory.instance.get(char_id)
+    def self.get(s)
+      CharacterFactory.instance.get(s)
     end
 
-    def normalize_attribute_name(b)
-      a = b.dup
-      a.gsub!(/_/, "-") #underline\82Í-\82É\92u\8a·
-      a.sub!(/-at-/,  "@")
-      a.sub!(/^map-/,  "=>")
-      a.sub!(/^to-/,   "->")
-      a.sub!(/^from-/, "<-")
-      a
+    def inspect
+      sprintf("Char:%x", @char_id)
     end
 
-    def get_char_attribute(b) # XEmacs UTF-2000\8cÝ\8a·API\8cQ
-      a = normalize_attribute_name(b)
-      #p [a, b]
-      atr = @attributes[a]
-      return atr if atr
-      atr = check_database(a)
-      if atr
-       @attributes[a] = atr
-       return atr
-      end
-      return get_char_attribute("="+a) unless a =~ /^=/ #\93ª\82É=\82ª\82Â\82¢\82Ä\82È\82¢\8fê\8d\87\82Í\82»\82ê\82ª\8fÈ\97ª\82³\82ê\82Ä\82¢\82é\82±\82Æ\82ð\89¼\92è\82µ\82Ä\81A\8dÄ\8bA\82·\82é
-      nil
-    end
-
-    def put_char_attribute(b,v)
-      a = normalize_attribute_name(b)
-      @attributes[a] = v;
-      CharDB.instance.put(a, mcs_utf8(), v)
-    end
-
-    def char_attribute_alist() check_all_database(); @attributes; end
-    def char_attribute_list()  check_all_database(); @attributes.keys; end
-    alias [] get_char_attribute  #\82»\82Ì\97ª\8fÌ
-    alias []= put_char_attribute
-    alias alist char_attribute_alist
-    alias list  char_attribute_list
-
-    def method_missing(mid, *args) # ref. ostruct.rb
-      mname = mid.id2name
-      return get_char_attribute(mname) if args.length == 0
-      put_char_attribute(mname.chop, args[0]) if mname =~ /=$/ #\91ã\93ü
-    end
+    def to_s() @utf8_mcs;      end
 
-    def has_attribute?() # \88Ó\96¡\82Ì\82 \82éattribute\82ð\8e\9d\82Á\82Ä\82Ü\82·\82©?
-      keys = list
-      keys.delete_if {|k|
-       k =~ /ids/
-      }
-      return (keys.length != 0)
-    end
-
-    def ==(ch)
-      return false if ch.nil?
-      return false unless ch.is_a? Character
-      self.char_id == ch.char_id
-    end
-
-    def self.u4itou4(num)
-      return "" unless num.is_a?(Integer)
-      return sprintf("%c%c%c%c", num&0xff, (num >> 8)&0xff, (num >> 16)&0xff, (num >> 24)&0xff) #UCS-4\90\94\92l\82ð\95\8e\9a\97ñ\82É\82µ\82Äreturn
-    end
-
-    def self.u4itou8(char_id) #ucs\82Ì\90\94\92l\82ð\8eó\82¯\82Æ\82è\81AUTF-8\82Ì\95\8e\9a\88ê\95\8e\9a\82ð\95Ô\82·
-      begin
-       u4 = Character.u4itou4(char_id)
-       u8 = Uconv.u4tou8(u4)
-       return u8
-      rescue
-       #raise ArgumentError, "invalid char_id (#{char_id})", caller(1)
-       #print "error\n"
-       return ""
-      end
-    end
-
-    def check_database(a)
-      db = CharDB.instance
-      u8 = mcs_utf8()
-      v = db.get(a, u8) # u8\82Å\95\\82³\82ê\82é\95\8e\9a\82Ìa\83A\83g\83\8a\83r\83\85\81[\83g\82ð\92²\82×\82é\81B
-      v
-    end
-
-    def check_all_database() # \8c»\8dÝ\82Ì@char_id\82©\82ç\81A\95\8e\9a\83f\81[\83^\83x\81[\83X\82ð\8eQ\8fÆ\82·\82é
-      return if @check_all_database
-      return if @char_id.nil?
-      db = CharDB.instance
-      u8 = mcs_utf8()
-      atrs = db.get_all(u8) #u8\82Å\95\\82³\82ê\82é\95\8e\9a\82Ì\83A\83g\83\8a\83r\83\85\81[\83g\82ð\91S\95\94\8e\9d\82Á\82Ä\82±\82¢
-      atrs.each {|a,v|
-       @attributes[a] = v #\82Æ\82©\82¢\82¤\8a´\82\82Å\91ã\93ü\82·\82é\82Ì\82Å\82¦\82¦\82©\82È?
-      }
-      @check_all_database = true #\8fd\82¢\8f\88\97\9d\82È\82Ì\82Å\88ê\89\9echeck\82·\82é
-    end
-
-    def ucs()
-      #p "ucs"
-      #ar = %w{ucs ucs-big5 ucs-cdp ucs-cns ucs-jis ucs-ks =>ucs =>ucs* =>ucs-jis}
-      #ar = %w{ucs ucs-jis ucs-big5 ucs-cdp ucs-cns ucs-ks =>ucs =>ucs* =>ucs-jis}
-      ar = %w{ucs-jis ucs =>ucs-jis}
-      #\95À\82Ñ\8f\87\82Í\9c\93\88Ó\93I\82Å\81Aucs-jis\82ð\90æ\82É\8fo\82µ\82Ä\82¢\82é\81B\96{\97\88\82Í\82±\82ê\82à\8ew\92è\82Å\82«\82é\82æ\82¤\82É\82·\82é\82×\82«\81B
-      ar.each {|a|     #p [a]
-       u = get_char_attribute(a)
-       return u if u
-      }
-      nil
-    end
+    def [](f)
+      f = normalize_feature_name(f)
 
-    #-------------------------------------------------------------------CCS\8aÖ\8cW
-    def to_utf8() Uconv.u4tou8(Character.u4itou4(ucs())) end #UTF8\95\8e\9a\97ñ\82ð\95Ô\82·
-    #alias to_s to_utf8
-    alias to_s mcs_utf8
+      v = @feature[f]
+      return v if v
+      v = @feature["="+f]
+      return v if v
 
-    def map_utf8()
-      u = ucs()
-      if u.nil? || 0xffff < u
-       return to_er()
-      else
-       return to_utf8()
+      v = get_feature(f)
+      if v
+       @feature[f] = v
+       return v
       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()
+      v = get_feature("="+f)
+      if v
+       @feature["="+f] = v
+       return v
       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()
+      nil
     end
 
-    def to_er(codesys=nil) #\8eÀ\91Ì\8eQ\8fÆ\82ð\95Ô\82·\81A\8aó\96]\82·\82écodesys\82ª\88ø\90\94(\96¢\8eÀ\91\95)
-      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) #\96{\93\96\82Í\82±\82ê\82Í\96³\82µ\82É\82µ\82½\82¢
+    def []=(k,v)
+      f = normalize_feature_name(k)
+      cd = ChiseDB.instance
+      ft = cd.get_feature(f)
+      ft.set_value(@char_id, v)
+      @feature[f] = v;
     end
 
-    def to_er_list()
-      ar = []
-      EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
-       er = to_er(codesys)
-       ar << er if er
-      }
-      ar
-    end
+    def method_missing(mid, *args) # ref. ostruct.rb
+      mname = mid.id2name
 
-    def inspect_x()
-      return "<>" if @char_id.nil?
-      ar = [to_utf8(), to_er().sub(/^&/,"").chop]
-      "<"+ar.join(",")+">"
-    end
-    alias inspect inspect_x
+      return self[mname] if args.empty? # get
 
-    def inspect_all_codesys() #\96¢\8a®\90¬
-      #to_er\82ð\91S\82Ä\82Ìcodesys\82É\82¨\82¢\82Ä\8eÀ\8ds\82·\82é\81B\82»\82Ì\8c\8b\89Ê\82ð\83R\83\93\83p\83N\83g\82É\82Ü\82Æ\82ß\82é
-    end
-
-    def inspect_all()
-      ar = [inspect.chop]
-      alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
-      return ar.join(",")+">"
-    end
+      if args.length == 1 && /=\Z/ =~ mname # put
+       self[mname.chop] = args.shift
+       return
+      end
 
-    def dump_all()
-      ar = [inspect]
-      alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
-      return ar.join('\n')+'\n'
+      raise "error"
     end
 
-    def get_attributes()
-      str = ""
-      alist.to_a.sort.each {|a, v|
-       str += "#{a}: #{v}\n"
-      }
-      str
+    def to_er
+      en = EntityReferenceEncoder.new
+      en.to_er(self)
     end
 
-    def inspect_ids(hex_flag=false)
-      ids = decompose
-      ar = []
-      ar << (hex_flag ? "x"+mcs_hex : to_utf8)
-      if to_s != ids #ids\82ª\95\94\95i\82»\82Ì\82à\82Ì\82¾\82Á\82½\82ç\95\94\95i\92Ç\89Á\82Í\82µ\82È\82¢
-       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")+")"
+    def is_idc?
+      0x2ff0 <= @char_id && @char_id <= 0x2fff
     end
 
-    #--------------------------------------------------------------------IDS\8aÖ\8cW
-    def glyph_decompose() do_decompose(false) end
-    def decompose()       do_decompose(true)  end
-    def do_decompose(check_meaning = true)
-      k = self.to_s
-      #       idss = self["ids"]
-      #       return idss if idss
-      #       return k if self.is_basic_kanji? #\8aî\96{\8a¿\8e\9a\82Ístop kanji\82Æ\82·\82é\82¼\82Æ\81B
-      if check_meaning
-       return self["ids-represent"] if self["ids-represent"] #ids_represent\82ð\8e\9d\82Á\82Ä\82¢\82é\8fê\8d\87\82Í\82»\82Ì\92l\82Æ\82·\82é\81B
-       return self["ids-element"] if self["ids-element"] #ids_element\82ð\8e\9d\82Á\82Ä\82¢\82é\8fê\8d\87\82Í\82»\82Ì\92l\82Æ\82·\82é\81B
-       idss = self["ids-meaning"]
-       return idss if idss && 0 < idss.length && k != idss
-      end
-      idss = self["ids-aggregated"]
-      return idss if idss && 0 < idss.length && k != idss
-      idss = self["ids"]
-      return idss if idss && 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] #\93ñ\8cÂ\96Ú\82¾\82¯\95Ô\82·\82Æ\82©?
-      #         return k #IDS\82É\93W\8aJ\82·\82é\95û\96@\82ª\96³\82¢\82Æ\81B
-      #       end
-      #       return k if k == idss
-      #       if idss.include?(k) #<C5-4C4D><C6-4A37>\82±\82Ì\93ñ\95\8e\9a\82ÌBUG\91Î\8dô
-      #         #return idss.sub(k, "")
-      #         return k #IDS\82É\93W\8aJ\82·\82é\95û\96@\82ª\96³\82¢\82Æ\81B
-      #       end
-      #       return idss
+    def idc_argument_number
+      return 0 unless is_idc?
+      return 3 if @char_id == 0x2ff2 || @char_id == 0x2ff3
+      return 2
     end
 
-    def decompose_all
-      pde = ""
-      de = self.decompose #\8fo\94­\93_
-      level = 0
-      while true
-       pde = de
-       de = pde.decompose #\82à\82¤\88ê\93x\95ª\89ð\82ð\82µ\82Ä\82Ý\82é\81B
-       break if pde == de #\83\8b\81[\83v\82ð\94²\82¯\82¾\82·
-       exit if 10 < level #p ["too many recursive", self] 
-       level += 1
-      end
-      return de
-    end
+    private
 
-    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 #\82È\82É\82©\95Ï\89»\82ª\82 \82Á\82½\82©\82ç\8dÄ\8bA
-      return de #\82à\82¤\82±\82ê\88È\8fã\95Ï\89»\82Í\96³\82³\82»\82¤\82¾\82¼\82Æ\81B
+    def get_feature(f)
+      cd = ChiseDB.instance
+      cd.load_feature(f, @char_id)
     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
+    def normalize_feature_name(a)
+      a = a.gsub(/_/, "-") #underlineは-に置換
+      a = a.sub(/-at-/,    "@")
+      a = a.sub(/-plus-/,  "+")
+      a = a.sub(/\Amap-/,  "=>")
+      a = a.sub(/\Ato-/,   "->")
+      a = a.sub(/\Afrom-/, "<-")
+      a
     end
   end
 end