New file.
authorMORIOKA Tomohiko <tomo.git@chise.org>
Wed, 4 May 2016 15:42:53 +0000 (00:42 +0900)
committerMORIOKA Tomohiko <tomo.git@chise.org>
Wed, 4 May 2016 15:42:53 +0000 (00:42 +0900)
char-db-json.el [new file with mode: 0644]

diff --git a/char-db-json.el b/char-db-json.el
new file mode 100644 (file)
index 0000000..c38d39a
--- /dev/null
@@ -0,0 +1,1235 @@
+;;; char-db-json.el --- Character Database utility -*- coding: utf-8-er; -*-
+
+;; Copyright (C) 1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,
+;;   2008,2009,2010,2011,2012,2013,2014,2015,2016 MORIOKA Tomohiko.
+
+;; Author: MORIOKA Tomohiko <tomo@kanji.zinbun.kyoto-u.ac.jp>
+;; Keywords: CHISE, Character Database, ISO/IEC 10646, UCS, Unicode, MULE.
+
+;; This file is part of XEmacs CHISE.
+
+;; XEmacs CHISE is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2, or (at
+;; your option) any later version.
+
+;; XEmacs CHISE is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs CHISE; see the file COPYING.  If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+(require 'char-db-util)
+(require 'cwiki-format)
+
+(setq char-db-ignored-attributes
+      '(ideographic-products
+       ->HNG
+       *instance@ruimoku/bibliography/title
+       *instance@morpheme-entry/zh-classical))
+
+
+;;; @ char-db formatters
+;;;
+    
+(defun char-db-json-insert-char-spec (char &optional readable column
+                                          required-features)
+  (unless column
+    (setq column (current-column)))
+  (let (char-spec temp-char)
+    (setq char-spec (char-db-make-char-spec char))
+    (unless (or (characterp char) ; char
+               (condition-case nil
+                   (setq char (find-char char-spec))
+                 (error nil)))
+      ;; define temporary character
+      ;;   Current implementation is dirty.
+      (setq temp-char (define-char (cons '(ideograph-daikanwa . 0)
+                                        char-spec)))
+      (remove-char-attribute temp-char 'ideograph-daikanwa)
+      (setq char temp-char))
+    (char-db-json-insert-char-features char
+                                      readable
+                                      (union (mapcar #'car char-spec)
+                                             required-features)
+                                      nil 'for-sub-node)
+    (when temp-char
+      ;; undefine temporary character
+      ;;   Current implementation is dirty.
+      (setq char-spec (char-attribute-alist temp-char))
+      (while char-spec
+       (remove-char-attribute temp-char (car (car char-spec)))
+       (setq char-spec (cdr char-spec))))))
+
+(defun char-db-json-insert-alist (alist &optional readable column)
+  (unless column
+    (setq column (current-column)))
+  (let ((line-breaking
+        (concat "\n" (make-string (1+ column) ?\ )))
+       name value
+       ret al ; cal
+       key
+       lbs cell rest separator)
+    (insert "(")
+    (while alist
+      (setq name (car (car alist))
+           value (cdr (car alist)))
+      (cond ((eq name 'char)
+            (insert "(char . ")
+            (if (setq ret (condition-case nil
+                              (find-char value)
+                            (error nil)))
+                (progn
+                  (setq al nil
+                        ;; cal nil
+                        )
+                  (while value
+                    (setq key (car (car value)))
+                     ;; (if (find-charset key)
+                     ;;     (setq cal (cons key cal))
+                    (setq al (cons key al))
+                    ;; )
+                    (setq value (cdr value)))
+                  (insert-char-attributes ret
+                                          readable
+                                          (or al 'none) ; cal
+                                          nil 'for-sub-node))
+              (insert (prin1-to-string value)))
+            (insert ")")
+            (insert line-breaking))
+           ((consp value)
+            (insert (format "(%-18s " name))
+            (setq lbs (concat "\n" (make-string (current-column) ?\ )))
+            (while (consp value)
+              (setq cell (car value))
+              (if (and (consp cell)
+                       (consp (car cell))
+                       (setq ret (condition-case nil
+                                     (find-char cell)
+                                   (error nil)))
+                       )
+                  (progn
+                    (setq rest cell
+                          al nil
+                          ;; cal nil
+                          )
+                    (while rest
+                      (setq key (car (car rest)))
+                       ;; (if (find-charset key)
+                       ;;     (setq cal (cons key cal))
+                      (setq al (cons key al))
+                      ;; )
+                      (setq rest (cdr rest)))
+                    (if separator
+                        (insert lbs))
+                    (insert-char-attributes ret
+                                            readable
+                                            al ; cal
+                                            nil 'for-sub-node)
+                    (setq separator lbs))
+                (if separator
+                    (insert separator))
+                (insert (prin1-to-string cell))
+                (setq separator " "))
+              (setq value (cdr value)))
+            (insert ")")
+            (insert line-breaking))
+           (t
+            (insert (format "(%-18s . %S)%s"
+                            name value
+                            line-breaking))))
+      (setq alist (cdr alist))))
+  (insert ")"))
+
+(defun char-db-json-insert-char-reference (plist &optional readable column)
+  (unless column
+    (setq column (current-column)))
+  (let ((line-breaking
+        (concat "\n" (make-string (1+ column) ?\ )))
+       (separator "")
+       name value)
+    (insert "{")
+    (while plist
+      (setq name (pop plist))
+      (setq value (pop plist))
+      (cond ((eq name :char)
+            (insert separator)
+            (insert "\"char\":\t")
+            (cond ((numberp value)
+                   (setq value (decode-char '=ucs value)))
+                   ;; ((consp value)
+                   ;;  (setq value (or (find-char value)
+                   ;;                  value)))
+                  )
+            (char-db-json-insert-char-spec value readable)
+             (insert line-breaking)
+            (setq separator ""))
+           ((eq name :radical)
+            (insert (format "%s%s\t%d, \"_comment\": \"%c\"%s"
+                            separator
+                            name value
+                            (ideographic-radical value)
+                            line-breaking))
+            (setq separator ""))
+            (t
+            (insert (format "%s%s\t%S" separator name value))
+            (setq separator line-breaking)))
+      ))
+  (insert " }"))
+
+(defun char-db-json-insert-ccs-feature (name value line-breaking)
+  (cond
+   ((integerp value)
+    (insert
+     (format
+      (cond
+       ((memq name '(=>iwds-1
+                    ==shinjigen
+                    =shinjigen
+                    =shinjigen@1ed ==shinjigen@1ed
+                    =shinjigen@rev ==shinjigen@rev
+                    =shinjigen/+p@rev ==shinjigen/+p@rev
+                    ===daikanwa/ho ==daikanwa/ho
+                    =daikanwa/ho =>>daikanwa/ho =>daikanwa/ho))
+       " %-18s  %4d,\t\"_comment\": \"%c")
+       ((eq name '=shinjigen@1ed/24pr)
+       " %-18s  %4d,\t\"_comment\": \"%c")
+       ((or
+        (memq name
+              '(===daikanwa
+                ==daikanwa =daikanwa =>>daikanwa =>daikanwa
+                =daikanwa@rev1 =daikanwa@rev2
+                =daikanwa/+p ==daikanwa/+p ===daikanwa/+p
+                =>>daikanwa/+p
+                =daikanwa/+2p =>>daikanwa/+2p
+                =gt ==gt ===gt
+                =>>gt =+>gt =>gt
+                =gt-k ==gt-k ===gt-k
+                =>>gt-k =>gt-k
+                =adobe-japan1-0 ==adobe-japan1-0 ===adobe-japan1-0
+                =adobe-japan1-1 ==adobe-japan1-1 ===adobe-japan1-1
+                =adobe-japan1-2 ==adobe-japan1-2 ===adobe-japan1-2
+                =adobe-japan1-3 ==adobe-japan1-3 ===adobe-japan1-3
+                =adobe-japan1-4 ==adobe-japan1-4 ===adobe-japan1-4
+                =adobe-japan1-5 ==adobe-japan1-5 ===adobe-japan1-5
+                =adobe-japan1-6 ==adobe-japan1-6 ===adobe-japan1-6
+                =>>adobe-japan1-0 =+>adobe-japan1-0
+                =>>adobe-japan1-1 =+>adobe-japan1-1
+                =>>adobe-japan1-2 =+>adobe-japan1-2
+                =>>adobe-japan1-3 =+>adobe-japan1-3
+                =>>adobe-japan1-4 =+>adobe-japan1-4
+                =>>adobe-japan1-5 =+>adobe-japan1-5
+                =>>adobe-japan1-6 =+>adobe-japan1-6
+                =>cbeta =cbeta =>>cbeta ==cbeta ===cbeta
+                =zinbun-oracle =>zinbun-oracle
+                ===hng-jou ===hng-keg ===hng-dng ===hng-mam
+                ===hng-drt ===hng-kgk ===hng-myz ===hng-kda
+                ===hng-khi ===hng-khm ===hng-hok ===hng-kyd ===hng-sok
+                ===hng-yhk ===hng-kak ===hng-kar ===hng-kae
+                ===hng-sys ===hng-tsu ===hng-tzj
+                ===hng-hos ===hng-nak ===hng-jhk
+                ===hng-hod ===hng-gok ===hng-ink ===hng-nto
+                ===hng-nkm ===hng-k24 ===hng-nkk
+                ===hng-kcc ===hng-kcj ===hng-kbk ===hng-sik
+                ===hng-skk ===hng-kyu ===hng-ksk ===hng-wan
+                ===hng-okd ===hng-wad ===hng-kmi
+                ===hng-zkd ===hng-doh ===hng-jyu
+                ===hng-tzs ===hng-kss ===hng-kyo
+                ===hng-smk))
+        ;; (string-match "^=adobe-" (symbol-name name))
+        )
+       " %-18s %5d,\t\"_comment\": \"%c")
+       ((memq name '(=hanyo-denshi/ks
+                    ==hanyo-denshi/ks ===hanyo-denshi/ks
+                    =>>hanyo-denshi/ks
+                    =koseki ==koseki
+                    =mj ==mj ===mj =>>mj =>mj
+                    =zihai mojikyo))
+       " %-18s %6d,\t\"_comment\": \"%c")
+       ((memq name '(=hanyo-denshi/tk ==hanyo-denshi/tk))
+       " %-18s %8d,\t\"_comment\": \"%c")
+       ((>= (charset-dimension name) 2)
+       " %-18s %5d,\t\"_comment\": \"%c")
+       (t
+       " %-18s %3d,\t\"_comment\": \"%c"))
+      (format "\"%s\":" name)
+      (if (= (charset-iso-graphic-plane name) 1)
+         (logior value
+                 (cond ((= (charset-dimension name) 1)
+                        #x80)
+                       ((= (charset-dimension name) 2)
+                        #x8080)
+                       ((= (charset-dimension name) 3)
+                        #x808080)
+                       (t 0)))
+       value)
+      (char-db-decode-isolated-char name value)))
+    (if (and (= (charset-chars name) 94)
+            (= (charset-dimension name) 2))
+       (insert (format " [%02d-%02d]\""
+                       (- (lsh value -8) 32)
+                       (- (logand value 255) 32)))
+      (insert "\""))
+    )
+   (t
+    (insert (format " %-18s %s"
+                   (format "\"%s\":" name) value))
+    ))
+  )
+
+(defun char-db-json-insert-relation-feature (char name value line-breaking
+                                            ccss readable)
+  (insert (format " %-18s [%s    "
+                 (format "\"%s\":" name) line-breaking))
+  (let ((lbs (concat "\n" (make-string (current-column) ?\ )))
+       separator cell sources required-features
+       ret)
+    (while (consp value)
+      (setq cell (car value))
+      (if (integerp cell)
+         (setq cell (decode-char '=ucs cell)))
+      (cond
+       ((eq name '->subsumptive)
+       (when (or (not (some (lambda (atr)
+                              (get-char-attribute cell atr))
+                            char-db-ignored-attributes))
+                 (some (lambda (ccs)
+                         (encode-char cell ccs 'defined-only))
+                       ccss))
+         (if separator
+             (insert separator)
+           (setq separator (format ",%s" lbs)))
+         (let ((char-db-ignored-attributes
+                (cons '<-subsumptive
+                      char-db-ignored-attributes)))
+           (char-db-json-insert-char-features
+            cell readable nil nil 'for-sub-node))
+         )
+       )
+       ((characterp cell)
+       (setq sources
+             (get-char-attribute
+              char (intern (format "%s*sources" name))))
+       (setq required-features nil)
+       (dolist (source sources)
+         (cond
+          ((memq source '(JP
+                          JP/Jouyou
+                          shinjigen shinjigen@1ed shinjigen@rev))
+           (setq required-features
+                 (union required-features
+                        '(=jis-x0208
+                          =jis-x0208@1990
+                          =jis-x0213-1@2000
+                          =jis-x0213-1@2004
+                          =jis-x0213-2
+                          =jis-x0212
+                          =jis-x0208@1983
+                          =jis-x0208@1978
+                          =shinjigen
+                          =shinjigen@1ed
+                          =shinjigen@rev
+                          =shinjigen/+p@rev))))
+          ((eq source 'CN)
+           (setq required-features
+                 (union required-features
+                        '(=gb2312
+                          =gb12345
+                          =iso-ir165)))))
+         (cond
+          ((find-charset (setq ret (intern (format "=%s" source))))
+           (setq required-features
+                 (cons ret required-features)))
+          (t (setq required-features
+                   (cons source required-features)))))
+       (cond ((string-match "@JP" (symbol-name name))
+              (setq required-features
+                    (union required-features
+                           '(=jis-x0208
+                             =jis-x0208@1990
+                             =jis-x0213-1-2000
+                             =jis-x0213-2-2000
+                             =jis-x0212
+                             =jis-x0208@1983
+                             =jis-x0208@1978))))
+             ((string-match "@CN" (symbol-name name))
+              (setq required-features
+                    (union required-features
+                           '(=gb2312
+                             =gb12345
+                             =iso-ir165)))))
+       (if separator
+           (insert separator)
+         (setq separator (format ",%s" lbs)))
+       (if readable
+           (insert (format "%S" cell))
+         (char-db-json-insert-char-spec cell readable
+                                        nil
+                                        required-features))
+       )
+       ((consp cell)
+       (if separator
+           (insert separator)
+         (setq separator (format ",%s" lbs)))
+       (if (consp (car cell))
+           (char-db-json-insert-char-spec cell readable)
+         (char-db-json-insert-char-reference cell readable))
+       )
+       (t
+       (if separator
+           (insert separator)
+         )
+       (insert (prin1-to-string cell))
+       (setq separator " ")))
+      (setq value (cdr value)))
+    (insert " ]")))
+
+(defun char-db-json-insert-char-features (char
+                                         &optional readable attributes column
+                                         for-sub-node)
+  (unless column
+    (setq column (current-column)))
+  (let (name value ; has-long-ccs-name
+       rest
+       radical strokes
+       (line-breaking
+        (concat "\n" (make-string (1+ column) ?\ )))
+       (line-separator nil)
+       lbs cell separator ret
+       key al cal
+       dest-ccss ; sources required-features
+       ccss)
+    (let (atr-d)
+      (setq attributes
+           (sort (if attributes
+                     (if (consp attributes)
+                         (progn
+                           (dolist (name attributes)
+                             (unless (memq name char-db-ignored-attributes)
+                               (if (find-charset name)
+                                   (push name ccss))
+                               (push name atr-d)))
+                           atr-d))
+                   (dolist (name (char-attribute-list))
+                     (unless (memq name char-db-ignored-attributes)
+                       (if (find-charset name)
+                           (push name ccss))
+                       (push name atr-d)))
+                   atr-d)
+                 #'char-attribute-name<)))
+    (insert "{")
+    (when (memq '<-subsumptive attributes)
+      (when (or readable (not for-sub-node))
+       (when (setq value (get-char-attribute char '<-subsumptive))
+         (char-db-json-insert-relation-feature char '<-subsumptive value
+                                               line-breaking
+                                               ccss readable)
+         (setq line-separator (format ",%s" line-breaking))
+         ))
+      (setq attributes (delq '<-subsumptive attributes))
+      )
+    (when (and (memq '<-denotational attributes)
+              (setq value (get-char-attribute char '<-denotational)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (char-db-json-insert-relation-feature char '<-denotational value
+                                           line-breaking
+                                           ccss readable)
+      (setq attributes (delq '<-denotational attributes)))
+    (when (and (memq '<-denotational@component attributes)
+              (setq value
+                    (get-char-attribute char '<-denotational@component)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (char-db-json-insert-relation-feature
+       char '<-denotational@component value
+       line-breaking
+       ccss readable)
+      (setq attributes (delq '<-denotational@component attributes)))
+    (when (and (memq 'name attributes)
+              (setq value (get-char-attribute char 'name)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format
+              (if (> (+ (current-column) (length value)) 48)
+                  "\"name\": %S"
+                "\"name\":               %S")
+              value))
+      (setq attributes (delq 'name attributes))
+      )
+    (when (and (memq 'name* attributes)
+              (setq value (get-char-attribute char 'name*)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format
+              (if (> (+ (current-column) (length value)) 48)
+                  "\"name*\": %S"
+                "{\"name*\":               %S")
+              value))
+      (setq attributes (delq 'name* attributes))
+      )
+    (when (and (memq 'script attributes)
+              (setq value (get-char-attribute char 'script)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"script\":\t\t%s}%s"
+                     (mapconcat (function prin1-to-string)
+                                value " ")
+                     line-breaking))
+      (setq attributes (delq 'script attributes))
+      )
+    (dolist (name '(=>ucs =>ucs*))
+      (when (and (memq name attributes)
+                (setq value (get-char-attribute char name)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"%-18s\":  #x%04X},\t\"_comment\": \"%c\"%s"
+                       name value (decode-char '=ucs value)
+                       line-breaking))
+       (setq attributes (delq name attributes))))
+    (dolist (name '(=>ucs@gb =>ucs@big5))
+      (when (and (memq name attributes)
+                (setq value (get-char-attribute char name)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"%-18s\":  #x%04X},\t\"_comment\": \"%c\"%s"
+                       name value
+                       (decode-char (intern
+                                     (concat "="
+                                             (substring
+                                              (symbol-name name) 2)))
+                                    value)
+                       line-breaking))
+       (setq attributes (delq name attributes))
+       ))
+    (when (and (memq 'general-category attributes)
+              (setq value (get-char-attribute char 'general-category)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format
+              "{\"general-category\":\t%s} // %s%s"
+              (mapconcat (lambda (cell)
+                           (format "%S" cell))
+                         value " ")
+              (cond ((rassoc value unidata-normative-category-alist)
+                     "Normative Category")
+                    ((rassoc value unidata-informative-category-alist)
+                     "Informative Category")
+                    (t
+                     "Unknown Category"))
+              line-breaking))
+      (setq attributes (delq 'general-category attributes))
+      )
+    (when (and (memq 'bidi-category attributes)
+              (setq value (get-char-attribute char 'bidi-category)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"bidi-category\":\t %S}%s"
+                     value
+                     line-breaking))
+      (setq attributes (delq 'bidi-category attributes))
+      )
+    (unless (or (not (memq 'mirrored attributes))
+               (eq (setq value (get-char-attribute char 'mirrored 'empty))
+                   'empty))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"mirrored\":\t\t %S}%s"
+                     value
+                     line-breaking))
+      (setq attributes (delq 'mirrored attributes))
+      )
+    (cond
+     ((and (memq 'decimal-digit-value attributes)
+          (setq value (get-char-attribute char 'decimal-digit-value)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"decimal-digit-value\":  %S}%s"
+                     value
+                     line-breaking))
+      (setq attributes (delq 'decimal-digit-value attributes))
+      (when (and (memq 'digit-value attributes)
+                (setq value (get-char-attribute char 'digit-value)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"digit-value\":\t  %S}%s"
+                       value
+                       line-breaking))
+       (setq attributes (delq 'digit-value attributes))
+       )
+      (when (and (memq 'numeric-value attributes)
+                (setq value (get-char-attribute char 'numeric-value)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"numeric-value\":\t  %S}%s"
+                       value
+                       line-breaking))
+       (setq attributes (delq 'numeric-value attributes))
+       )
+      )
+     (t
+      (when (and (memq 'digit-value attributes)
+                (setq value (get-char-attribute char 'digit-value)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"digit-value\":\t %S}%s"
+                       value
+                       line-breaking))
+       (setq attributes (delq 'digit-value attributes))
+       )
+      (when (and (memq 'numeric-value attributes)
+                (setq value (get-char-attribute char 'numeric-value)))
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"numeric-value\":\t %S}%s"
+                       value
+                       line-breaking))
+       (setq attributes (delq 'numeric-value attributes))
+       )))
+    (when (and (memq 'iso-10646-comment attributes)
+              (setq value (get-char-attribute char 'iso-10646-comment)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"iso-10646-comment\":\t %S}%s"
+                     value
+                     line-breaking))
+      (setq attributes (delq 'iso-10646-comment attributes))
+      )
+    (when (and (memq 'morohashi-daikanwa attributes)
+              (setq value (get-char-attribute char 'morohashi-daikanwa)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"morohashi-daikanwa\":\t%s}%s"
+                     (mapconcat (function prin1-to-string) value " ")
+                     line-breaking))
+      (setq attributes (delq 'morohashi-daikanwa attributes))
+      )
+    (setq radical nil
+         strokes nil)
+    (when (and (memq 'ideographic-radical attributes)
+              (setq value (get-char-attribute char 'ideographic-radical)))
+      (setq radical value)
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format " \"ideographic-radical\": %S,\t\"_comment\": \"%c\""
+                     radical
+                     (ideographic-radical radical)
+                     ))
+      (setq attributes (delq 'ideographic-radical attributes))
+      )
+    (when (and (memq 'shuowen-radical attributes)
+              (setq value (get-char-attribute char 'shuowen-radical)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"shuowen-radical\":\t %S},\t\"_comment\": \"%c\"%s"
+                     value
+                     (shuowen-radical value)
+                     line-breaking))
+      (setq attributes (delq 'shuowen-radical attributes))
+      )
+    (let (key)
+      (dolist (domain
+              (append
+               char-db-feature-domains
+               (let (dest domain)
+                 (dolist (feature (char-attribute-list))
+                   (setq feature (symbol-name feature))
+                   (when (string-match
+                          "\\(radical\\|strokes\\)@\\([^@*]+\\)\\(\\*\\|$\\)"
+                          feature)
+                     (setq domain (intern (match-string 2 feature)))
+                    (unless (memq domain dest)
+                      (setq dest (cons domain dest)))))
+                 (sort dest #'string<))))
+       (setq key (intern (format "%s@%s" 'ideographic-radical domain)))
+       (when (and (memq key attributes)
+                  (setq value (get-char-attribute char key)))
+         (setq radical value)
+         (if line-separator
+             (insert line-separator)
+           (setq line-separator (format ",%s" line-breaking)))
+         (insert (format "{\"%s\": %S},\t\"_comment\": \"%c\"%s"
+                         key
+                         radical
+                         (ideographic-radical radical)
+                         line-breaking))
+         (setq attributes (delq key attributes))
+         )
+       (setq key (intern (format "%s@%s" 'ideographic-strokes domain)))
+       (when (and (memq key attributes)
+                  (setq value (get-char-attribute char key)))
+         (setq strokes value)
+         (if line-separator
+             (insert line-separator)
+           (setq line-separator (format ",%s" line-breaking)))
+         (insert (format " \"%s\": %S"
+                         key
+                         strokes))
+         (setq attributes (delq key attributes))
+         )
+       (setq key (intern (format "%s@%s" 'total-strokes domain)))
+       (when (and (memq key attributes)
+                  (setq value (get-char-attribute char key)))
+         (if line-separator
+             (insert line-separator)
+           (setq line-separator (format ",%s" line-breaking)))
+         (insert (format " \"%s\":       %S"
+                         key
+                         value
+                         ))
+         (setq attributes (delq key attributes))
+         )
+       (dolist (feature '(ideographic-radical
+                          ideographic-strokes
+                          total-strokes))
+         (setq key (intern (format "%s@%s*sources" feature domain)))
+         (when (and (memq key attributes)
+                    (setq value (get-char-attribute char key)))
+           (if line-separator
+               (insert line-separator)
+             (setq line-separator (format ",%s" line-breaking)))
+           (insert (format " \"%s\":%s" key line-breaking))
+           (dolist (cell value)
+             (insert (format " %s" cell)))
+           (setq attributes (delq key attributes))
+           ))
+       ))
+    (when (and (memq 'ideographic-strokes attributes)
+              (setq value (get-char-attribute char 'ideographic-strokes)))
+      (setq strokes value)
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format " \"ideographic-strokes\": %S"
+                     strokes
+                     ))
+      (setq attributes (delq 'ideographic-strokes attributes))
+      )
+    (when (and (memq 'kangxi-radical attributes)
+              (setq value (get-char-attribute char 'kangxi-radical)))
+      (unless (eq value radical)
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"kangxi-radical\":\t%S},\t\"_comment\": \"%c\"%s"
+                       value
+                       (ideographic-radical value)
+                       line-breaking))
+       (or radical
+           (setq radical value)))
+      (setq attributes (delq 'kangxi-radical attributes))
+      )
+    (when (and (memq 'kangxi-strokes attributes)
+              (setq value (get-char-attribute char 'kangxi-strokes)))
+      (unless (eq value strokes)
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"kangxi-strokes\":\t%S}%s"
+                       value
+                       line-breaking))
+       (or strokes
+           (setq strokes value)))
+      (setq attributes (delq 'kangxi-strokes attributes))
+      )
+    (when (and (memq 'japanese-radical attributes)
+              (setq value (get-char-attribute char 'japanese-radical)))
+      (unless (eq value radical)
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"japanese-radical\":\t%S},\t\"_comment\": \"%c\"%s"
+                       value
+                       (ideographic-radical value)
+                       line-breaking))
+       (or radical
+           (setq radical value)))
+      (setq attributes (delq 'japanese-radical attributes))
+      )
+    (when (and (memq 'japanese-strokes attributes)
+              (setq value (get-char-attribute char 'japanese-strokes)))
+      (unless (eq value strokes)
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"japanese-strokes\":\t%S}%s"
+                       value
+                       line-breaking))
+       (or strokes
+           (setq strokes value)))
+      (setq attributes (delq 'japanese-strokes attributes))
+      )
+    (when (and (memq 'cns-radical attributes)
+              (setq value (get-char-attribute char 'cns-radical)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"cns-radical\":\t%S},\t\"_comment\": \"%c\"%s"
+                     value
+                     (ideographic-radical value)
+                     line-breaking))
+      (setq attributes (delq 'cns-radical attributes))
+      )
+    (when (and (memq 'cns-strokes attributes)
+              (setq value (get-char-attribute char 'cns-strokes)))
+      (unless (eq value strokes)
+       (if line-separator
+           (insert line-separator)
+         (setq line-separator (format ",%s" line-breaking)))
+       (insert (format "{\"cns-strokes\":\t%S}%s"
+                       value
+                       line-breaking))
+       (or strokes
+           (setq strokes value)))
+      (setq attributes (delq 'cns-strokes attributes))
+      )
+    (when (and (memq 'ideographic- attributes)
+              (setq value (get-char-attribute char 'ideographic-)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert "{\"ideographic-\":       ")
+      (setq lbs (concat "\n" (make-string (current-column) ?\ ))
+           separator nil)
+      (while (consp value)
+       (setq cell (car value))
+       (if (integerp cell)
+           (setq cell (decode-char '=ucs cell)))
+       (cond ((characterp cell)
+              (if separator
+                  (insert lbs))
+              (if readable
+                  (insert (format "%S" cell))
+                (char-db-json-insert-char-spec cell readable))
+              (setq separator lbs))
+             ((consp cell)
+              (if separator
+                  (insert lbs))
+              (if (consp (car cell))
+                  (char-db-json-insert-char-spec cell readable)
+                (char-db-json-insert-char-reference cell readable))
+              (setq separator lbs))
+             (t
+              (if separator
+                  (insert separator))
+              (insert (prin1-to-string cell))
+              (setq separator " ")))
+       (setq value (cdr value)))
+      (insert " }")
+      (insert line-breaking)
+      (setq attributes (delq 'ideographic- attributes)))
+    (when (and (memq 'total-strokes attributes)
+              (setq value (get-char-attribute char 'total-strokes)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format " \"total-strokes\":       %S"
+                     value
+                     ))
+      (setq attributes (delq 'total-strokes attributes))
+      )
+    (when (and (memq '->ideograph attributes)
+              (setq value (get-char-attribute char '->ideograph)))
+      (if line-separator
+         (insert line-separator)
+       (setq line-separator (format ",%s" line-breaking)))
+      (insert (format "{\"->ideograph\":\t%s}%s"
+                     (mapconcat (lambda (code)
+                                  (cond ((symbolp code)
+                                         (symbol-name code))
+                                        ((integerp code)
+                                         (format "#x%04X" code))
+                                        (t
+                                         (format "%s %S"
+                                                 line-breaking code))))
+                                value " ")
+                     line-breaking))
+      (setq attributes (delq '->ideograph attributes))
+      )
+    (if (equal (get-char-attribute char '->titlecase)
+              (get-char-attribute char '->uppercase))
+       (setq attributes (delq '->titlecase attributes)))
+    (unless readable
+      (dolist (ignored '(composition
+                        ->denotational <-subsumptive ->ucs-unified
+                        ->ideographic-component-forms))
+       (setq attributes (delq ignored attributes))))
+    (while attributes
+      (setq name (car attributes))
+      (unless (eq (setq value (get-char-attribute char name 'value-is-empty))
+                 'value-is-empty)
+       (cond ((setq ret (find-charset name))
+              (setq name (charset-name ret))
+              (when (not (memq name dest-ccss))
+                (setq dest-ccss (cons name dest-ccss))
+                (if line-separator
+                    (insert line-separator)
+                  (setq line-separator (format ",%s" line-breaking)))
+                (char-db-json-insert-ccs-feature name value line-breaking))
+              )
+             ((string-match "^=>ucs@" (symbol-name name))
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (insert (format "{\"%-18s\": #x%04X},\t\"_comment\": \"%c\"%s"
+                              name value (decode-char '=ucs value)
+                              line-breaking))
+              )
+             ((eq name 'jisx0208-1978/4X)
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (insert (format "{\"%-18s\": #x%04X}%s"
+                              name value
+                              line-breaking))
+              )
+             ((and
+               (not readable)
+               (not (eq name '->subsumptive))
+               (not (eq name '->uppercase))
+               (not (eq name '->lowercase))
+               (not (eq name '->titlecase))
+               (not (eq name '->canonical))
+               (not (eq name '->Bopomofo))
+               (not (eq name '->mistakable))
+               (not (eq name '->ideographic-variants))
+               (null (get-char-attribute
+                      char (intern (format "%s*sources" name))))
+               (not (string-match "\\*sources$" (symbol-name name)))
+               (null (get-char-attribute
+                      char (intern (format "%s*note" name))))
+               (not (string-match "\\*note$" (symbol-name name)))
+               (or (eq name '<-identical)
+                   (eq name '<-uppercase)
+                   (eq name '<-lowercase)
+                   (eq name '<-titlecase)
+                   (eq name '<-canonical)
+                   (eq name '<-ideographic-variants)
+                   ;; (eq name '<-synonyms)
+                   (string-match "^<-synonyms" (symbol-name name))
+                   (eq name '<-mistakable)
+                   (when (string-match "^->" (symbol-name name))
+                     (cond
+                      ((string-match "^->fullwidth" (symbol-name name))
+                       (not (and (consp value)
+                                 (characterp (car value))
+                                 (encode-char
+                                  (car value) '=ucs 'defined-only)))
+                       )
+                      (t)))
+                   ))
+              )
+             ((or (eq name 'ideographic-structure)
+                  (eq name 'ideographic-combination)
+                  (eq name 'ideographic-)
+                  (eq name '=decomposition)
+                  (char-feature-base-name= '=decomposition name)
+                  (char-feature-base-name= '=>decomposition name)
+                  ;; (string-match "^=>*decomposition\\(@[^*]+\\)?$"
+                  ;;               (symbol-name name))
+                  (string-match "^\\(->\\|<-\\)[^*]*$" (symbol-name name))
+                  (string-match "^\\(->\\|<-\\)[^*]*\\*sources$"
+                                (symbol-name name))
+                  )
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (char-db-json-insert-relation-feature char name value
+                                                    line-breaking
+                                                    ccss readable))
+             ((memq name '(ideograph=
+                           original-ideograph-of
+                           ancient-ideograph-of
+                           vulgar-ideograph-of
+                           wrong-ideograph-of
+                           ;; simplified-ideograph-of
+                           ideographic-variants
+                           ;; ideographic-different-form-of
+                           ))
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (insert (format "{\"%-18s\":%s " name line-breaking))
+              (setq lbs (concat "\n" (make-string (current-column) ?\ ))
+                    separator nil)
+              (while (consp value)
+                (setq cell (car value))
+                (if (and (consp cell)
+                         (consp (car cell)))
+                    (progn
+                      (if separator
+                          (insert lbs))
+                      (char-db-json-insert-alist cell readable)
+                      (setq separator lbs))
+                  (if separator
+                      (insert separator))
+                  (insert (prin1-to-string cell))
+                  (setq separator " "))
+                (setq value (cdr value)))
+              (insert " }")
+              (insert line-breaking))
+             ((consp value)
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (insert (format "{\"%-18s\": " name))
+              (setq lbs (concat "\n" (make-string (current-column) ?\ ))
+                    separator nil)
+              (while (consp value)
+                (setq cell (car value))
+                (if (and (consp cell)
+                         (consp (car cell))
+                         (setq ret (condition-case nil
+                                       (find-char cell)
+                                     (error nil))))
+                    (progn
+                      (setq rest cell
+                            al nil
+                            cal nil)
+                      (while rest
+                        (setq key (car (car rest)))
+                        (if (find-charset key)
+                            (setq cal (cons key cal))
+                          (setq al (cons key al)))
+                        (setq rest (cdr rest)))
+                      (if separator
+                          (insert lbs))
+                      (char-db-json-insert-char-features ret
+                                                         readable
+                                                         al
+                                                         nil 'for-sub-node)
+                      (setq separator lbs))
+                  (setq ret (prin1-to-string cell))
+                  (if separator
+                      (if (< (+ (current-column)
+                                (length ret)
+                                (length separator))
+                             76)
+                          (insert separator)
+                        (insert lbs)))
+                  (insert ret)
+                  (setq separator " "))
+                (setq value (cdr value)))
+              (insert " }")
+              (insert line-breaking))
+             (t
+              (if line-separator
+                  (insert line-separator)
+                (setq line-separator (format ",%s" line-breaking)))
+              (insert (format "{\"%-18s\":" name))
+              (setq ret (prin1-to-string value))
+              (unless (< (+ (current-column)
+                            (length ret)
+                            3)
+                         76)
+                (insert line-breaking))
+              (insert ret " }" line-breaking)
+              ;; (insert (format "(%-18s . %S)%s"
+              ;;                 name value
+              ;;                 line-breaking))
+              )
+             ))
+      (setq attributes (cdr attributes)))
+    (insert " }")))
+
+(defun char-db-json-char-data (char &optional readable
+                                   attributes column)
+  (unless column
+    (setq column (current-column)))
+  (save-restriction
+    (narrow-to-region (point)(point))
+    (char-db-json-insert-char-features char readable attributes column)
+    (goto-char (point-min))
+    (while (re-search-forward "[ \t]+$" nil t)
+      (replace-match ""))
+    ;; from tabify.
+    (goto-char (point-min))
+    (while (re-search-forward "[ \t][ \t][ \t]*" nil t)
+      (let ((column (current-column))
+           (indent-tabs-mode t))
+       (delete-region (match-beginning 0) (point))
+       (indent-to column)))
+    (goto-char (point-max))
+    ;; (tabify (point-min)(point-max))
+    ))
+
+(defun char-db-json-char-data-with-variant (char &optional printable
+                                          no-ucs-unified
+                                          script excluded-script)
+  (insert "[ ")
+  (char-db-json-char-data char printable)
+  (let ((variants (char-variants char))
+       rest
+       variant vs ret
+       )
+    (setq variants (sort variants #'<))
+    (setq rest variants)
+    (setq variants (cons char variants))
+    (while rest
+      (setq variant (car rest))
+      (unless (get-char-attribute variant '<-subsumptive)
+       (if (and (or (null script)
+                    (null (setq vs (get-char-attribute variant 'script)))
+                    (memq script vs))
+                (or (null excluded-script)
+                    (null (setq vs (get-char-attribute variant 'script)))
+                    (not (memq excluded-script vs))))
+           (unless (and no-ucs-unified (get-char-attribute variant '=ucs))
+             (insert ",\n  ")
+             (char-db-json-char-data variant printable)
+             (if (setq ret (char-variants variant))
+                 (while ret
+                   (or (memq (car ret) variants)
+                        ;; (get-char-attribute (car ret) '<-subsumptive)
+                       (setq rest (nconc rest (list (car ret)))))
+                   (setq ret (cdr ret)))))))
+      (setq rest (cdr rest)))
+    (insert "\n]\n")))
+
+(defun char-db-json-insert-char-range-data (min max
+                                               &optional script
+                                               excluded-script)
+  (let ((code min)
+       char)
+    (while (<= code max)
+      (setq char (decode-char '=ucs code))
+      (if (encode-char char '=ucs 'defined-only)
+         (char-db-json-char-data-with-variant char nil 'no-ucs-unified
+                                        script excluded-script))
+      (setq code (1+ code)))))
+
+(defun write-char-range-data-to-json-file (min max file
+                                              &optional script
+                                              excluded-script)
+  (let ((coding-system-for-write char-db-file-coding-system))
+    (with-temp-buffer
+      (insert (format "// -*- coding: %s -*-\n"
+                     char-db-file-coding-system))
+      (char-db-json-insert-char-range-data min max script excluded-script)
+      (write-region (point-min)(point-max) file))))
+
+;;;###autoload
+(defun what-char-definition-json (char)
+  (interactive (list (char-after)))
+  (let ((buf (get-buffer-create "*Character Description*"))
+       (the-buf (current-buffer))
+       (win-conf (current-window-configuration)))
+    (pop-to-buffer buf)
+    (make-local-variable 'what-character-original-window-configuration)
+    (setq what-character-original-window-configuration win-conf)
+    (setq buffer-read-only nil)
+    (erase-buffer)
+    (condition-case err
+       (progn
+         (char-db-json-char-data-with-variant char nil)
+         (unless (char-attribute-alist char)
+           (insert (format "// = %c\n"
+                           (let* ((rest (split-char char))
+                                  (ccs (pop rest))
+                                  (code (pop rest)))
+                             (while rest
+                               (setq code (logior (lsh code 8)
+                                                  (pop rest))))
+                             (decode-char ccs code)))))
+          ;; (char-db-update-comment)
+         (set-buffer-modified-p nil)
+         (view-mode the-buf (lambda (buf)
+                              (set-window-configuration
+                               what-character-original-window-configuration)
+                              ))
+         (goto-char (point-min)))
+      (error (progn
+              (set-window-configuration
+               what-character-original-window-configuration)
+              (signal (car err) (cdr err)))))))
+
+(defun char-db-json-batch-view ()
+  (setq terminal-coding-system 'binary)
+  (condition-case err
+      (let* ((target (pop command-line-args-left))
+            ret genre
+            object)
+       (princ "Content-Type: application/json; charset=UTF-8
+
+")
+       (cond
+        ((stringp target)
+         (when (string-match "^char=\\(&[^&;]+;\\)" target)
+           (setq ret (match-end 0))
+           (setq target
+                 (concat "char="
+                         (www-uri-encode-object
+                          (www-uri-decode-object
+                           'character (match-string 1 target)))
+                         (substring target ret))))
+         (setq target
+               (mapcar (lambda (cell)
+                         (if (string-match "=" cell)
+                             (progn
+                               (setq genre (substring cell 0 (match-beginning 0))
+                                     ret (substring cell (match-end 0)))
+                               (cons
+                                (intern
+                                 (decode-uri-string genre 'utf-8-mcs-er))
+                                ret))
+                           (list (decode-uri-string cell 'utf-8-mcs-er))))
+                       (split-string target "&")))
+         (setq ret (car target))
+         (cond ((eq (car ret) 'char)
+                (setq object (www-uri-decode-object (car ret)(cdr ret)))
+                (when (characterp object)
+                  (with-temp-buffer
+                    (char-db-json-char-data object)
+                    (encode-coding-region (point-min)(point-max)
+                                          char-db-file-coding-system)
+                    (princ (buffer-string))
+                    ))
+                )
+               ((eq (car ret) 'character)
+                (setq object (www-uri-decode-object (car ret)(cdr ret)))
+                (when (characterp object)
+                  (with-temp-buffer
+                    (char-db-json-char-data object)
+                    (encode-coding-region (point-min)(point-max)
+                                          char-db-file-coding-system)
+                    (princ (buffer-string))
+                    ))
+                ))
+         ))
+       )
+    (error nil
+          (princ (format "%S" err)))
+    ))
+
+
+;;; @ end
+;;;
+
+(provide 'char-db-json)
+
+;;; char-db-json.el ends here