]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/eieio-base.el
Merge from origin/emacs-24
[gnu-emacs] / lisp / emacs-lisp / eieio-base.el
1 ;;; eieio-base.el --- Base classes for EIEIO. -*- lexical-binding:t -*-
2
3 ;;; Copyright (C) 2000-2002, 2004-2005, 2007-2015 Free Software
4 ;;; Foundation, Inc.
5
6 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; Keywords: OO, lisp
8 ;; Package: eieio
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26 ;;
27 ;; Base classes for EIEIO. These classes perform some basic tasks
28 ;; but are generally useless on their own. To use any of these classes,
29 ;; inherit from one or more of them.
30
31 ;;; Code:
32
33 (require 'eieio)
34 (eval-when-compile (require 'cl-lib))
35
36 ;;; eieio-instance-inheritor
37 ;;
38 ;; Enable instance inheritance via the `clone' method.
39 ;; Works by using the `slot-unbound' method which usually throws an
40 ;; error if a slot is unbound.
41 (defclass eieio-instance-inheritor ()
42 ((parent-instance :initarg :parent-instance
43 :type eieio-instance-inheritor
44 :documentation
45 "The parent of this instance.
46 If a slot of this class is referenced, and is unbound, then the parent
47 is checked for a value.")
48 )
49 "This special class can enable instance inheritance.
50 Use `clone' to make a new object that does instance inheritance from
51 a parent instance. When a slot in the child is referenced, and has
52 not been set, use values from the parent."
53 :abstract t)
54
55 (cl-defmethod slot-unbound ((object eieio-instance-inheritor)
56 _class slot-name _fn)
57 "If a slot OBJECT in this CLASS is unbound, try to inherit, or throw a signal.
58 SLOT-NAME is the offending slot. FN is the function signaling the error."
59 (if (slot-boundp object 'parent-instance)
60 ;; It may not look like it, but this line recurses back into this
61 ;; method if the parent instance's slot is unbound.
62 (eieio-oref (oref object parent-instance) slot-name)
63 ;; Throw the regular signal.
64 (cl-call-next-method)))
65
66 (cl-defmethod clone ((obj eieio-instance-inheritor) &rest _params)
67 "Clone OBJ, initializing `:parent' to OBJ.
68 All slots are unbound, except those initialized with PARAMS."
69 (let ((nobj (cl-call-next-method)))
70 (oset nobj parent-instance obj)
71 nobj))
72
73 (cl-defmethod eieio-instance-inheritor-slot-boundp ((object eieio-instance-inheritor)
74 slot)
75 "Return non-nil if the instance inheritor OBJECT's SLOT is bound.
76 See `slot-boundp' for details on binding slots.
77 The instance inheritor uses unbound slots as a way of cascading cloned
78 slot values, so testing for a slot being bound requires extra steps
79 for this kind of object."
80 (if (slot-boundp object slot)
81 ;; If it is regularly bound, return t.
82 t
83 (if (slot-boundp object 'parent-instance)
84 (eieio-instance-inheritor-slot-boundp (oref object parent-instance)
85 slot)
86 nil)))
87
88 \f
89 ;;; eieio-instance-tracker
90 ;;
91 ;; Track all created instances of this class.
92 ;; The class must initialize the `tracking-symbol' slot, and that
93 ;; symbol is then used to contain these objects.
94 (defclass eieio-instance-tracker ()
95 ((tracking-symbol :type symbol
96 :allocation :class
97 :documentation
98 "The symbol used to maintain a list of our instances.
99 The instance list is treated as a variable, with new instances added to it.")
100 )
101 "This special class enables instance tracking.
102 Inheritors from this class must overload `tracking-symbol' which is
103 a variable symbol used to store a list of all instances."
104 :abstract t)
105
106 (cl-defmethod initialize-instance :after ((this eieio-instance-tracker)
107 &rest _slots)
108 "Make sure THIS is in our master list of this class.
109 Optional argument SLOTS are the initialization arguments."
110 ;; Theoretically, this is never called twice for a given instance.
111 (let ((sym (oref this tracking-symbol)))
112 (if (not (memq this (symbol-value sym)))
113 (set sym (append (symbol-value sym) (list this))))))
114
115 (cl-defmethod delete-instance ((this eieio-instance-tracker))
116 "Remove THIS from the master list of this class."
117 (set (oref this tracking-symbol)
118 (delq this (symbol-value (oref this tracking-symbol)))))
119
120 ;; In retrospect, this is a silly function.
121 (defun eieio-instance-tracker-find (key slot list-symbol)
122 "Find KEY as an element of SLOT in the objects in LIST-SYMBOL.
123 Returns the first match."
124 (object-assoc key slot (symbol-value list-symbol)))
125
126 ;;; eieio-singleton
127 ;;
128 ;; The singleton Design Pattern specifies that there is but one object
129 ;; of a given class ever created. The EIEIO singleton base class defines
130 ;; a CLASS allocated slot which contains the instance used. All calls to
131 ;; `make-instance' will either create a new instance and store it in this
132 ;; slot, or it will just return what is there.
133 (defclass eieio-singleton ()
134 ((singleton :type eieio-singleton
135 :allocation :class
136 :documentation
137 "The only instance of this class that will be instantiated.
138 Multiple calls to `make-instance' will return this object."))
139 "This special class causes subclasses to be singletons.
140 A singleton is a class which will only ever have one instance."
141 :abstract t)
142
143 (cl-defmethod eieio-constructor ((class (subclass eieio-singleton)) &rest _slots)
144 "Constructor for singleton CLASS.
145 NAME and SLOTS initialize the new object.
146 This constructor guarantees that no matter how many you request,
147 only one object ever exists."
148 ;; NOTE TO SELF: In next version, make `slot-boundp' support classes
149 ;; with class allocated slots or default values.
150 (let ((old (oref-default class singleton)))
151 (if (eq old eieio-unbound)
152 (oset-default class singleton (cl-call-next-method))
153 old)))
154
155 \f
156 ;;; eieio-persistent
157 ;;
158 ;; For objects which must save themselves to disk. Provides an
159 ;; `object-write' method to save an object to disk, and a
160 ;; `eieio-persistent-read' function to call to read an object
161 ;; from disk.
162 ;;
163 ;; Also provide the method `eieio-persistent-path-relative' to
164 ;; calculate path names relative to a given instance. This will
165 ;; make the saved object location independent by converting all file
166 ;; references to be relative to the directory the object is saved to.
167 ;; You must call `eieio-persistent-path-relative' on each file name
168 ;; saved in your object.
169 (defclass eieio-persistent ()
170 ((file :initarg :file
171 :type string
172 :documentation
173 "The save file for this persistent object.
174 This must be a string, and must be specified when the new object is
175 instantiated.")
176 (extension :type string
177 :allocation :class
178 :initform ".eieio"
179 :documentation
180 "Extension of files saved by this object.
181 Enables auto-choosing nice file names based on name.")
182 (file-header-line :type string
183 :allocation :class
184 :initform ";; EIEIO PERSISTENT OBJECT"
185 :documentation
186 "Header line for the save file.
187 This is used with the `object-write' method.")
188 (do-backups :type boolean
189 :allocation :class
190 :initform t
191 :documentation
192 "Saving this object should make backup files.
193 Setting to nil will mean no backups are made."))
194 "This special class enables persistence through save files
195 Use the `object-save' method to write this object to disk. The save
196 format is Emacs Lisp code which calls the constructor for the saved
197 object. For this reason, only slots which do not have an `:initarg'
198 specified will not be saved."
199 :abstract t)
200
201 (cl-defmethod eieio-persistent-save-interactive ((this eieio-persistent) prompt
202 &optional name)
203 "Prepare to save THIS. Use in an `interactive' statement.
204 Query user for file name with PROMPT if THIS does not yet specify
205 a file. Optional argument NAME specifies a default file name."
206 (unless (slot-boundp this 'file)
207 (oset this file
208 (read-file-name prompt nil
209 (if name
210 (concat name (oref this extension))
211 ))))
212 (oref this file))
213
214 (defun eieio-persistent-read (filename &optional class allow-subclass)
215 "Read a persistent object from FILENAME, and return it.
216 Signal an error if the object in FILENAME is not a constructor
217 for CLASS. Optional ALLOW-SUBCLASS says that it is ok for
218 `eieio-persistent-read' to load in subclasses of class instead of
219 being pedantic."
220 (unless class
221 (message "Unsafe call to `eieio-persistent-read'."))
222 (when class (eieio--check-type class-p class))
223 (let ((ret nil)
224 (buffstr nil))
225 (unwind-protect
226 (progn
227 (with-current-buffer (get-buffer-create " *tmp eieio read*")
228 (insert-file-contents filename nil nil nil t)
229 (goto-char (point-min))
230 (setq buffstr (buffer-string)))
231 ;; Do the read in the buffer the read was initialized from
232 ;; so that any initialize-instance calls that depend on
233 ;; the current buffer will work.
234 (setq ret (read buffstr))
235 (when (not (child-of-class-p (car ret) 'eieio-persistent))
236 (error "Corrupt object on disk: Unknown saved object"))
237 (when (and class
238 (not (or (eq (car ret) class ) ; same class
239 (and allow-subclass
240 (child-of-class-p (car ret) class)) ; subclasses
241 )))
242 (error "Corrupt object on disk: Invalid saved class"))
243 (setq ret (eieio-persistent-convert-list-to-object ret))
244 (oset ret file filename))
245 (kill-buffer " *tmp eieio read*"))
246 ret))
247
248 (defun eieio-persistent-convert-list-to-object (inputlist)
249 "Convert the INPUTLIST, representing object creation to an object.
250 While it is possible to just `eval' the INPUTLIST, this code instead
251 validates the existing list, and explicitly creates objects instead of
252 calling eval. This avoids the possibility of accidentally running
253 malicious code.
254
255 Note: This function recurses when a slot of :type of some object is
256 identified, and needing more object creation."
257 (let ((objclass (nth 0 inputlist))
258 ;; (objname (nth 1 inputlist))
259 (slots (nthcdr 2 inputlist))
260 (createslots nil))
261
262 ;; If OBJCLASS is an eieio autoload object, then we need to load it.
263 (eieio-class-un-autoload objclass)
264
265 (while slots
266 (let ((name (car slots))
267 (value (car (cdr slots))))
268
269 ;; Make sure that the value proposed for SLOT is valid.
270 ;; In addition, strip out quotes, list functions, and update
271 ;; object constructors as needed.
272 (setq value (eieio-persistent-validate/fix-slot-value
273 (eieio--class-v objclass) name value))
274
275 (push name createslots)
276 (push value createslots)
277 )
278
279 (setq slots (cdr (cdr slots))))
280
281 (apply #'make-instance objclass (nreverse createslots))
282
283 ;;(eval inputlist)
284 ))
285
286 (defun eieio-persistent-validate/fix-slot-value (class slot proposed-value)
287 "Validate that in CLASS, the SLOT with PROPOSED-VALUE is good, then fix.
288 A limited number of functions, such as quote, list, and valid object
289 constructor functions are considered valid.
290 Second, any text properties will be stripped from strings."
291 (cond ((consp proposed-value)
292 ;; Lists with something in them need special treatment.
293 (let ((slot-idx (eieio--slot-name-index class
294 nil slot))
295 (type nil)
296 (classtype nil))
297 (setq slot-idx (- slot-idx
298 (eval-when-compile eieio--object-num-slots)))
299 (setq type (aref (eieio--class-public-type class)
300 slot-idx))
301
302 (setq classtype (eieio-persistent-slot-type-is-class-p
303 type))
304
305 (cond ((eq (car proposed-value) 'quote)
306 (car (cdr proposed-value)))
307
308 ;; An empty list sometimes shows up as (list), which is dumb, but
309 ;; we need to support it for backward compat.
310 ((and (eq (car proposed-value) 'list)
311 (= (length proposed-value) 1))
312 nil)
313
314 ;; We have a slot with a single object that can be
315 ;; saved here. Recurse and evaluate that
316 ;; sub-object.
317 ((and classtype (class-p classtype)
318 (child-of-class-p (car proposed-value) classtype))
319 (eieio-persistent-convert-list-to-object
320 proposed-value))
321
322 ;; List of object constructors.
323 ((and (eq (car proposed-value) 'list)
324 ;; 2nd item is a list.
325 (consp (car (cdr proposed-value)))
326 ;; 1st elt of 2nd item is a class name.
327 (class-p (car (car (cdr proposed-value))))
328 )
329
330 ;; Check the value against the input class type.
331 ;; If something goes wrong, issue a smart warning
332 ;; about how a :type is needed for this to work.
333 (unless (and
334 ;; Do we have a type?
335 (consp classtype) (class-p (car classtype)))
336 (error "In save file, list of object constructors found, but no :type specified for slot %S of type %S"
337 slot classtype))
338
339 ;; We have a predicate, but it doesn't satisfy the predicate?
340 (dolist (PV (cdr proposed-value))
341 (unless (child-of-class-p (car PV) (car classtype))
342 (error "Corrupt object on disk")))
343
344 ;; We have a list of objects here. Lets load them
345 ;; in.
346 (let ((objlist nil))
347 (dolist (subobj (cdr proposed-value))
348 (push (eieio-persistent-convert-list-to-object subobj)
349 objlist))
350 ;; return the list of objects ... reversed.
351 (nreverse objlist)))
352 (t
353 proposed-value))))
354
355 ((stringp proposed-value)
356 ;; Else, check for strings, remove properties.
357 (substring-no-properties proposed-value))
358
359 (t
360 ;; Else, just return whatever the constant was.
361 proposed-value))
362 )
363
364 (defun eieio-persistent-slot-type-is-class-p (type)
365 "Return the class referred to in TYPE.
366 If no class is referenced there, then return nil."
367 (cond ((class-p type)
368 ;; If the type is a class, then return it.
369 type)
370 ((and (eq 'list-of (car-safe type)) (class-p (cadr type)))
371 ;; If it is the type of a list of a class, then return that class and
372 ;; the type.
373 (cons (cadr type) type))
374
375 ((and (symbolp type) (get type 'cl-deftype-handler))
376 ;; Macro-expand the type according to cl-deftype definitions.
377 (eieio-persistent-slot-type-is-class-p
378 (funcall (get type 'cl-deftype-handler))))
379
380 ;; FIXME: foo-child should not be a valid type!
381 ((and (symbolp type) (string-match "-child\\'" (symbol-name type))
382 (class-p (intern-soft (substring (symbol-name type) 0
383 (match-beginning 0)))))
384 (unless eieio-backward-compatibility
385 (error "Use of bogus %S type instead of %S"
386 type (intern-soft (substring (symbol-name type) 0
387 (match-beginning 0)))))
388 ;; If it is the predicate ending with -child, then return
389 ;; that class. Unfortunately, in EIEIO, typep of just the
390 ;; class is the same as if we used -child, so no further work needed.
391 (intern-soft (substring (symbol-name type) 0
392 (match-beginning 0))))
393 ;; FIXME: foo-list should not be a valid type!
394 ((and (symbolp type) (string-match "-list\\'" (symbol-name type))
395 (class-p (intern-soft (substring (symbol-name type) 0
396 (match-beginning 0)))))
397 (unless eieio-backward-compatibility
398 (error "Use of bogus %S type instead of (list-of %S)"
399 type (intern-soft (substring (symbol-name type) 0
400 (match-beginning 0)))))
401 ;; If it is the predicate ending with -list, then return
402 ;; that class and the predicate to use.
403 (cons (intern-soft (substring (symbol-name type) 0
404 (match-beginning 0)))
405 type))
406
407 ((eq (car-safe type) 'or)
408 ;; If type is a list, and is an or, it is possibly something
409 ;; like (or null myclass), so check for that.
410 (let ((ans nil))
411 (dolist (subtype (cdr type))
412 (setq ans (eieio-persistent-slot-type-is-class-p
413 subtype)))
414 ans))
415
416 (t
417 ;; No match, not a class.
418 nil)))
419
420 (cl-defmethod object-write ((this eieio-persistent) &optional comment)
421 "Write persistent object THIS out to the current stream.
422 Optional argument COMMENT is a header line comment."
423 (cl-call-next-method this (or comment (oref this file-header-line))))
424
425 (cl-defmethod eieio-persistent-path-relative ((this eieio-persistent) file)
426 "For object THIS, make absolute file name FILE relative."
427 (file-relative-name (expand-file-name file)
428 (file-name-directory (oref this file))))
429
430 (cl-defmethod eieio-persistent-save ((this eieio-persistent) &optional file)
431 "Save persistent object THIS to disk.
432 Optional argument FILE overrides the file name specified in the object
433 instance."
434 (save-excursion
435 (let ((b (set-buffer (get-buffer-create " *tmp object write*")))
436 (default-directory (file-name-directory (oref this file)))
437 (cfn (oref this file)))
438 (unwind-protect
439 (save-excursion
440 (erase-buffer)
441 (let ((standard-output (current-buffer)))
442 (oset this file
443 (if file
444 (eieio-persistent-path-relative this file)
445 (file-name-nondirectory cfn)))
446 (object-write this (oref this file-header-line)))
447 (let ((backup-inhibited (not (oref this do-backups)))
448 (cs (car (find-coding-systems-region
449 (point-min) (point-max)))))
450 (unless (eq cs 'undecided)
451 (setq buffer-file-coding-system cs))
452 ;; Old way - write file. Leaves message behind.
453 ;;(write-file cfn nil)
454
455 ;; New way - Avoid the vast quantities of error checking
456 ;; just so I can get at the special flags that disable
457 ;; displaying random messages.
458 (write-region (point-min) (point-max)
459 cfn nil 1)
460 ))
461 ;; Restore :file, and kill the tmp buffer
462 (oset this file cfn)
463 (setq buffer-file-name nil)
464 (kill-buffer b)))))
465
466 ;; Notes on the persistent object:
467 ;; It should also set up some hooks to help it keep itself up to date.
468
469 \f
470 ;;; Named object
471
472 (defclass eieio-named ()
473 ((object-name :initarg :object-name :initform nil))
474 "Object with a name."
475 :abstract t)
476
477 (cl-defmethod eieio-object-name-string ((obj eieio-named))
478 "Return a string which is OBJ's name."
479 (or (slot-value obj 'object-name)
480 (symbol-name (eieio-object-class obj))))
481
482 (cl-defmethod eieio-object-set-name-string ((obj eieio-named) name)
483 "Set the string which is OBJ's NAME."
484 (eieio--check-type stringp name)
485 (eieio-oset obj 'object-name name))
486
487 (cl-defmethod clone ((obj eieio-named) &rest params)
488 "Clone OBJ, initializing `:parent' to OBJ.
489 All slots are unbound, except those initialized with PARAMS."
490 (let* ((newname (and (stringp (car params)) (pop params)))
491 (nobj (apply #'cl-call-next-method obj params))
492 (nm (slot-value obj 'object-name)))
493 (eieio-oset obj 'object-name
494 (or newname
495 (save-match-data
496 (if (and nm (string-match "-\\([0-9]+\\)" nm))
497 (let ((num (1+ (string-to-number
498 (match-string 1 nm)))))
499 (concat (substring nm 0 (match-beginning 0))
500 "-" (int-to-string num)))
501 (concat nm "-1")))))
502 nobj))
503
504 (provide 'eieio-base)
505
506 ;;; eieio-base.el ends here