Update copyright year to 2015
[emacs.git] / lisp / emacs-lisp / eieio-base.el
blob7478908051ccc310df37101010320e6b75b171c2
1 ;;; eieio-base.el --- Base classes for EIEIO. -*- lexical-binding:t -*-
3 ;;; Copyright (C) 2000-2002, 2004-2005, 2007-2015 Free Software
4 ;;; Foundation, Inc.
6 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; Keywords: OO, lisp
8 ;; Package: eieio
10 ;; This file is part of GNU Emacs.
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.
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.
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/>.
25 ;;; Commentary:
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.
31 ;;; Code:
33 (require 'eieio)
34 (eval-when-compile (require 'cl-lib))
36 ;;; eieio-instance-inheritor
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-child
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.")
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)
55 (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 (call-next-method)))
66 (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 (make-vector (length obj) eieio-unbound))
70 (nm (eieio--object-name obj))
71 (passname (and params (stringp (car params))))
72 (num 1))
73 (aset nobj 0 'object)
74 (setf (eieio--object-class nobj) (eieio--object-class obj))
75 ;; The following was copied from the default clone.
76 (if (not passname)
77 (save-match-data
78 (if (string-match "-\\([0-9]+\\)" nm)
79 (setq num (1+ (string-to-number (match-string 1 nm)))
80 nm (substring nm 0 (match-beginning 0))))
81 (setf (eieio--object-name nobj) (concat nm "-" (int-to-string num))))
82 (setf (eieio--object-name nobj) (car params)))
83 ;; Now initialize from params.
84 (if params (shared-initialize nobj (if passname (cdr params) params)))
85 (oset nobj parent-instance obj)
86 nobj))
88 (defmethod eieio-instance-inheritor-slot-boundp ((object eieio-instance-inheritor)
89 slot)
90 "Return non-nil if the instance inheritor OBJECT's SLOT is bound.
91 See `slot-boundp' for details on binding slots.
92 The instance inheritor uses unbound slots as a way of cascading cloned
93 slot values, so testing for a slot being bound requires extra steps
94 for this kind of object."
95 (if (slot-boundp object slot)
96 ;; If it is regularly bound, return t.
98 (if (slot-boundp object 'parent-instance)
99 (eieio-instance-inheritor-slot-boundp (oref object parent-instance)
100 slot)
101 nil)))
104 ;;; eieio-instance-tracker
106 ;; Track all created instances of this class.
107 ;; The class must initialize the `tracking-symbol' slot, and that
108 ;; symbol is then used to contain these objects.
109 (defclass eieio-instance-tracker ()
110 ((tracking-symbol :type symbol
111 :allocation :class
112 :documentation
113 "The symbol used to maintain a list of our instances.
114 The instance list is treated as a variable, with new instances added to it.")
116 "This special class enables instance tracking.
117 Inheritors from this class must overload `tracking-symbol' which is
118 a variable symbol used to store a list of all instances."
119 :abstract t)
121 (defmethod initialize-instance :AFTER ((this eieio-instance-tracker)
122 &rest _slots)
123 "Make sure THIS is in our master list of this class.
124 Optional argument SLOTS are the initialization arguments."
125 ;; Theoretically, this is never called twice for a given instance.
126 (let ((sym (oref this tracking-symbol)))
127 (if (not (memq this (symbol-value sym)))
128 (set sym (append (symbol-value sym) (list this))))))
130 (defmethod delete-instance ((this eieio-instance-tracker))
131 "Remove THIS from the master list of this class."
132 (set (oref this tracking-symbol)
133 (delq this (symbol-value (oref this tracking-symbol)))))
135 ;; In retrospect, this is a silly function.
136 (defun eieio-instance-tracker-find (key slot list-symbol)
137 "Find KEY as an element of SLOT in the objects in LIST-SYMBOL.
138 Returns the first match."
139 (object-assoc key slot (symbol-value list-symbol)))
141 ;;; eieio-singleton
143 ;; The singleton Design Pattern specifies that there is but one object
144 ;; of a given class ever created. The EIEIO singleton base class defines
145 ;; a CLASS allocated slot which contains the instance used. All calls to
146 ;; `make-instance' will either create a new instance and store it in this
147 ;; slot, or it will just return what is there.
148 (defclass eieio-singleton ()
149 ((singleton :type eieio-singleton
150 :allocation :class
151 :documentation
152 "The only instance of this class that will be instantiated.
153 Multiple calls to `make-instance' will return this object."))
154 "This special class causes subclasses to be singletons.
155 A singleton is a class which will only ever have one instance."
156 :abstract t)
158 (defmethod constructor :STATIC ((class eieio-singleton) _name &rest _slots)
159 "Constructor for singleton CLASS.
160 NAME and SLOTS initialize the new object.
161 This constructor guarantees that no matter how many you request,
162 only one object ever exists."
163 ;; NOTE TO SELF: In next version, make `slot-boundp' support classes
164 ;; with class allocated slots or default values.
165 (let ((old (oref-default class singleton)))
166 (if (eq old eieio-unbound)
167 (oset-default class singleton (call-next-method))
168 old)))
171 ;;; eieio-persistent
173 ;; For objects which must save themselves to disk. Provides an
174 ;; `object-write' method to save an object to disk, and a
175 ;; `eieio-persistent-read' function to call to read an object
176 ;; from disk.
178 ;; Also provide the method `eieio-persistent-path-relative' to
179 ;; calculate path names relative to a given instance. This will
180 ;; make the saved object location independent by converting all file
181 ;; references to be relative to the directory the object is saved to.
182 ;; You must call `eieio-persistent-path-relative' on each file name
183 ;; saved in your object.
184 (defclass eieio-persistent ()
185 ((file :initarg :file
186 :type string
187 :documentation
188 "The save file for this persistent object.
189 This must be a string, and must be specified when the new object is
190 instantiated.")
191 (extension :type string
192 :allocation :class
193 :initform ".eieio"
194 :documentation
195 "Extension of files saved by this object.
196 Enables auto-choosing nice file names based on name.")
197 (file-header-line :type string
198 :allocation :class
199 :initform ";; EIEIO PERSISTENT OBJECT"
200 :documentation
201 "Header line for the save file.
202 This is used with the `object-write' method.")
203 (do-backups :type boolean
204 :allocation :class
205 :initform t
206 :documentation
207 "Saving this object should make backup files.
208 Setting to nil will mean no backups are made."))
209 "This special class enables persistence through save files
210 Use the `object-save' method to write this object to disk. The save
211 format is Emacs Lisp code which calls the constructor for the saved
212 object. For this reason, only slots which do not have an `:initarg'
213 specified will not be saved."
214 :abstract t)
216 (defmethod eieio-persistent-save-interactive ((this eieio-persistent) prompt
217 &optional name)
218 "Prepare to save THIS. Use in an `interactive' statement.
219 Query user for file name with PROMPT if THIS does not yet specify
220 a file. Optional argument NAME specifies a default file name."
221 (unless (slot-boundp this 'file)
222 (oset this file
223 (read-file-name prompt nil
224 (if name
225 (concat name (oref this extension))
226 ))))
227 (oref this file))
229 (defun eieio-persistent-read (filename &optional class allow-subclass)
230 "Read a persistent object from FILENAME, and return it.
231 Signal an error if the object in FILENAME is not a constructor
232 for CLASS. Optional ALLOW-SUBCLASS says that it is ok for
233 `eieio-persistent-read' to load in subclasses of class instead of
234 being pedantic."
235 (unless class
236 (message "Unsafe call to `eieio-persistent-read'."))
237 (when class (eieio--check-type class-p class))
238 (let ((ret nil)
239 (buffstr nil))
240 (unwind-protect
241 (progn
242 (with-current-buffer (get-buffer-create " *tmp eieio read*")
243 (insert-file-contents filename nil nil nil t)
244 (goto-char (point-min))
245 (setq buffstr (buffer-string)))
246 ;; Do the read in the buffer the read was initialized from
247 ;; so that any initialize-instance calls that depend on
248 ;; the current buffer will work.
249 (setq ret (read buffstr))
250 (when (not (child-of-class-p (car ret) 'eieio-persistent))
251 (error "Corrupt object on disk: Unknown saved object"))
252 (when (and class
253 (not (or (eq (car ret) class ) ; same class
254 (and allow-subclass
255 (child-of-class-p (car ret) class)) ; subclasses
257 (error "Corrupt object on disk: Invalid saved class"))
258 (setq ret (eieio-persistent-convert-list-to-object ret))
259 (oset ret file filename))
260 (kill-buffer " *tmp eieio read*"))
261 ret))
263 (defun eieio-persistent-convert-list-to-object (inputlist)
264 "Convert the INPUTLIST, representing object creation to an object.
265 While it is possible to just `eval' the INPUTLIST, this code instead
266 validates the existing list, and explicitly creates objects instead of
267 calling eval. This avoids the possibility of accidentally running
268 malicious code.
270 Note: This function recurses when a slot of :type of some object is
271 identified, and needing more object creation."
272 (let ((objclass (nth 0 inputlist))
273 (objname (nth 1 inputlist))
274 (slots (nthcdr 2 inputlist))
275 (createslots nil))
277 ;; If OBJCLASS is an eieio autoload object, then we need to load it.
278 (eieio-class-un-autoload objclass)
280 (while slots
281 (let ((name (car slots))
282 (value (car (cdr slots))))
284 ;; Make sure that the value proposed for SLOT is valid.
285 ;; In addition, strip out quotes, list functions, and update
286 ;; object constructors as needed.
287 (setq value (eieio-persistent-validate/fix-slot-value
288 objclass name value))
290 (push name createslots)
291 (push value createslots)
294 (setq slots (cdr (cdr slots))))
296 (apply 'make-instance objclass objname (nreverse createslots))
298 ;;(eval inputlist)
301 (defun eieio-persistent-validate/fix-slot-value (class slot proposed-value)
302 "Validate that in CLASS, the SLOT with PROPOSED-VALUE is good, then fix.
303 A limited number of functions, such as quote, list, and valid object
304 constructor functions are considered valid.
305 Second, any text properties will be stripped from strings."
306 (cond ((consp proposed-value)
307 ;; Lists with something in them need special treatment.
308 (let ((slot-idx (eieio-slot-name-index class nil slot))
309 (type nil)
310 (classtype nil))
311 (setq slot-idx (- slot-idx 3))
312 (setq type (aref (eieio--class-public-type (class-v class))
313 slot-idx))
315 (setq classtype (eieio-persistent-slot-type-is-class-p
316 type))
318 (cond ((eq (car proposed-value) 'quote)
319 (car (cdr proposed-value)))
321 ;; An empty list sometimes shows up as (list), which is dumb, but
322 ;; we need to support it for backward compat.
323 ((and (eq (car proposed-value) 'list)
324 (= (length proposed-value) 1))
325 nil)
327 ;; We have a slot with a single object that can be
328 ;; saved here. Recurse and evaluate that
329 ;; sub-object.
330 ((and classtype (class-p classtype)
331 (child-of-class-p (car proposed-value) classtype))
332 (eieio-persistent-convert-list-to-object
333 proposed-value))
335 ;; List of object constructors.
336 ((and (eq (car proposed-value) 'list)
337 ;; 2nd item is a list.
338 (consp (car (cdr proposed-value)))
339 ;; 1st elt of 2nd item is a class name.
340 (class-p (car (car (cdr proposed-value))))
343 ;; Check the value against the input class type.
344 ;; If something goes wrong, issue a smart warning
345 ;; about how a :type is needed for this to work.
346 (unless (and
347 ;; Do we have a type?
348 (consp classtype) (class-p (car classtype)))
349 (error "In save file, list of object constructors found, but no :type specified for slot %S"
350 slot))
352 ;; We have a predicate, but it doesn't satisfy the predicate?
353 (dolist (PV (cdr proposed-value))
354 (unless (child-of-class-p (car PV) (car classtype))
355 (error "Corrupt object on disk")))
357 ;; We have a list of objects here. Lets load them
358 ;; in.
359 (let ((objlist nil))
360 (dolist (subobj (cdr proposed-value))
361 (push (eieio-persistent-convert-list-to-object subobj)
362 objlist))
363 ;; return the list of objects ... reversed.
364 (nreverse objlist)))
366 proposed-value))))
368 ((stringp proposed-value)
369 ;; Else, check for strings, remove properties.
370 (substring-no-properties proposed-value))
373 ;; Else, just return whatever the constant was.
374 proposed-value))
377 (defun eieio-persistent-slot-type-is-class-p (type)
378 "Return the class refered to in TYPE.
379 If no class is referenced there, then return nil."
380 (cond ((class-p type)
381 ;; If the type is a class, then return it.
382 type)
384 ((and (symbolp type) (string-match "-child$" (symbol-name type))
385 (class-p (intern-soft (substring (symbol-name type) 0
386 (match-beginning 0)))))
387 ;; If it is the predicate ending with -child, then return
388 ;; that class. Unfortunately, in EIEIO, typep of just the
389 ;; class is the same as if we used -child, so no further work needed.
390 (intern-soft (substring (symbol-name type) 0
391 (match-beginning 0))))
393 ((and (symbolp type) (string-match "-list$" (symbol-name type))
394 (class-p (intern-soft (substring (symbol-name type) 0
395 (match-beginning 0)))))
396 ;; If it is the predicate ending with -list, then return
397 ;; that class and the predicate to use.
398 (cons (intern-soft (substring (symbol-name type) 0
399 (match-beginning 0)))
400 type))
402 ((and (consp type) (eq (car type) 'or))
403 ;; If type is a list, and is an or, it is possibly something
404 ;; like (or null myclass), so check for that.
405 (let ((ans nil))
406 (dolist (subtype (cdr type))
407 (setq ans (eieio-persistent-slot-type-is-class-p
408 subtype)))
409 ans))
412 ;; No match, not a class.
413 nil)))
415 (defmethod object-write ((this eieio-persistent) &optional comment)
416 "Write persistent object THIS out to the current stream.
417 Optional argument COMMENT is a header line comment."
418 (call-next-method this (or comment (oref this file-header-line))))
420 (defmethod eieio-persistent-path-relative ((this eieio-persistent) file)
421 "For object THIS, make absolute file name FILE relative."
422 (file-relative-name (expand-file-name file)
423 (file-name-directory (oref this file))))
425 (defmethod eieio-persistent-save ((this eieio-persistent) &optional file)
426 "Save persistent object THIS to disk.
427 Optional argument FILE overrides the file name specified in the object
428 instance."
429 (save-excursion
430 (let ((b (set-buffer (get-buffer-create " *tmp object write*")))
431 (default-directory (file-name-directory (oref this file)))
432 (cfn (oref this file)))
433 (unwind-protect
434 (save-excursion
435 (erase-buffer)
436 (let ((standard-output (current-buffer)))
437 (oset this file
438 (if file
439 (eieio-persistent-path-relative this file)
440 (file-name-nondirectory cfn)))
441 (object-write this (oref this file-header-line)))
442 (let ((backup-inhibited (not (oref this do-backups)))
443 (cs (car (find-coding-systems-region
444 (point-min) (point-max)))))
445 (unless (eq cs 'undecided)
446 (setq buffer-file-coding-system cs))
447 ;; Old way - write file. Leaves message behind.
448 ;;(write-file cfn nil)
450 ;; New way - Avoid the vast quantities of error checking
451 ;; just so I can get at the special flags that disable
452 ;; displaying random messages.
453 (write-region (point-min) (point-max)
454 cfn nil 1)
456 ;; Restore :file, and kill the tmp buffer
457 (oset this file cfn)
458 (setq buffer-file-name nil)
459 (kill-buffer b)))))
461 ;; Notes on the persistent object:
462 ;; It should also set up some hooks to help it keep itself up to date.
465 ;;; Named object
467 ;; Named objects use the objects `name' as a slot, and that slot
468 ;; is accessed with the `object-name' symbol.
470 (defclass eieio-named ()
472 "Object with a name.
473 Name storage already occurs in an object. This object provides get/set
474 access to it."
475 :abstract t)
477 (defmethod slot-missing ((obj eieio-named)
478 slot-name operation &optional new-value)
479 "Called when a non-existent slot is accessed.
480 For variable `eieio-named', provide an imaginary `object-name' slot.
481 Argument OBJ is the named object.
482 Argument SLOT-NAME is the slot that was attempted to be accessed.
483 OPERATION is the type of access, such as `oref' or `oset'.
484 NEW-VALUE is the value that was being set into SLOT if OPERATION were
485 a set type."
486 (if (memq slot-name '(object-name :object-name))
487 (cond ((eq operation 'oset)
488 (if (not (stringp new-value))
489 (signal 'invalid-slot-type
490 (list obj slot-name 'string new-value)))
491 (eieio-object-set-name-string obj new-value))
492 (t (eieio-object-name-string obj)))
493 (call-next-method)))
495 (provide 'eieio-base)
497 ;;; eieio-base.el ends here