(char-ideographic-strokes): Modify preferences to support inheritance.
[chise/xemacs-chise.git.1] / lisp / utf-2000 / ideograph-util.el
1 ;;; ideograph-util.el --- Ideographic Character Database utility
2
3 ;; Copyright (C) 1999,2000,2001,2002,2003,2004 MORIOKA Tomohiko.
4
5 ;; Author: MORIOKA Tomohiko <tomo@kanji.zinbun.kyoto-u.ac.jp>
6 ;; Keywords: CHISE, Chaon model, ISO/IEC 10646, Unicode, UCS-4, MULE.
7
8 ;; This file is part of XEmacs CHISE.
9
10 ;; XEmacs CHISE is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
14
15 ;; XEmacs CHISE is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs CHISE; see the file COPYING.  If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 (require 'char-db-util)
28
29 (defvar ideograph-radical-chars-vector
30   (make-vector 215 nil))
31
32 (defun char-ideographic-radical (char &optional radical)
33   (let (ret)
34     (or (catch 'tag
35           (dolist (domain char-db-feature-domains)
36             (if (and (setq ret (char-feature
37                                 char
38                                 (intern
39                                  (format "%s@%s"
40                                          'ideographic-radical domain))))
41                      (or (eq ret radical)
42                          (null radical)))
43                 (throw 'tag ret))))
44         (catch 'tag
45           (dolist (cell (get-char-attribute char 'ideographic-))
46             (if (and (setq ret (plist-get cell :radical))
47                      (or (eq ret radical)
48                          (null radical)))
49                 (throw 'tag ret))))
50         (char-feature char 'ideographic-radical)
51         (progn
52           (setq ret
53                 (or (get-char-attribute char 'daikanwa-radical)
54                     (get-char-attribute char 'kangxi-radical)
55                     (get-char-attribute char 'japanese-radical)
56                     (get-char-attribute char 'korean-radical)))
57           (when ret
58             (put-char-attribute char 'ideographic-radical ret)
59             ret)))))
60
61 (defvar ideograph-radical-strokes-vector
62   ;;0  1  2  3  4  5  6  7  8  9
63   [nil 1  1  1  1  1  1  2  2  2
64     2  2  2  2  2  2  2  2  2  2
65     2  2  2  2  2  2  2  2  2  2
66     3  3  3  3  3  3  3  3  3  3
67     3  3  3  3  3  3  3  3  3  3
68     3  3  3  3  3  3  3  3  3  3
69     3  4  4  4  3  4  4  4  4  4
70     4  4  4  4  4  4  4  4  4  4
71     4  4  4  4  4  3  4  4  4  4
72     4  4  4  4  3  5  4  5  5  5
73     ;; 100
74     5  5  5  5  5  5  5  5  5  5
75     5  5  5  5  5  5  5  5  6  6
76     6  6  6  6  6  6  6  6  6  6
77     4  6  6  6  6  6  6  6  6  6
78     4  6  6  6  6  6  6  7  7  7
79     7  7  7  7  7  7  7  7  7  7
80     7  7  4  3  7  7  7  8  7  8
81     3  8  8  8  8  8  9  9  9  9
82     9  9  9  9  8  9  9 10 10 10
83    10 10 10 10 10 11 11 11 11 11
84    ;; 200
85    11 12 12 12 12 13 13 13 13 14
86    14 15 16 16 17])
87
88 ;;;###autoload
89 (defun char-ideographic-strokes-from-domains (char domains &optional radical)
90   (let (ret)
91     (catch 'tag
92       (dolist (domain domains)
93         (if (and (setq ret (or (char-feature
94                                 char
95                                 (intern
96                                  (format "%s@%s"
97                                          'ideographic-radical domain)))
98                                (char-feature
99                                 char 'ideographic-radical)))
100                  (or (eq ret radical)
101                      (null radical))
102                  (setq ret (char-feature
103                             char
104                             (intern
105                              (format "%s@%s"
106                                      'ideographic-strokes domain)))))
107             (throw 'tag ret))))))
108
109 ;;;###autoload
110 (defun char-ideographic-strokes (char &optional radical preferred-domains)
111   (let (ret)
112     (or (catch 'tag
113           (dolist (cell (get-char-attribute char 'ideographic-))
114             (if (and (setq ret (plist-get cell :radical))
115                      (or (eq ret radical)
116                          (null radical)))
117                 (throw 'tag (plist-get cell :strokes)))))
118         (char-ideographic-strokes-from-domains
119          char preferred-domains radical)
120         (get-char-attribute char 'ideographic-strokes)
121         (char-ideographic-strokes-from-domains
122          char char-db-feature-domains radical)
123         (char-feature char 'ideographic-strokes)
124         (get-char-attribute char 'daikanwa-strokes)
125         (let ((strokes
126                (or (get-char-attribute char 'kangxi-strokes)
127                    (get-char-attribute char 'japanese-strokes)
128                    (get-char-attribute char 'korean-strokes)
129                    (let ((r (char-ideographic-radical char))
130                          (ts (get-char-attribute char 'total-strokes)))
131                      (if (and r ts)
132                          (- ts (aref ideograph-radical-strokes-vector r))))
133                    )))
134           (when strokes
135             (put-char-attribute char 'ideographic-strokes strokes)
136             strokes)))))
137
138 ;;;###autoload
139 (defun char-total-strokes-from-domains (char domains)
140   (let (ret)
141     (catch 'tag
142       (dolist (domain domains)
143         (if (setq ret (get-char-attribute
144                        char
145                        (intern
146                         (format "%s@%s"
147                                 'total-strokes domain))))
148             (throw 'tag ret))))))
149
150 ;;;###autoload
151 (defun char-total-strokes (char &optional preferred-domains)
152   (or (char-total-strokes-from-domains char preferred-domains)
153       (get-char-attribute char 'total-strokes)
154       (char-total-strokes-from-domains char char-db-feature-domains)))
155
156 ;;;###autoload
157 (defun update-ideograph-radical-table ()
158   (interactive)
159   (let (ret radical script)
160     (dolist (feature
161              (cons 'ideographic-radical
162                    (mapcar
163                     (lambda (domain)
164                       (intern (format "%s@%s" 'ideographic-radical domain)))
165                     char-db-feature-domains)))
166       (map-char-attribute
167        (lambda (chr radical)
168          (dolist (char (cons chr
169                              (get-char-attribute chr '->denotational)))
170            (when (and radical
171                       (eq radical
172                           (char-ideographic-radical char radical))
173                       (or (null (setq script
174                                       (get-char-attribute char 'script)))
175                           (memq 'Ideograph script)))
176              (unless (memq char
177                            (setq ret
178                                  (aref ideograph-radical-chars-vector
179                                        radical)))
180                (char-ideographic-strokes char)
181                (aset ideograph-radical-chars-vector radical
182                      (cons char ret)))))
183          nil)
184        feature))
185     (map-char-attribute
186      (lambda (char data)
187        (dolist (cell data)
188          (setq radical (plist-get cell :radical))
189          (when (and radical
190                     (or (null (setq script (get-char-attribute char 'script)))
191                         (memq 'Ideograph script)))
192            (unless (memq char
193                          (setq ret
194                                (aref ideograph-radical-chars-vector radical)))
195              (char-ideographic-strokes char)
196              (aset ideograph-radical-chars-vector radical
197                    (cons char ret))))))
198      'ideographic-)))
199
200 (defun int-list< (a b)
201   (if (numberp (car a))
202       (if (numberp (car b))
203           (if (= (car a) (car b))
204               (int-list< (cdr a)(cdr b))
205             (< (car a) (car b)))
206         nil)
207     (numberp (car b))))
208
209 (defun morohashi-daikanwa< (a b)
210   (if (integerp a)
211       (setq a (list a)))
212   (if (integerp b)
213       (setq b (list b)))
214   (cond ((eq (car a) 'ho)
215          (if (eq (car b) 'ho)
216              (int-list< (cdr a)(cdr b))
217            nil))
218         ((numberp (car a))
219          (if (eq (car b) 'ho)
220              t
221            (int-list< a b)))
222         (t
223          (if (eq (car b) 'ho)
224              t
225            (int-list< a b)))))
226
227 ;; (defun nil=-int< (a b)
228 ;;   (cond ((null a) nil)
229 ;;         ((null b) nil)
230 ;;         (t (< a b))))
231
232 ;; (defun nil>-int< (a b)
233 ;;   (cond ((null a) nil)
234 ;;         ((null b) t)
235 ;;         (t (< a b))))
236
237 ;;;###autoload
238 (defun char-representative-of-daikanwa (char)
239   (if (or (encode-char char 'ideograph-daikanwa 'defined-only)
240           (encode-char char '=daikanwa-rev2 'defined-only))
241       char
242     (let ((m (char-feature char '=>daikanwa))
243           m-m m-s pat)
244       (or (and (integerp m)
245                (or (decode-char '=daikanwa-rev2 m 'defined-only)
246                    (decode-char 'ideograph-daikanwa m)))
247           (when (or m
248                     (setq m (get-char-attribute char 'morohashi-daikanwa)))
249             (setq m-m (pop m))
250             (setq m-s (pop m))
251             (if (= m-s 0)
252                 (or (decode-char '=daikanwa-rev2 m-m 'defined-only)
253                     (decode-char 'ideograph-daikanwa m-m))
254               (when m
255                 (setq pat (list m-m m-s))
256                 (map-char-attribute (lambda (c v)
257                                       (if (equal pat v)
258                                           c))
259                                     'morohashi-daikanwa))))
260           char))))
261
262 (defun char-attributes-poly< (c1 c2 accessors testers defaulters)
263   (catch 'tag
264     (let (a1 a2 accessor tester dm)
265       (while (and accessors testers)
266         (setq accessor (car accessors)
267               tester (car testers)
268               dm (car defaulters))
269         (when (and accessor tester)
270           (setq a1 (funcall accessor c1)
271                 a2 (funcall accessor c2))
272           (cond ((null a1)
273                  (if a2
274                      (cond ((eq dm '<)
275                             (throw 'tag t))
276                            ((eq dm '>)
277                             (throw 'tag nil)))))
278                 ((null a2)
279                  (cond ((eq dm '<)
280                         (throw 'tag nil))
281                        ((eq dm '>)
282                         (throw 'tag t))))
283                 (t
284                  (cond ((funcall tester a1 a2)
285                         (throw 'tag t))
286                        ((funcall tester a2 a1)
287                         (throw 'tag nil))))))
288         (setq accessors (cdr accessors)
289               testers (cdr testers)
290               defaulters (cdr defaulters))))))
291
292 (defvar ideographic-radical nil)
293
294 (defun char-daikanwa-strokes (char &optional radical)
295   (unless radical
296     (setq radical ideographic-radical))
297   (let ((drc (char-representative-of-daikanwa char)))
298     (if (= (char-ideographic-radical drc radical)
299            (char-ideographic-radical char radical))
300         (setq char drc)))
301   (char-ideographic-strokes char radical '(daikanwa)))
302
303 ;;;###autoload
304 (defun char-daikanwa (char)
305   (or (encode-char char 'ideograph-daikanwa 'defined-only)
306       (encode-char char '=daikanwa-rev2 'defined-only)
307       (get-char-attribute char 'morohashi-daikanwa)
308       (let ((ret (char-feature char '=>daikanwa)))
309         (and ret
310              (if (or (get-char-attribute char '<-subsumptive)
311                      (get-char-attribute char '<-denotational))
312                  (list ret 0)
313                ret)))))
314
315 ;;;###autoload
316 (defun char-ucs (char)
317   (or (encode-char char '=ucs 'defined-only)
318       (char-feature char '=>ucs)))
319
320 (defun char-id (char)
321   (logand (char-int char) #x3FFFFFFF))
322
323 (defun ideograph-char< (a b &optional radical)
324   (let ((ideographic-radical (or radical
325                                  ideographic-radical)))
326     (char-attributes-poly<
327      a b
328      '(char-daikanwa-strokes char-daikanwa char-ucs char-id)
329      '(< morohashi-daikanwa< < <)
330      '(> > > >))))
331
332 (defun insert-ideograph-radical-char-data (radical)
333   (let ((chars
334          (sort (copy-list (aref ideograph-radical-chars-vector radical))
335                (lambda (a b)
336                  (ideograph-char< a b radical))))
337         attributes ; ccss
338         )
339     (dolist (name (char-attribute-list))
340       (unless (memq name char-db-ignored-attributes)
341         ;; (if (find-charset name)
342         ;;     (push name ccss)
343         (push name attributes)
344         ;; )
345         ))
346     (setq attributes (sort attributes #'char-attribute-name<)
347           ;; ccss (sort ccss #'char-attribute-name<)
348           )
349     (aset ideograph-radical-chars-vector radical chars)
350     (dolist (char chars)
351       (when ;;(or
352           (not (some (lambda (atr)
353                        (get-char-attribute char atr))
354                      char-db-ignored-attributes))
355         ;; (some (lambda (ccs)
356         ;;         (encode-char char ccs 'defined-only))
357         ;;       ccss)
358         ;;)
359         (insert-char-data char nil attributes ;ccss
360                           )))))
361
362 (defun write-ideograph-radical-char-data (radical file)
363   (if (file-directory-p file)
364       (let ((name (get-char-attribute (int-char (+ #x2EFF radical)) 'name)))
365         (if (string-match "KANGXI RADICAL " name)
366             (setq name (capitalize (substring name (match-end 0)))))
367         (setq name (mapconcat (lambda (char)
368                                 (if (eq char ? )
369                                     "-"
370                                   (char-to-string char))) name ""))
371         (setq file
372               (expand-file-name
373                (format "Ideograph-R%03d-%s.el" radical name)
374                file))))
375   (with-temp-buffer
376     (insert ";; -*- coding: utf-8-mcs -*-\n")
377     (insert-ideograph-radical-char-data radical)
378     (let ((coding-system-for-write 'utf-8-mcs))
379       (write-region (point-min)(point-max) file)
380       )))
381
382 (defun ideographic-structure= (char1 char2)
383   (if (char-ref-p char1)
384       (setq char1 (plist-get char1 :char)))
385   (if (char-ref-p char2)
386       (setq char2 (plist-get char2 :char)))
387   (let ((s1 (if (characterp char1)
388                 (get-char-attribute char1 'ideographic-structure)
389               (cdr (assq 'ideographic-structure char1))))
390         (s2 (if (characterp char2)
391                 (get-char-attribute char2 'ideographic-structure)
392               (cdr (assq 'ideographic-structure char2))))
393         e1 e2)
394     (if (or (null s1)(null s2))
395         (char-spec= char1 char2)
396       (catch 'tag
397         (while (and s1 s2)
398           (setq e1 (car s1)
399                 e2 (car s2))
400           (unless (ideographic-structure= e1 e2)
401             (throw 'tag nil))
402           (setq s1 (cdr s1)
403                 s2 (cdr s2)))
404         (and (null s1)(null s2))))))
405
406 ;;;###autoload
407 (defun ideographic-structure-find-char (structure)
408   (let (rest)
409     (map-char-attribute (lambda (char value)
410                           (setq rest structure)
411                           (catch 'tag
412                             (while (and rest value)
413                               (unless (ideographic-structure=
414                                        (car rest)(car value))
415                                 (throw 'tag nil))
416                               (setq rest (cdr rest)
417                                     value (cdr value)))
418                             (unless (or rest value)
419                               char)))
420                         'ideographic-structure)))
421
422 ;;;###autoload
423 (defun chise-string< (string1 string2 accessors)
424   (let ((len1 (length string1))
425         (len2 (length string2))
426         len
427         (i 0)
428         c1 c2
429         rest func
430         v1 v2)
431     (setq len (min len1 len2))
432     (catch 'tag
433       (while (< i len)
434         (setq c1 (aref string1 i)
435               c2 (aref string2 i))
436         (setq rest accessors)
437         (while (and rest
438                     (setq func (car rest))
439                     (setq v1 (funcall func c1)
440                           v2 (funcall func c2))
441                     (eq v1 v2))
442           (setq rest (cdr rest)))
443         (if v1
444             (if v2
445                 (cond ((< v1 v2)
446                        (throw 'tag t))
447                       ((> v1 v2)
448                        (throw 'tag nil)))
449               (throw 'tag nil))
450           (if v2
451               (throw 'tag t)))
452         (setq i (1+ i)))
453       (< len1 len2))))
454
455
456 (provide 'ideograph-util)
457
458 ;;; ideograph-util.el ends here