root/trunk/bknr/datastore/src/indices/category-index.lisp

Revision 2584, 6.4 kB (checked in by hans, 10 months ago)

Cleanup in indices - Remove &ALLOW-OTHER-KEYS, add more error checking. Thanks to Klaus Unger for reporting

Line 
1 (in-package :bknr.indices)
2
3 ;;; category tree structure
4
5 (defun make-node (name children)
6   (cons name children))
7
8 (defun node-name (node)
9   (car node))
10
11 (defun (setf node-name) (new-value node)
12   (setf (car node) new-value))
13
14 (defun node-children (node)
15   (cdr node))
16
17 (defun node-children-empty-p (node)
18   (null (node-children node)))
19
20 (defun (setf node-children) (new-value node)
21   (setf (cdr node) new-value))
22
23 (defstruct category-tree
24   (test #'eql)
25   (root-node (make-node :root nil)))
26
27 (defun node-to-categories (node &optional parent-category)
28   (let ((category (append parent-category (list (node-name node)))))
29     (cons category (mapcan #'(lambda (child) (node-to-categories child category))
30                            (node-children node)))))
31
32 (defun nodes-to-categories (nodes &optional parent-category)
33   (mapcan #'(lambda (node) (node-to-categories node parent-category)) nodes))
34
35 (defun tree-categories (tree &optional category)
36   (nodes-to-categories (node-children (category-tree-root-node tree)) category))
37
38 (defun tree-find-node (tree category)
39   (unless (listp category)
40     (setf category (list category)))
41   (do* ((curnode (category-tree-root-node tree)
42                  (find catname (node-children curnode)
43                        :key #'node-name
44                        :test (category-tree-test tree)))
45         (curcat category (cdr curcat))
46         (catname (car curcat) (car curcat)))
47        ((or (null curnode)
48             (null curcat))
49         curnode)))
50
51 (defun category-to-node (category)
52   (if (null category)
53       nil
54       (let ((child (category-to-node (cdr category))))
55             (make-node (first category)
56                        (when child (list child))))))
57
58 (defun tree-add-category (tree category)
59   (unless (listp category)
60     (setf category (list category)))
61   (do* ((curnode (category-tree-root-node tree))
62         (curcat category (cdr curcat))
63         (catname (car curcat) (car curcat)))
64        ((or (null curnode)
65             (null curcat))
66         tree)
67     (let ((node (find catname (node-children curnode)
68                       :key #'node-name
69                       :test (category-tree-test tree))))
70       (if node
71           (setf curnode node)
72           (progn
73             (push (category-to-node curcat)
74                   (node-children curnode))
75             (return-from tree-add-category tree))))))
76
77 (defun tree-remove-category (tree category)
78   (unless (listp category)
79     (setf category (list category)))
80   (when category
81     (let* ((parent-category (parent-category category))
82            (parent-node (tree-find-node tree parent-category)))
83       (when parent-node
84         (setf (node-children parent-node)
85               (remove (category-name category)
86                       (node-children parent-node)
87                       :key #'car
88                       :test (category-tree-test tree)))
89         (when (node-children-empty-p parent-node)
90           (tree-remove-category tree parent-category)))))
91   tree)
92
93 (defun parent-categories (category)
94   (let (res)
95     (dotimes (i (1-  (length category)))
96       (push (butlast category (1+ i)) res))
97     res))
98
99 (defun parent-category (category)
100   (butlast category 1))
101
102 (defun category-name (category)
103   (car (last category)))
104
105 (defun tree-find-children (tree category)
106   (nodes-to-categories (node-children (tree-find-node tree category)) category))
107
108 (defun tree-find-siblings (tree category)
109   (let ((len (length category)))
110     (if (<= len 1)
111         tree
112         (let ((sib-cat (subseq category 0 (1- (length category)))))
113           (nodes-to-categories (tree-find-children tree sib-cat) sib-cat)))))
114
115 ;;; category index
116
117 (defclass category-index (hash-index)
118   ((tree :initform (make-category-tree)
119          :initarg :tree
120          :accessor category-index-tree))
121   (:default-initargs :test #'equal))
122
123 (defmethod initialize-instance :after ((index category-index) &key (tree-test #'eql))
124   (with-slots (tree) index
125     (setf tree (make-category-tree :test tree-test))))
126
127 (defmethod index-get ((index category-index) category)
128   (let* ((tree (category-index-tree index))
129          (hash (slot-index-hash-table index))
130          (categories (cons category
131                            (tree-find-children tree category))))
132     (mapcan #'(lambda (category)
133                 (copy-list (gethash category hash))) categories)))
134
135 (defmethod index-add ((index category-index) object)
136   (unless (slot-boundp object (slot-index-slot-name index))
137     (return-from index-add))
138   (let ((key (slot-value object (slot-index-slot-name index)))
139         (hash-table (slot-index-hash-table index))
140         (tree (category-index-tree index)))
141     (when (and (not (slot-index-index-nil index))
142                (null key))
143       (return-from index-add))
144     (if (nth-value 1 (gethash key hash-table))
145         (push object (gethash key hash-table))
146         (progn
147           (tree-add-category tree key)
148           (setf (gethash key hash-table) (list object))))))
149
150 (defmethod index-remove ((index category-index) object)
151   (let ((key (slot-value object (slot-index-slot-name index)))
152         (hash-table (slot-index-hash-table index))
153         (tree (category-index-tree index)))
154     (let ((new-value (delete-first object (gethash key hash-table))))
155       (if (null new-value)
156           (progn
157             (tree-remove-category tree key)
158             (remhash key hash-table))
159           (setf (gethash key hash-table) new-value)))))
160
161 (defmethod index-keys ((index category-index))
162   (tree-categories (category-index-tree index)))
163
164 (defmethod index-reinitialize :around ((new-index category-index)
165                                        (old-index category-index))
166   (let* ((new-index (call-next-method))
167          (tree (category-index-tree new-index))
168          (new-hash (slot-index-hash-table new-index)))
169     (loop for key being the hash-key of new-hash
170           do (tree-add-category tree key))
171     new-index))
172
173 #|
174
175 (defclass image ()
176   ((category :index-type category-index
177              :index-reader images-with-category
178              :index-keys all-image-categories
179              :index-var *image-category-index*
180              :initarg :category
181              :reader image-category))
182   (:metaclass indexed-class))
183
184 (make-instance 'image :category '(:photo :stills :nature))
185 (make-instance 'image :category '(:photo :naked :woman))
186 (make-instance 'image :category '(:painting :abstract :cubist))
187
188 (defclass track ()
189   ((category :index-type category-index
190              :index-initargs (:tree-test #'equal)
191              :index-reader tracks-with-category
192              :index-keys all-track-categories
193              :index-var *track-category-index*
194              :initarg :category
195              :reader track-category))
196   (:metaclass indexed-class))
197
198 (make-instance 'track :category '("Rock" "New-Age" "Noise"))
199 (make-instance 'track :category '("Rock" "New-Age" "Techno"))
200              
201
202 |#
Note: See TracBrowser for help on using the browser.