update.
[chise/ruby.git] / chise / db.rb
1 # Copyright (C) 2002-2004 Kouichirou Eto, All rights reserved.
2
3 require "singleton"
4 require "bdb"
5 require "chise/config"
6 require "chise/rbchise"
7 require "chise/util"
8
9 module NotUse_CHISE
10
11   class DBS # collection of DBs. not yet
12   end
13
14   class ADB < BDB::Hash # A DataBase.
15     def initialize(*args)
16       super
17       @filename = args[0]
18       @modified = false
19       at_exit {
20 #       p ["at_exit", @filename, @modified]
21         if @modified
22 #         p ["close", @filename, @modified]
23           self.close #これがないと、うまくデータベースがセーブされないのです。
24         end
25       }
26     end
27
28     def self.open_create(filename)
29       ADB.open(filename, nil, BDB::CREATE | BDB::EXCL) #上書きはしない
30     end
31 a
32     def mykey(key)
33       if key.is_a?(String)
34         if key.char_length == 1
35           return "?"+key  #Stringだったら引く前に?を足す
36         end
37       end
38       #key = key.to_s if key.is_a?(Numeric) #NumberだったらStringにする。
39       #ここで && key ! =~ /^\?/ をいれると、?自身を検索できなくなってしまう。
40       return key
41     end
42
43     def myget(key) #keyキーを引いて返す
44       key = mykey(key)
45       v = get(key) #存在しなかったらnilを返すことになる
46       return myvalue(v)
47     end
48
49     def myput(key, v) #keyにvをいれる
50       key = mykey(key)
51       put(key, v) #putする
52       @modified = true
53     end
54   end
55
56   class DB # abstract class for DataBase
57     def get_filename(t)
58       return @pre + CHISE.unix_to_win(t) + @post if CHISE.windows?
59       return @pre + t + @post
60     end
61
62     def get_dirname(t)
63       File.dirname(get_filename(t))
64     end
65
66     def open_dbs()
67       @dbs = Hash.new
68       keys = find_keys()
69       keys.each {|key| open_db(key) }
70     end
71
72     def find_keys()
73       files = []
74       Dir.glob(@glob){|f|
75         next if ! File.file?(f)
76         next if f =~ /.txt$/
77         files << f
78       }
79       keys = []
80       files.each {|f|
81         t = CHISE.win_to_unix(f)
82         t.sub!(%r|^#{@pre}|, "")
83         t.sub!(%r|#{@post}$|, "") if @post != ""
84         keys << t
85       }
86       keys
87     end
88
89     def close_db(t)
90       db = get(t)
91       return nil if db.nil?
92       db.close
93       @dbs.delete(t)
94     end
95
96     def open_db(t)
97       return nil if get(t) #すでにopenしていたら再openはしない。
98       begin
99         bdb = ADB.open(get_filename(t), nil, 0)
100         @dbs[t] = bdb if bdb != nil
101       rescue
102         p ["open error", get_filename(t)]; return nil
103       end
104       true
105     end
106
107     def make_db(t, h=nil) #tという名前でhという中身のデータベースを作る
108       return nil if get(t) #すでにある場合はreturn
109       Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
110       db = nil
111       begin
112         db = ADB.open_create(get_filename(t)) #上書きはしない
113         if h != nil
114           h.each {|k, v|
115             k = "?"+k if k.is_a?(String)
116             db[k] = v
117           }
118         end
119         db.close
120       rescue
121         p ["make error", get_filename(t)]; return nil
122       end
123       true
124     end
125
126     def make_db_no_question_mark(t, h=nil) #tという名前でhという中身のデータベースを作る
127       return nil if get(t) #すでにある場合はreturn
128       Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
129       db = nil
130       begin
131         db = ADB.open_create(get_filename(t)) #上書きはしない
132         if h != nil
133           h.each {|k, v|
134             #        k = "?"+k if k.is_a?(String)
135             db[k] = v
136           }
137         end
138         db.close
139       rescue
140         p ["make error", get_filename(t)]; return nil
141       end
142       true
143     end
144
145     def remove_db(t) #tという名前のデータベースを消去する
146       db = get(t)
147       if db
148         db.close
149         @dbs.delete(t)
150       end
151       begin
152         File.unlink(get_filename(t)) if FileTest.file?(get_filename(t))
153       rescue
154         p ["unlink error", get_filename(t)]; return nil
155       end
156       dn = get_dirname(t)
157       Dir.rmdir(dn) if FileTest.directory?(dn) && Dir.entries(dn).length <= 2 #空directoryだったら消す
158       true
159     end
160
161     def to_num(s)
162       return s.to_i if s =~ /^\d+$/
163       s
164     end
165
166     def dump_db(t)
167       db = get(t)
168       return nil unless db
169       file = get_filename(t)
170       open("#{file}.txt", "w"){|out|
171         #        out.binmode.sync = true
172         ar = db.to_a
173         ar.map! {|k, v| [to_num(k), to_num(v)] }
174         ar.sort.each {|k, v|
175           out.printf("%s\t%s\n", k, v)
176         }
177       }
178       true
179     end
180
181     def each_db()  @dbs.to_a.sort.each {|t, db| yield(t, db) } end
182     def dump_all()  each_db {|t, db| dump_db(t) } end
183     def close_all() each_db {|t, db| db.close   } end
184     def keys() @dbs.keys end
185
186     def each(t)
187       return unless block_given?
188       db = @dbs[t]
189       return nil unless db
190       db.each {|k, v|
191         k = to_num(k)
192         v = to_num(v)
193         k.sub!(/^\?/, "") if k =~ /^\?/ #冒頭の?は取り除く
194         vv = get(t, k)  #p ["each", t, k, v, vv]
195         yield(k, vv)
196       }
197     end
198
199     def each_sort(t)
200       return unless block_given?
201       db = @dbs[t]
202       return nil unless db
203       ar = db.to_a
204       ar.map! {|k, v| [to_num(k), to_num(v)] }
205       ar.sort.each {|k, v|
206         k.sub!(/^\?/, "") if k =~ /^\?/ #冒頭の?は取り除く
207         vv = get(t, k)  #p ["each", t, k, v, vv]
208         yield(k, vv)
209       }
210     end
211
212     def get(t, key=nil) #tというデータベースのkeyキーを引いて返す
213       db = @dbs[t]
214       return db if key.nil?
215       return nil unless db
216       db.myget(key)
217     end
218
219     def put(t, key, v) #tというデータベースのkeyにvをいれる
220       db = @dbs[t]
221       if db == nil
222         db = make_db(t) 
223         db = open_db(t) 
224         db = @dbs[t]
225       end
226       db.myput(key, v) #putする
227     end
228   end
229
230   class CharDB < DB # An Attribute DataBase.  Key is in UTF8-MCS.
231     include Singleton
232
233     def initialize()
234       super
235       dir = Config.instance.db_dir
236       @glob, @pre, @post = "#{dir}/system-char-id/*", "#{dir}/system-char-id/", ""
237       open_dbs()
238     end
239
240     def get_all(u8) #全データベースのu8キーを引いてHashにまとめて返す
241       atrs = Hash.new
242       @dbs.each {|t, db|
243         v = get(t, u8)
244         atrs[t] = v if v != nil
245       }
246       atrs
247     end
248   end
249
250   class CodesysDB < DB # A CodeSystem DataBase.
251     include Singleton
252
253     def initialize()
254       super
255       dir = Config.instance.db_dir
256       @glob, @pre, @post = "#{dir}/*/system-char-id", "#{dir}/", "/system-char-id"
257       open_dbs()
258     end
259
260     #def keys() @dbs.keys.sort end #どんなCodesysの情報を持っているかの一覧
261     def keys() @dbs.keys end #どんなCodesysの情報を持っているかの一覧
262
263     def get_codesys(t)
264       db = get(t)
265       return nil unless db
266       return Codesys.new(t)
267     end
268   end
269
270   class Codesys < DB
271     def initialize(name)
272       #super
273       @name = name
274       @dbs = CodesysDB.instance
275     end
276
277     def keys() #どんなコードポイントの情報を持っているかの一覧
278       ks = @dbs.get(@name).keys
279 #      if @name =~ /jisx0208/ #特別処理
280 #       n = @dbs.get("=jis-x0208").keys 
281 #       #        p ["keys", @name, ks, n]
282 #       ks += n
283 #      end
284       ks.map! {|k| to_num(k) }
285       ks
286     end
287
288     def get(key)
289       v = @dbs.get(@name, key)
290       return v if v
291 #      if @name =~ /jisx0208/ #jisx0208が含まれている場合だけ特別処理する
292 #       return @dbs.get("=jis-x0208", key)
293 #      end
294       return nil
295     end
296
297     def each()
298       return unless block_given?
299       db = @dbs.get(@name)
300       return nil unless db
301       db.each {|k, v|
302         k = to_num(k)
303         v = to_num(v)
304         k.sub!(/^\?/, "") if k =~ /^\?/ #冒頭の?は取り除く
305         vv = @dbs.get(@name, k) #p ["each", t, k, v, vv]
306         yield(k, vv)
307       }
308     end
309
310     def each_sort()
311       return unless block_given?
312       db = @dbs.get(@name)
313       return nil unless db
314       ar = db.to_a
315       ar.map! {|k, v| [to_num(k), to_num(v)] }
316       ar.sort.each {|k, v|
317         k.sub!(/^\?/, "") if k =~ /^\?/ #冒頭の?は取り除く
318         vv = @dbs.get(@name, k) #p ["each", t, k, v, vv]
319         yield(k, vv)
320       }
321     end
322   end
323
324   class JISX0208
325     def initialize
326       db = CodesysDB.instance
327       @common = db.get_codesys("=jis-x0208")
328       @newest = db.get_codesys("japanese-jisx0208-1990")
329     end
330
331     def get_char(code)
332       char = @common.get(code)
333       return char unless char.nil?
334       char = @newest.get(code)
335       return char unless char.nil?
336       return nil
337     end
338
339   end
340 end