a8e4d6a002d322fbec549c0039f6c3931f466df6
[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 'new)
295      (elmo-msgdb-unset-flag msgdb number 'unread))
296     (uncached
297      (elmo-msgdb-unset-flag msgdb number 'cached))
298     (t
299      (let* ((cur-flags (modb-standard-message-flags msgdb number))
300             (new-flags (copy-sequence cur-flags))
301             diff)
302        (or (memq flag new-flags)
303            (setq new-flags (cons flag new-flags)))
304        (when (and (eq flag 'unread)
305                   (memq 'answered new-flags))
306          (setq new-flags (delq 'answered new-flags)))
307        (unless (equal new-flags cur-flags)
308          (setq diff (elmo-list-diff new-flags cur-flags))
309          (modb-standard-countup-flags msgdb (car diff))
310          (modb-standard-countup-flags msgdb (cadr diff) -1)
311          (elmo-set-hash-val (modb-standard-key number)
312                             (cons number new-flags)
313                             (modb-standard-flag-map msgdb))
314          (modb-standard-set-flag-modified msgdb number))))))
315
316 (luna-define-method elmo-msgdb-unset-flag ((msgdb modb-standard)
317                                            number flag)
318   (case flag
319     (read
320      (elmo-msgdb-set-flag msgdb number 'unread))
321     (uncached
322      (elmo-msgdb-set-flag msgdb number 'cached))
323     (all
324      (modb-standard-countup-flags msgdb
325                                   (modb-standard-message-flags msgdb number)
326                                   -1)
327      (elmo-clear-hash-val (modb-standard-key number)
328                           (modb-standard-flag-map msgdb)))
329     (t
330      (let* ((cur-flags (modb-standard-message-flags msgdb number))
331             (new-flags (copy-sequence cur-flags))
332             diff)
333        (and (memq flag new-flags)
334             (setq new-flags (delq flag new-flags)))
335        (when (and (eq flag 'unread)
336                   (memq 'answered new-flags))
337          (setq new-flags (delq 'answered new-flags)))
338        (unless (equal new-flags cur-flags)
339          (setq diff (elmo-list-diff new-flags cur-flags))
340          (modb-standard-countup-flags msgdb (car diff))
341          (modb-standard-countup-flags msgdb (cadr diff) -1)
342          (elmo-set-hash-val (modb-standard-key number)
343                             (cons number new-flags)
344                             (modb-standard-flag-map msgdb))
345          (modb-standard-set-flag-modified msgdb number))))))
346
347 (luna-define-method elmo-msgdb-flag-count ((msgdb modb-standard))
348   (modb-standard-flag-count-internal msgdb))
349
350 (luna-define-method elmo-msgdb-list-messages ((msgdb modb-standard))
351   (copy-sequence
352    (modb-standard-number-list-internal msgdb)))
353
354 (luna-define-method elmo-msgdb-list-flagged ((msgdb modb-standard) flag)
355   (let (entry matched)
356     (case flag
357       (read
358        (dolist (number (modb-standard-number-list-internal msgdb))
359          (unless (memq 'unread (modb-standard-message-flags msgdb number))
360            (setq matched (cons number matched)))))
361       (digest
362        (mapatoms
363         (lambda (atom)
364           (setq entry (symbol-value atom))
365           (when (modb-standard-match-flags '(unread important)
366                                            (cdr entry))
367             (setq matched (cons (car entry) matched))))
368         (modb-standard-flag-map msgdb)))
369       (any
370        (mapatoms
371         (lambda (atom)
372           (setq entry (symbol-value atom))
373           (when (modb-standard-match-flags '(unread important answered)
374                                            (cdr entry))
375             (setq matched (cons (car entry) matched))))
376         (modb-standard-flag-map msgdb)))
377       (t
378        (mapatoms
379         (lambda (atom)
380           (setq entry (symbol-value atom))
381           (when (memq flag (cdr entry))
382             (setq matched (cons (car entry) matched))))
383         (modb-standard-flag-map msgdb))))
384     matched))
385
386 (luna-define-method elmo-msgdb-search ((msgdb modb-standard)
387                                        condition &optional numbers)
388   (if (vectorp condition)
389       (let ((key (elmo-filter-key condition))
390             results)
391         (cond
392          ((and (string= key "flag")
393                (eq (elmo-filter-type condition) 'match))
394           (setq results (elmo-msgdb-list-flagged
395                          msgdb
396                          (intern (elmo-filter-value condition))))
397           (if numbers
398               (elmo-list-filter numbers results)
399             results))
400          ((member key '("first" "last"))
401           (let* ((numbers (or numbers
402                               (modb-standard-number-list-internal msgdb)))
403                  (len (length numbers))
404                  (lastp (string= key "last"))
405                  (value (string-to-number (elmo-filter-value condition))))
406             (when (eq (elmo-filter-type condition) 'unmatch)
407               (setq lastp (not lastp)
408                     value (- len value)))
409             (if lastp
410                 (nthcdr (max (- len value) 0) numbers)
411               (when (> value 0)
412                 (let* ((numbers (copy-sequence numbers))
413                        (last (nthcdr (1- value) numbers)))
414                   (when last
415                     (setcdr last nil))
416                   numbers)))))
417          (t
418           t)))
419     t))
420
421 (luna-define-method elmo-msgdb-append-entity ((msgdb modb-standard)
422                                               entity &optional flags)
423   (let ((number (elmo-msgdb-message-entity-number
424                  (elmo-message-entity-handler entity) entity))
425         (msg-id (elmo-msgdb-message-entity-field
426                  (elmo-message-entity-handler entity) entity 'message-id))
427         duplicate)
428     ;; number-list
429     (modb-standard-set-number-list-internal
430      msgdb
431      (nconc (modb-standard-number-list-internal msgdb)
432             (list number)))
433     ;; entity-map
434     (let ((table (modb-standard-entity-map msgdb)))
435       (setq duplicate (elmo-get-hash-val msg-id table))
436       (elmo-set-hash-val (modb-standard-key number) entity table)
437       (elmo-set-hash-val msg-id entity table))
438     ;; modification flags
439     (modb-standard-set-message-modified msgdb number)
440     ;; flag-map
441     (when flags
442       (elmo-set-hash-val
443        (modb-standard-key number)
444        (cons number flags)
445        (modb-standard-flag-map msgdb))
446       (modb-standard-countup-flags msgdb flags)
447       (modb-standard-set-flag-modified msgdb number))
448     duplicate))
449
450 (luna-define-method elmo-msgdb-delete-messages ((msgdb modb-standard)
451                                                 numbers)
452   (let ((number-list (modb-standard-number-list-internal msgdb))
453         (entity-map (modb-standard-entity-map-internal msgdb))
454         (flag-map (modb-standard-flag-map-internal msgdb))
455         key entity)
456     (dolist (number numbers)
457       (setq key (modb-standard-key number)
458             entity (elmo-get-hash-val key entity-map))
459       ;; number-list
460       (setq number-list (delq number number-list))
461       ;; entity-map
462       (elmo-clear-hash-val key entity-map)
463       (elmo-clear-hash-val (modb-standard-entity-id entity) entity-map)
464       ;; flag-count (must be BEFORE flag-map)
465       (modb-standard-countup-flags
466        msgdb
467        (modb-standard-message-flags msgdb number)
468        -1)
469       ;; flag-map
470       (elmo-clear-hash-val key flag-map)
471       (modb-standard-set-message-modified msgdb number)
472       (modb-standard-set-flag-modified msgdb number))
473     (modb-standard-set-number-list-internal msgdb number-list)
474     (modb-standard-set-entity-map-internal msgdb entity-map)
475     (modb-standard-set-flag-map-internal msgdb flag-map)))
476
477 (luna-define-method elmo-msgdb-sort-entities ((msgdb modb-standard)
478                                               predicate &optional app-data)
479   (message "Sorting...")
480   (let ((numbers (modb-standard-number-list-internal msgdb)))
481     (modb-standard-set-number-list-internal
482      msgdb
483      (sort numbers (lambda (a b)
484                      (funcall predicate
485                               (elmo-msgdb-message-entity msgdb a)
486                               (elmo-msgdb-message-entity msgdb b)
487                               app-data))))
488     (message "Sorting...done")
489     msgdb))
490
491 (luna-define-method elmo-msgdb-message-entity ((msgdb modb-standard) key)
492   (let ((ret (and key
493                   (elmo-get-hash-val
494                    (cond ((stringp key) key)
495                          ((numberp key) (modb-standard-key key)))
496                    (modb-standard-entity-map-internal msgdb)))))
497     (if (eq 'autoload (car-safe ret))
498         (when modb-standard-divide-number
499           (modb-standard-load-entity
500            msgdb
501            (elmo-msgdb-location msgdb)
502            (/ (nth 1 ret) modb-standard-divide-number))
503           (elmo-get-hash-val
504            (cond ((stringp key) key)
505                  ((numberp key) (modb-standard-key key)))
506            (modb-standard-entity-map-internal msgdb)))
507       ret)))
508
509 (require 'product)
510 (product-provide (provide 'modb-standard) (require 'elmo-version))
511
512 ;;; modb-standard.el ends here