mv files
[chise/ruby.git] / lib / chise / db.rb
diff --git a/lib/chise/db.rb b/lib/chise/db.rb
new file mode 100755 (executable)
index 0000000..6057770
--- /dev/null
@@ -0,0 +1,400 @@
+#!/usr/bin/env ruby
+# $Id: db.rb,v 1.1 2003-11-10 08:31:26 eto Exp $
+# Copyright (C) 2002-2003 Kouichirou Eto, All rights reserved.
+# This is free software with ABSOLUTELY NO WARRANTY.
+# You can redistribute it and/or modify it under the terms of the GNU GPL2.
+
+require 'bdb'
+require 'singleton'
+
+module CHISE
+
+  class DBS #======================================================================複数のDBを集めたclass、未完成
+  end
+
+  class ADB < BDB::Hash #======================================================================一つのDB
+    def initialize(*args)
+      super
+      @filename = args[0]
+      @modified = false
+      at_exit {
+#      p ['at_exit', @filename, @modified]
+       if @modified
+#        p ['close', @filename, @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 JISX0208 #======================================================================
+    def initialize
+      db = CodesysDB.instance
+      @common = db.get_codesys('=jis-x0208')
+      @newest = db.get_codesys('japanese-jisx0208-1990')
+    end
+    def get_char(code)
+      char = @common.get(code)
+      return char unless char.nil?
+      char = @newest.get(code)
+      return char unless char.nil?
+      return nil
+    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
+
+end
+
+#----------------------------------------------------------------------終了