i
[chise/ruby.git] / chise / character.rb
1 # Copyright (C) 2002-2004 Kouichirou Eto, All rights reserved.
2
3 require "singleton"
4 require "chise/parser"
5
6 module CHISE
7   class CharacterFactory # generate Character object and cache them
8     include Singleton
9
10     MAX_CACHE_CHARACTER = 10000
11
12     def initialize
13       clear
14       @parser = CharacterParser.new
15     end
16
17     def clear
18       @chars = {}
19     end
20
21     def get(char_id)
22       check_max
23       mcs = @parser.parse(char_id)
24       @chars[mcs] = Character.new(mcs) if @chars[mcs].nil?
25       @chars[mcs]
26     end
27
28     def check_max
29       clear if MAX_CACHE_CHARACTER < @chars.length # clear all caches.
30     end
31   end
32
33   class Character
34     def initialize(char_id=nil)
35       @char_id = char_id
36       @attributes = {}
37       @check_all_database = false
38     end
39     attr_reader :char_id
40
41
42
43
44
45
46     def to_i() @char_id end
47     def mcs_utf8() Character.u4itou8(@char_id) end
48     def mcs_hex() sprintf("%x", @char_id) end
49
50     def self.get(char_id) # flyweight pattern
51       CharacterFactory.instance.get(char_id)
52     end
53
54     def normalize_attribute_name(b)
55       a = b.dup
56       a.gsub!(/_/, "-") #underline\82Í-\82É\92u\8a·
57       a.sub!(/-at-/,  "@")
58       a.sub!(/^map-/,  "=>")
59       a.sub!(/^to-/,   "->")
60       a.sub!(/^from-/, "<-")
61       a
62     end
63
64     def get_char_attribute(b) # XEmacs CHISE compatible API
65       a = normalize_attribute_name(b)
66       #p [a, b]
67       atr = @attributes[a]
68       return atr if atr
69       atr = check_database(a)
70       if atr
71         @attributes[a] = atr
72         return atr
73       end
74       return get_char_attribute("="+a) unless a =~ /^=/
75       #\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é
76       nil
77     end
78
79     def put_char_attribute(b,v)
80       a = normalize_attribute_name(b)
81       @attributes[a] = v;
82       CharDB.instance.put(a, mcs_utf8(), v)
83     end
84
85     def char_attribute_alist() check_all_database(); @attributes; end
86     def char_attribute_list()  check_all_database(); @attributes.keys; end
87     alias [] get_char_attribute  #\82»\82Ì\97ª\8fÌ
88     alias []= put_char_attribute
89     alias alist char_attribute_alist
90     alias list  char_attribute_list
91
92     def method_missing(mid, *args) # ref. ostruct.rb
93       mname = mid.id2name
94       return get_char_attribute(mname) if args.length == 0
95       put_char_attribute(mname.chop, args[0]) if mname =~ /=$/ #\91ã\93ü
96     end
97
98     def has_attribute?() # \88Ó\96¡\82Ì\82 \82éattribute\82ð\8e\9d\82Á\82Ä\82Ü\82·\82©?
99       keys = list
100       keys.delete_if {|k|
101         k =~ /ids/
102       }
103       return (keys.length != 0)
104     end
105
106     def ==(ch)
107       return false if ch.nil?
108       return false unless ch.is_a? Character
109       self.char_id == ch.char_id
110     end
111
112     def self.u4itou4(num)
113       return "" unless num.is_a?(Integer)
114       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
115     end
116
117     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·
118       begin
119         u4 = Character.u4itou4(char_id)
120         u8 = Uconv.u4tou8(u4)
121         return u8
122       rescue
123         #raise ArgumentError, "invalid char_id (#{char_id})", caller(1)
124         #print "error\n"
125         return ""
126       end
127     end
128
129     def check_database(a)
130       db = CharDB.instance
131       u8 = mcs_utf8()
132       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
133       v
134     end
135
136     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é
137       return if @check_all_database
138       return if @char_id.nil?
139       db = CharDB.instance
140       u8 = mcs_utf8()
141       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¢
142       atrs.each {|a,v|
143         @attributes[a] = v #\82Æ\82©\82¢\82¤\8a´\82\82Å\91ã\93ü\82·\82é\82Ì\82Å\82¦\82¦\82©\82È?
144       }
145       @check_all_database = true #\8fd\82¢\8f\88\97\9d\82È\82Ì\82Å\88ê\89\9echeck\82·\82é
146     end
147
148     def ucs()
149       #p "ucs"
150       #ar = %w{ucs ucs-big5 ucs-cdp ucs-cns ucs-jis ucs-ks =>ucs =>ucs* =>ucs-jis}
151       #ar = %w{ucs ucs-jis ucs-big5 ucs-cdp ucs-cns ucs-ks =>ucs =>ucs* =>ucs-jis}
152       ar = %w{ucs-jis ucs =>ucs-jis}
153       #\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
154       ar.each {|a|      #p [a]
155         u = get_char_attribute(a)
156         return u if u
157       }
158       nil
159     end
160
161     #-------------------------------------------------------------------CCS\8aÖ\8cW
162     def to_utf8() Uconv.u4tou8(Character.u4itou4(ucs())) end #UTF8\95\8e\9a\97ñ\82ð\95Ô\82·
163     #alias to_s to_utf8
164     alias to_s mcs_utf8
165
166     def map_utf8()
167       u = ucs()
168       if u.nil? || 0xffff < u
169         return to_er()
170       else
171         return to_utf8()
172       end
173     end
174     alias map_ucs map_utf8
175
176     def map_ucs_er()
177       u = ucs()
178       if u.nil? || 0xffff < u
179         return to_er()
180       else
181         return Character.get(u).to_er()
182       end
183     end
184
185     def to_euc()
186       u = ucs()
187       return "" if u.nil? || 0xffff < u
188       Uconv.u16toeuc(Uconv.u4tou16(Character.u4itou4(ucs())))
189     end
190
191     def map_euc()
192       e = to_euc()
193       return e if e != ""
194       return to_er()
195     end
196
197     def to_sjis()
198       u = ucs()
199       return "" if u.nil? || 0xffff < u
200       Uconv.u16tosjis(Uconv.u4tou16(Character.u4itou4(ucs())))
201     end
202
203     def map_sjis()
204       e = to_sjis()
205       return e if e != ""
206       return to_er()
207     end
208
209     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)
210       return "" if @char_id.nil?
211       return sprintf("&#x%04x;", @char_id) if @char_id <= 0xffff
212       return sprintf("&#x%05x;", @char_id) if @char_id <= 0xfffff
213       EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
214         code = self[codesys]
215         next if code.nil?
216         return sprintf("&#{er_prefix}%0#{keta}#{numtype};", code)
217       }
218       return sprintf("&MCS-%08X;", @char_id) #\96{\93\96\82Í\82±\82ê\82Í\96³\82µ\82É\82µ\82½\82¢
219     end
220
221     def to_er_list()
222       ar = []
223       EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
224         er = to_er(codesys)
225         ar << er if er
226       }
227       ar
228     end
229
230     def inspect_x()
231       return "<>" if @char_id.nil?
232       ar = [to_utf8(), to_er().sub(/^&/,"").chop]
233       "<"+ar.join(",")+">"
234     end
235     alias inspect inspect_x
236
237     def inspect_all_codesys() #\96¢\8a®\90¬
238       #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é
239     end
240
241     def inspect_all()
242       ar = [inspect.chop]
243       alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
244       return ar.join(",")+">"
245     end
246
247     def dump_all()
248       ar = [inspect]
249       alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
250       return ar.join('\n')+'\n'
251     end
252
253     def get_attributes()
254       str = ""
255       alist.to_a.sort.each {|a, v|
256         str += "#{a}: #{v}\n"
257       }
258       str
259     end
260
261     def inspect_ids(hex_flag=false)
262       ids = decompose
263       ar = []
264       ar << (hex_flag ? "x"+mcs_hex : to_utf8)
265       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¢
266         ids.each_char {|ch|
267           char = ch.char
268           next if char.is_ids?
269           if hex_flag then
270             ar << "x"+char.mcs_hex
271           else
272             u = char.to_utf8
273             if u != ""
274               ar << u
275             else
276               ar << char.to_er
277             end
278           end
279         }
280       end
281       return "("+ar.join("\t")+")"
282     end
283
284     #--------------------------------------------------------------------IDS\8aÖ\8cW
285     def glyph_decompose() do_decompose(false) end
286     def decompose()       do_decompose(true)  end
287     def do_decompose(check_meaning = true)
288       k = self.to_s
289       #       idss = self["ids"]
290       #       return idss if idss
291       #       return k if self.is_basic_kanji? #\8aî\96{\8a¿\8e\9a\82Ístop kanji\82Æ\82·\82é\82¼\82Æ\81B
292       if check_meaning
293         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
294         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
295         idss = self["ids-meaning"]
296         return idss if idss && 0 < idss.length && k != idss
297       end
298       idss = self["ids-aggregated"]
299       return idss if idss && 0 < idss.length && k != idss
300       idss = self["ids"]
301       return idss if idss && 0 < idss.length && k != idss
302       return k
303       #       return k if idss.nil? || idss.length == 0 || k == idss
304       #       if idss.char_length == 2
305       # p ["What???", k, idss, k.inspect_all]
306       #  #return idssx[1] #\93ñ\8cÂ\96Ú\82¾\82¯\95Ô\82·\82Æ\82©?
307       #  return k #IDS\82É\93W\8aJ\82·\82é\95û\96@\82ª\96³\82¢\82Æ\81B
308       #       end
309       #       return k if k == idss
310       #       if idss.include?(k) #<C5-4C4D><C6-4A37>\82±\82Ì\93ñ\95\8e\9a\82ÌBUG\91Î\8dô
311       #  #return idss.sub(k, "")
312       #  return k #IDS\82É\93W\8aJ\82·\82é\95û\96@\82ª\96³\82¢\82Æ\81B
313       #       end
314       #       return idss
315     end
316
317     def decompose_all
318       pde = ""
319       de = self.decompose #\8fo\94­\93_
320       level = 0
321       while true
322         pde = de
323         de = pde.decompose #\82à\82¤\88ê\93x\95ª\89ð\82ð\82µ\82Ä\82Ý\82é\81B
324         break if pde == de #\83\8b\81[\83v\82ð\94²\82¯\82¾\82·
325         exit if 10 < level #p ["too many recursive", self] 
326         level += 1
327       end
328       return de
329     end
330
331     def decompose_all_nu(level=nil)
332       level = 0 if level.nil?
333       if 10 < level
334         p ["too many recursive", self] 
335         exit
336       end
337       de = self.decompose
338       return de.decompose_all(level+1) if de != self #\82È\82É\82©\95Ï\89»\82ª\82 \82Á\82½\82©\82ç\8dÄ\8bA
339       return de #\82à\82¤\82±\82ê\88È\8fã\95Ï\89»\82Í\96³\82³\82»\82¤\82¾\82¼\82Æ\81B
340     end
341
342     def is_ids?() 0x2ff0 <= @char_id && @char_id <= 0x2fff end
343
344     def ids_operator_argc()
345       return 0 unless is_ids?
346       return 3 if @char_id == 0x2ff2 || @char_id == 0x2ff3
347       return 2
348     end
349   end
350 end