23d2580d0e787f7d0b79fb29ca319e048a517581
[elisp/wanderlust.git] / elmo / modb-standard.el
1 ;;; modb-standard.el --- Standartd Implement of MODB.
2
3 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;;      Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news
8
9 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
10
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15 ;;
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25 ;;
26
27 ;;; Commentary:
28 ;;
29
30 ;;; Code:
31 ;;
32 (eval-when-compile (require 'cl))
33
34 (require 'elmo-util)
35 (require 'mime)
36 (require 'modb)
37
38 (defcustom modb-standard-divide-number 500
39   "*Standard modb divide entity number."
40   :type '(choice (const :tag "Not divide" nil)
41                  number)
42   :group 'elmo)
43
44 (defvar modb-standard-entity-filename "entity"
45   "Message entity database.")
46
47 (defvar modb-standard-flag-filename "flag"
48   "Message number <=> Flag status database.")
49
50 (defvar modb-standard-msgid-filename "msgid"
51   "Message number <=> Message-Id database.")
52
53 (eval-and-compile
54   (luna-define-class modb-standard (modb-generic)
55                      (number-list       ; sorted list of message numbers.
56                       entity-map        ; number, msg-id -> entity mapping.
57                       flag-map          ; number -> flag-list mapping
58                       flag-count        ; list of (FLAG . COUNT)
59                       ))
60   (luna-define-internal-accessors 'modb-standard))
61
62 ;; for internal use only
63 (defsubst modb-standard-key (number)
64   (concat "#" (number-to-string number)))
65
66 (defsubst modb-standard-entity-id (entity)
67   (if (eq 'autoload (car-safe entity))
68       (cddr entity)
69     (elmo-msgdb-message-entity-field
70      (elmo-message-entity-handler entity)
71      entity 'message-id)))
72
73 (defsubst modb-standard-entity-map (modb)
74   (or (modb-standard-entity-map-internal modb)
75       (modb-standard-set-entity-map-internal
76        modb
77        (elmo-make-hash (elmo-msgdb-length modb)))))
78
79 (defsubst modb-standard-flag-map (modb)
80   (or (modb-standard-flag-map-internal modb)
81       (modb-standard-set-flag-map-internal
82        modb
83        (elmo-make-hash (elmo-msgdb-length modb)))))
84
85 (defsubst modb-standard-set-message-modified (modb number)
86   (if modb-standard-divide-number
87       (let ((section (/ number modb-standard-divide-number))
88             (modified (modb-generic-message-modified-internal modb)))
89         (unless (memq section modified)
90           (modb-generic-set-message-modified-internal
91            modb (cons section modified))))
92     (modb-generic-set-message-modified-internal modb t)))
93
94 (defsubst modb-standard-set-flag-modified (modb number)
95   (modb-generic-set-flag-modified-internal modb t))
96
97 (defsubst modb-standard-message-flags (modb number)
98   (cdr (elmo-get-hash-val (modb-standard-key number)
99                           (modb-standard-flag-map-internal modb))))
100
101 (defsubst modb-standard-match-flags (check-flags flags)
102   (catch 'done
103     (while check-flags
104       (when (memq (car check-flags) flags)
105         (throw 'done t))
106       (setq check-flags (cdr check-flags)))))
107
108 (defsubst modb-standard-countup-flags (modb flags &optional delta)
109   (let ((flag-count (modb-standard-flag-count-internal modb))
110         (delta (or delta 1))
111         elem)
112     (dolist (flag flags)
113       (if (setq elem (assq flag flag-count))
114           (setcdr elem (+ (cdr elem) delta))
115         (setq flag-count (cons (cons flag delta) flag-count))))
116     (modb-standard-set-flag-count-internal modb flag-count)))
117
118 ;; save and load functions
119 (defun modb-standard-load-msgid (modb path)
120   (let* ((alist (elmo-object-load
121                  (expand-file-name modb-standard-msgid-filename path)))
122          (table (or (modb-standard-entity-map-internal modb)
123                     (elmo-make-hash (length alist))))
124          numbers info)
125     (dolist (pair alist)
126       (setq info (cons 'autoload pair))
127       (elmo-set-hash-val (modb-standard-key (car pair)) info table)
128       (elmo-set-hash-val (cdr pair) info table)
129       (setq numbers (cons (car pair) numbers)))
130     (modb-standard-set-number-list-internal modb (nreverse numbers))
131     (modb-standard-set-entity-map-internal modb table)))
132
133 (defun modb-standard-save-msgid (modb path)
134   (let ((table (modb-standard-entity-map-internal modb))
135         entity alist)
136     (dolist (number (modb-standard-number-list-internal modb))
137       (setq entity (elmo-get-hash-val (modb-standard-key number) table))
138       (setq alist (cons (cons number (modb-standard-entity-id entity))
139                         alist)))
140     (elmo-object-save
141      (expand-file-name modb-standard-msgid-filename path)
142      (nreverse alist))))
143
144 (defun modb-standard-load-flag (modb path)
145   (let ((table (or (modb-standard-flag-map-internal modb)
146                    (elmo-make-hash (elmo-msgdb-length modb)))))
147     (dolist (info (elmo-object-load
148                    (expand-file-name modb-standard-flag-filename path)))
149       (modb-standard-countup-flags modb (cdr info))
150       (elmo-set-hash-val (modb-standard-key (car info)) info table))
151     (modb-standard-set-flag-map-internal modb table)))
152
153 (defun modb-standard-save-flag (modb path)
154   (let (table flist info)
155     (when (setq table (modb-standard-flag-map-internal modb))
156       (mapatoms
157        (lambda (atom)
158          (setq info (symbol-value atom))
159          (when (cdr info)
160            (setq flist (cons info flist))))
161        table))
162     (elmo-object-save
163      (expand-file-name modb-standard-flag-filename path)
164      flist)))
165
166 (defsubst modb-standard-entity-filename (section)
167   (if section
168       (concat modb-standard-entity-filename
169               "-"
170               (number-to-string section))
171     modb-standard-entity-filename))
172
173 (defun modb-standard-load-entity (modb path &optional section)
174   (let ((table (or (modb-standard-entity-map-internal modb)
175                    (elmo-make-hash (elmo-msgdb-length modb)))))
176     (dolist (entity (elmo-object-load
177                      (expand-file-name
178                       (modb-standard-entity-filename section)
179                       path)))
180       (elmo-set-hash-val (modb-standard-key
181                           (elmo-msgdb-message-entity-number
182                            (elmo-message-entity-handler entity)
183                            entity))
184                          entity
185                          table)
186       (elmo-set-hash-val (elmo-msgdb-message-entity-field
187                           (elmo-message-entity-handler entity)
188                           entity 'message-id)
189                          entity
190                          table))
191     (modb-standard-set-entity-map-internal modb table)))
192
193 (defsubst modb-standard-save-entity-1 (modb path &optional section)
194   (let ((table (modb-standard-entity-map-internal modb))
195         (filename (expand-file-name
196                    (modb-standard-entity-filename section) path))
197         entity entities)
198     (dolist (number (modb-standard-number-list-internal modb))
199       (when (and (or (null section)
200                      (= section (/ number modb-standard-divide-number)))
201                  (setq entity (elmo-msgdb-message-entity modb number)))
202         (setq entities (cons entity entities))))
203     (if entities
204         (elmo-object-save filename entities)
205       (ignore-errors (delete-file filename)))))
206
207 (defun modb-standard-save-entity (modb path)
208   (let ((sections (modb-generic-message-modified-internal modb)))
209     (cond ((listp sections)
210            (dolist (section sections)
211              (modb-standard-save-entity-1 modb path section)))
212           (sections
213            (modb-standard-save-entity-1 modb path)))))
214
215 ;;; Implement
216 ;;
217 (luna-define-method elmo-msgdb-load ((msgdb modb-standard))
218   (let ((inhibit-quit t)
219         (path (elmo-msgdb-location msgdb)))
220     (when (file-exists-p (expand-file-name modb-standard-flag-filename path))
221       (modb-standard-load-msgid msgdb path)
222       (modb-standard-load-flag msgdb path)
223       (unless modb-standard-divide-number
224         (modb-standard-load-entity msgdb path))
225       t)))
226
227 (luna-define-method elmo-msgdb-save ((msgdb modb-standard))
228   (let ((path (elmo-msgdb-location msgdb)))
229     (when (elmo-msgdb-message-modified-p msgdb)
230       (modb-standard-save-msgid  msgdb path)
231       (modb-standard-save-entity msgdb path)
232       (modb-generic-set-message-modified-internal msgdb nil))
233     (when (elmo-msgdb-flag-modified-p msgdb)
234       (modb-standard-save-flag msgdb path)
235       (modb-generic-set-flag-modified-internal msgdb nil))))
236
237 (luna-define-method elmo-msgdb-append :around ((msgdb modb-standard)
238                                                msgdb-append)
239   (when (> (elmo-msgdb-length msgdb-append) 0)
240     (if (eq (luna-class-name msgdb-append) 'modb-standard)
241         (let ((numbers (modb-standard-number-list-internal msgdb-append))
242               duplicates)
243           ;; number-list
244           (modb-standard-set-number-list-internal
245            msgdb
246            (nconc (modb-standard-number-list-internal msgdb)
247                   numbers))
248           ;; entity-map
249           (let ((table (modb-standard-entity-map msgdb))
250                 entity msg-id)
251             (dolist (number numbers)
252               (setq entity (elmo-msgdb-message-entity msgdb-append number)
253                     msg-id (modb-standard-entity-id entity))
254               (if (elmo-get-hash-val msg-id table)
255                   (setq duplicates (cons number duplicates))
256                 (elmo-set-hash-val msg-id entity table))
257               (elmo-set-hash-val (modb-standard-key number)
258                                  entity
259                                  table)))
260           ;; flag-map
261           (let ((table (modb-standard-flag-map msgdb)))
262             (mapatoms
263              (lambda (atom)
264                (elmo-set-hash-val (symbol-name atom)
265                                   (symbol-value atom)
266                                   table))
267              (modb-standard-flag-map msgdb-append)))
268           ;; flag-count
269           (dolist (pair (modb-standard-flag-count-internal msgdb-append))
270             (modb-standard-countup-flags msgdb (list (car pair)) (cdr pair)))
271           ;; modification flags
272           (dolist (number (modb-standard-number-list-internal msgdb-append))
273             (modb-standard-set-message-modified msgdb number)
274             (modb-standard-set-flag-modified msgdb number))
275           duplicates)
276       (luna-call-next-method))))
277
278 (luna-define-method elmo-msgdb-clear :after ((msgdb modb-standard))
279   (modb-standard-set-number-list-internal msgdb nil)
280   (modb-standard-set-entity-map-internal msgdb nil)
281   (modb-standard-set-flag-map-internal msgdb nil)
282   (modb-standard-set-flag-count-internal msgdb nil))
283
284 (luna-define-method elmo-msgdb-length ((msgdb modb-standard))
285   (length (modb-standard-number-list-internal msgdb)))
286
287 (luna-define-method elmo-msgdb-flags ((msgdb modb-standard) number)
288   (modb-standard-message-flags msgdb number))
289
290 (luna-define-method elmo-msgdb-set-flag ((msgdb modb-standard)
291                                          number flag)
292   (case flag
293     (read
294      (elmo-msgdb-unset-flag msgdb number 'unread))
295     (uncached
296      (elmo-msgdb-unset-flag msgdb number 'cached))
297     (t
298      (let ((cur-flags (modb-standard-message-flags msgdb number))
299            new-flags diff)
300        (unless (memq flag cur-flags)
301          (setq new-flags (cons flag cur-flags))
302          (setq diff (elmo-list-diff new-flags cur-flags))
303          (modb-standard-countup-flags msgdb (car diff))
304          (modb-standard-countup-flags msgdb (cadr diff) -1)
305          (elmo-set-hash-val (modb-standard-key number)
306                             (cons number new-flags)
307                             (modb-standard-flag-map msgdb))
308          (modb-standard-set-flag-modified msgdb number))))))
309
310 (luna-define-method elmo-msgdb-unset-flag ((msgdb modb-standard)
311                                            number flag)
312   (case flag
313     (read
314      (elmo-msgdb-set-flag msgdb number 'unread))
315     (uncached
316      (elmo-msgdb-set-flag msgdb number 'cached))
317     (all
318      (modb-standard-countup-flags msgdb
319                                   (modb-standard-message-flags msgdb number)
320                                   -1)
321      (elmo-clear-hash-val (modb-standard-key number)
322                           (modb-standard-flag-map msgdb)))
323     (t
324      (let ((cur-flags (modb-standard-message-flags msgdb number))
325            new-flags diff)
326        (when (memq flag cur-flags)
327          (setq new-flags (delq flag (copy-sequence cur-flags)))
328          (setq diff (elmo-list-diff new-flags cur-flags))
329          (modb-standard-countup-flags msgdb (car diff))
330          (modb-standard-countup-flags msgdb (cadr diff) -1)
331          (elmo-set-hash-val (modb-standard-key number)
332                             (cons number new-flags)
333                             (modb-standard-flag-map msgdb))
334          (modb-standard-set-flag-modified msgdb number))
335        (when (eq flag 'unread)
336          (elmo-msgdb-unset-flag msgdb number 'new))))))
337
338 (luna-define-method elmo-msgdb-flag-count ((msgdb modb-standard))
339   (modb-standard-flag-count-internal msgdb))
340
341 (luna-define-method elmo-msgdb-list-messages ((msgdb modb-standard))
342   (copy-sequence
343    (modb-standard-number-list-internal msgdb)))
344
345 (luna-define-method elmo-msgdb-list-flagged ((msgdb modb-standard) flag)
346   (let (entry matched)
347     (case flag
348       (read
349        (dolist (number (modb-standard-number-list-internal msgdb))
350          (unless (memq 'unread (modb-standard-message-flags msgdb number))
351            (setq matched (cons number matched)))))
352       (digest
353        (mapatoms
354         (lambda (atom)
355           (setq entry (symbol-value atom))
356           (when (modb-standard-match-flags '(unread important)
357                                            (cdr entry))
358             (setq matched (cons (car entry) matched))))
359         (modb-standard-flag-map msgdb)))
360       (any
361        (mapatoms
362         (lambda (atom)
363           (setq entry (symbol-value atom))
364           (when (modb-standard-match-flags '(unread important answered)
365                                            (cdr entry))
366             (setq matched (cons (car entry) matched))))
367         (modb-standard-flag-map msgdb)))
368       (t
369        (mapatoms
370         (lambda (atom)
371           (setq entry (symbol-value atom))
372           (when (memq flag (cdr entry))
373             (setq matched (cons (car entry) matched))))
374         (modb-standard-flag-map msgdb))))
375     matched))
376
377 (luna-define-method elmo-msgdb-search ((msgdb modb-standard)
378                                        condition &optional numbers)
379   (if (vectorp condition)
380       (let ((key (elmo-filter-key condition))
381             results)
382         (cond
383          ((and (string= key "flag")
384                (eq (elmo-filter-type condition) 'match))
385           (setq results (elmo-msgdb-list-flagged
386                          msgdb
387                          (intern (elmo-filter-value condition))))
388           (if numbers
389               (elmo-list-filter numbers results)
390             results))
391          ((member key '("first" "last"))
392           (let* ((numbers (or numbers
393                               (modb-standard-number-list-internal msgdb)))
394                  (len (length numbers))
395                  (lastp (string= key "last"))
396                  (value (string-to-number (elmo-filter-value condition))))
397             (when (eq (elmo-filter-type condition) 'unmatch)
398               (setq lastp (not lastp)
399                     value (- len value)))
400             (if lastp
401                 (nthcdr (max (- len value) 0) numbers)
402               (when (> value 0)
403                 (let* ((numbers (copy-sequence numbers))
404                        (last (nthcdr (1- value) numbers)))
405                   (when last
406                     (setcdr last nil))
407                   numbers)))))
408          (t
409           t)))
410     t))
411
412 (luna-define-method elmo-msgdb-append-entity ((msgdb modb-standard)
413                                               entity &optional flags)
414   (let ((number (elmo-msgdb-message-entity-number
415                  (elmo-message-entity-handler entity) entity))
416         (msg-id (elmo-msgdb-message-entity-field
417                  (elmo-message-entity-handler entity) entity 'message-id))
418         duplicate)
419     ;; number-list
420     (modb-standard-set-number-list-internal
421      msgdb
422      (nconc (modb-standard-number-list-internal msgdb)
423             (list number)))
424     ;; entity-map
425     (let ((table (modb-standard-entity-map msgdb)))
426       (setq duplicate (elmo-get-hash-val msg-id table))
427       (elmo-set-hash-val (modb-standard-key number) entity table)
428       (elmo-set-hash-val msg-id entity table))
429     ;; modification flags
430     (modb-standard-set-message-modified msgdb number)
431     ;; flag-map
432     (when flags
433       (elmo-set-hash-val
434        (modb-standard-key number)
435        (cons number flags)
436        (modb-standard-flag-map msgdb))
437       (modb-standard-countup-flags msgdb flags)
438       (modb-standard-set-flag-modified msgdb number))
439     duplicate))
440
441 (luna-define-method elmo-msgdb-delete-messages ((msgdb modb-standard)
442                                                 numbers)
443   (let ((number-list (modb-standard-number-list-internal msgdb))
444         (entity-map (modb-standard-entity-map-internal msgdb))
445         (flag-map (modb-standard-flag-map-internal msgdb))
446         key entity)
447     (dolist (number numbers)
448       (setq key (modb-standard-key number)
449             entity (elmo-get-hash-val key entity-map))
450       ;; number-list
451       (setq number-list (delq number number-list))
452       ;; entity-map
453       (elmo-clear-hash-val key entity-map)
454       (elmo-clear-hash-val (modb-standard-entity-id entity) entity-map)
455       ;; flag-count (must be BEFORE flag-map)
456       (modb-standard-countup-flags
457        msgdb
458        (modb-standard-message-flags msgdb number)
459        -1)
460       ;; flag-map
461       (elmo-clear-hash-val key flag-map)
462       (modb-standard-set-message-modified msgdb number)
463       (modb-standard-set-flag-modified msgdb number))
464     (modb-standard-set-number-list-internal msgdb number-list)
465     (modb-standard-set-entity-map-internal msgdb entity-map)
466     (modb-standard-set-flag-map-internal msgdb flag-map)))
467
468 (luna-define-method elmo-msgdb-sort-entities ((msgdb modb-standard)
469                                               predicate &optional app-data)
470   (message "Sorting...")
471   (let ((numbers (modb-standard-number-list-internal msgdb)))
472     (modb-standard-set-number-list-internal
473      msgdb
474      (sort numbers (lambda (a b)
475                      (funcall predicate
476                               (elmo-msgdb-message-entity msgdb a)
477                               (elmo-msgdb-message-entity msgdb b)
478                               app-data))))
479     (message "Sorting...done")
480     msgdb))
481
482 (luna-define-method elmo-msgdb-message-entity ((msgdb modb-standard) key)
483   (let ((ret (and key
484                   (elmo-get-hash-val
485                    (cond ((stringp key) key)
486                          ((numberp key) (modb-standard-key key)))
487                    (modb-standard-entity-map-internal msgdb)))))
488     (if (eq 'autoload (car-safe ret))
489         (when modb-standard-divide-number
490           (modb-standard-load-entity
491            msgdb
492            (elmo-msgdb-location msgdb)
493            (/ (nth 1 ret) modb-standard-divide-number))
494           (elmo-get-hash-val
495            (cond ((stringp key) key)
496                  ((numberp key) (modb-standard-key key)))
497            (modb-standard-entity-map-internal msgdb)))
498       ret)))
499
500 (require 'product)
501 (product-provide (provide 'modb-standard) (require 'elmo-version))
502
503 ;;; modb-standard.el ends here