eieio-base.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. ;;; eieio-base.el --- Base classes for EIEIO.
  2. ;;; Copyright (C) 2000-2002, 2004-2005, 2007-2012
  3. ;;; Free Software Foundation, Inc.
  4. ;; Author: Eric M. Ludlam <zappo@gnu.org>
  5. ;; Version: 0.2
  6. ;; Keywords: OO, lisp
  7. ;; Package: eieio
  8. ;; This file is part of GNU Emacs.
  9. ;; GNU Emacs is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; GNU Emacs is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;;
  21. ;; Base classes for EIEIO. These classes perform some basic tasks
  22. ;; but are generally useless on their own. To use any of these classes,
  23. ;; inherit from one or more of them.
  24. ;;; Code:
  25. (require 'eieio)
  26. ;;; eieio-instance-inheritor
  27. ;;
  28. ;; Enable instance inheritance via the `clone' method.
  29. ;; Works by using the `slot-unbound' method which usually throws an
  30. ;; error if a slot is unbound.
  31. (defclass eieio-instance-inheritor ()
  32. ((parent-instance :initarg :parent-instance
  33. :type eieio-instance-inheritor-child
  34. :documentation
  35. "The parent of this instance.
  36. If a slot of this class is referenced, and is unbound, then the parent
  37. is checked for a value.")
  38. )
  39. "This special class can enable instance inheritance.
  40. Use `clone' to make a new object that does instance inheritance from
  41. a parent instance. When a slot in the child is referenced, and has
  42. not been set, use values from the parent."
  43. :abstract t)
  44. (defmethod slot-unbound ((object eieio-instance-inheritor) class slot-name fn)
  45. "If a slot OBJECT in this CLASS is unbound, try to inherit, or throw a signal.
  46. SLOT-NAME is the offending slot. FN is the function signaling the error."
  47. (if (slot-boundp object 'parent-instance)
  48. ;; It may not look like it, but this line recurses back into this
  49. ;; method if the parent instance's slot is unbound.
  50. (eieio-oref (oref object parent-instance) slot-name)
  51. ;; Throw the regular signal.
  52. (call-next-method)))
  53. (defmethod clone ((obj eieio-instance-inheritor) &rest params)
  54. "Clone OBJ, initializing `:parent' to OBJ.
  55. All slots are unbound, except those initialized with PARAMS."
  56. (let ((nobj (make-vector (length obj) eieio-unbound))
  57. (nm (aref obj object-name))
  58. (passname (and params (stringp (car params))))
  59. (num 1))
  60. (aset nobj 0 'object)
  61. (aset nobj object-class (aref obj object-class))
  62. ;; The following was copied from the default clone.
  63. (if (not passname)
  64. (save-match-data
  65. (if (string-match "-\\([0-9]+\\)" nm)
  66. (setq num (1+ (string-to-number (match-string 1 nm)))
  67. nm (substring nm 0 (match-beginning 0))))
  68. (aset nobj object-name (concat nm "-" (int-to-string num))))
  69. (aset nobj object-name (car params)))
  70. ;; Now initialize from params.
  71. (if params (shared-initialize nobj (if passname (cdr params) params)))
  72. (oset nobj parent-instance obj)
  73. nobj))
  74. (defmethod eieio-instance-inheritor-slot-boundp ((object eieio-instance-inheritor)
  75. slot)
  76. "Return non-nil if the instance inheritor OBJECT's SLOT is bound.
  77. See `slot-boundp' for details on binding slots.
  78. The instance inheritor uses unbound slots as a way of cascading cloned
  79. slot values, so testing for a slot being bound requires extra steps
  80. for this kind of object."
  81. (if (slot-boundp object slot)
  82. ;; If it is regularly bound, return t.
  83. t
  84. (if (slot-boundp object 'parent-instance)
  85. (eieio-instance-inheritor-slot-boundp (oref object parent-instance)
  86. slot)
  87. nil)))
  88. ;;; eieio-instance-tracker
  89. ;;
  90. ;; Track all created instances of this class.
  91. ;; The class must initialize the `tracking-symbol' slot, and that
  92. ;; symbol is then used to contain these objects.
  93. (defclass eieio-instance-tracker ()
  94. ((tracking-symbol :type symbol
  95. :allocation :class
  96. :documentation
  97. "The symbol used to maintain a list of our instances.
  98. The instance list is treated as a variable, with new instances added to it.")
  99. )
  100. "This special class enables instance tracking.
  101. Inheritors from this class must overload `tracking-symbol' which is
  102. a variable symbol used to store a list of all instances."
  103. :abstract t)
  104. (defmethod initialize-instance :AFTER ((this eieio-instance-tracker)
  105. &rest slots)
  106. "Make sure THIS is in our master list of this class.
  107. Optional argument SLOTS are the initialization arguments."
  108. ;; Theoretically, this is never called twice for a given instance.
  109. (let ((sym (oref this tracking-symbol)))
  110. (if (not (memq this (symbol-value sym)))
  111. (set sym (append (symbol-value sym) (list this))))))
  112. (defmethod delete-instance ((this eieio-instance-tracker))
  113. "Remove THIS from the master list of this class."
  114. (set (oref this tracking-symbol)
  115. (delq this (symbol-value (oref this tracking-symbol)))))
  116. ;; In retrospect, this is a silly function.
  117. (defun eieio-instance-tracker-find (key slot list-symbol)
  118. "Find KEY as an element of SLOT in the objects in LIST-SYMBOL.
  119. Returns the first match."
  120. (object-assoc key slot (symbol-value list-symbol)))
  121. ;;; eieio-singleton
  122. ;;
  123. ;; The singleton Design Pattern specifies that there is but one object
  124. ;; of a given class ever created. The EIEIO singleton base class defines
  125. ;; a CLASS allocated slot which contains the instance used. All calls to
  126. ;; `make-instance' will either create a new instance and store it in this
  127. ;; slot, or it will just return what is there.
  128. (defclass eieio-singleton ()
  129. ((singleton :type eieio-singleton
  130. :allocation :class
  131. :documentation
  132. "The only instance of this class that will be instantiated.
  133. Multiple calls to `make-instance' will return this object."))
  134. "This special class causes subclasses to be singletons.
  135. A singleton is a class which will only ever have one instance."
  136. :abstract t)
  137. (defmethod constructor :STATIC ((class eieio-singleton) name &rest slots)
  138. "Constructor for singleton CLASS.
  139. NAME and SLOTS initialize the new object.
  140. This constructor guarantees that no matter how many you request,
  141. only one object ever exists."
  142. ;; NOTE TO SELF: In next version, make `slot-boundp' support classes
  143. ;; with class allocated slots or default values.
  144. (let ((old (oref-default class singleton)))
  145. (if (eq old eieio-unbound)
  146. (oset-default class singleton (call-next-method))
  147. old)))
  148. ;;; eieio-persistent
  149. ;;
  150. ;; For objects which must save themselves to disk. Provides an
  151. ;; `object-write' method to save an object to disk, and a
  152. ;; `eieio-persistent-read' function to call to read an object
  153. ;; from disk.
  154. ;;
  155. ;; Also provide the method `eieio-persistent-path-relative' to
  156. ;; calculate path names relative to a given instance. This will
  157. ;; make the saved object location independent by converting all file
  158. ;; references to be relative to the directory the object is saved to.
  159. ;; You must call `eieio-persistent-path-relative' on each file name
  160. ;; saved in your object.
  161. (defclass eieio-persistent ()
  162. ((file :initarg :file
  163. :type string
  164. :documentation
  165. "The save file for this persistent object.
  166. This must be a string, and must be specified when the new object is
  167. instantiated.")
  168. (extension :type string
  169. :allocation :class
  170. :initform ".eieio"
  171. :documentation
  172. "Extension of files saved by this object.
  173. Enables auto-choosing nice file names based on name.")
  174. (file-header-line :type string
  175. :allocation :class
  176. :initform ";; EIEIO PERSISTENT OBJECT"
  177. :documentation
  178. "Header line for the save file.
  179. This is used with the `object-write' method.")
  180. (do-backups :type boolean
  181. :allocation :class
  182. :initform t
  183. :documentation
  184. "Saving this object should make backup files.
  185. Setting to nil will mean no backups are made."))
  186. "This special class enables persistence through save files
  187. Use the `object-save' method to write this object to disk. The save
  188. format is Emacs Lisp code which calls the constructor for the saved
  189. object. For this reason, only slots which do not have an `:initarg'
  190. specified will not be saved."
  191. :abstract t)
  192. (defmethod eieio-persistent-save-interactive ((this eieio-persistent) prompt
  193. &optional name)
  194. "Prepare to save THIS. Use in an `interactive' statement.
  195. Query user for file name with PROMPT if THIS does not yet specify
  196. a file. Optional argument NAME specifies a default file name."
  197. (unless (slot-boundp this 'file)
  198. (oset this file
  199. (read-file-name prompt nil
  200. (if name
  201. (concat name (oref this extension))
  202. ))))
  203. (oref this file))
  204. (defun eieio-persistent-read (filename)
  205. "Read a persistent object from FILENAME, and return it."
  206. (let ((ret nil)
  207. (buffstr nil))
  208. (unwind-protect
  209. (progn
  210. (with-current-buffer (get-buffer-create " *tmp eieio read*")
  211. (insert-file-contents filename nil nil nil t)
  212. (goto-char (point-min))
  213. (setq buffstr (buffer-string)))
  214. ;; Do the read in the buffer the read was initialized from
  215. ;; so that any initialize-instance calls that depend on
  216. ;; the current buffer will work.
  217. (setq ret (read buffstr))
  218. (if (not (child-of-class-p (car ret) 'eieio-persistent))
  219. (error "Corrupt object on disk"))
  220. (setq ret (eval ret))
  221. (oset ret file filename))
  222. (kill-buffer " *tmp eieio read*"))
  223. ret))
  224. (defmethod object-write ((this eieio-persistent) &optional comment)
  225. "Write persistent object THIS out to the current stream.
  226. Optional argument COMMENT is a header line comment."
  227. (call-next-method this (or comment (oref this file-header-line))))
  228. (defmethod eieio-persistent-path-relative ((this eieio-persistent) file)
  229. "For object THIS, make absolute file name FILE relative."
  230. (file-relative-name (expand-file-name file)
  231. (file-name-directory (oref this file))))
  232. (defmethod eieio-persistent-save ((this eieio-persistent) &optional file)
  233. "Save persistent object THIS to disk.
  234. Optional argument FILE overrides the file name specified in the object
  235. instance."
  236. (save-excursion
  237. (let ((b (set-buffer (get-buffer-create " *tmp object write*")))
  238. (default-directory (file-name-directory (oref this file)))
  239. (cfn (oref this file)))
  240. (unwind-protect
  241. (save-excursion
  242. (erase-buffer)
  243. (let ((standard-output (current-buffer)))
  244. (oset this file
  245. (if file
  246. (eieio-persistent-path-relative this file)
  247. (file-name-nondirectory cfn)))
  248. (object-write this (oref this file-header-line)))
  249. (let ((backup-inhibited (not (oref this do-backups)))
  250. (cs (car (find-coding-systems-region
  251. (point-min) (point-max)))))
  252. (unless (eq cs 'undecided)
  253. (setq buffer-file-coding-system cs))
  254. ;; Old way - write file. Leaves message behind.
  255. ;;(write-file cfn nil)
  256. ;; New way - Avoid the vast quantities of error checking
  257. ;; just so I can get at the special flags that disable
  258. ;; displaying random messages.
  259. (write-region (point-min) (point-max)
  260. cfn nil 1)
  261. ))
  262. ;; Restore :file, and kill the tmp buffer
  263. (oset this file cfn)
  264. (setq buffer-file-name nil)
  265. (kill-buffer b)))))
  266. ;; Notes on the persistent object:
  267. ;; It should also set up some hooks to help it keep itself up to date.
  268. ;;; Named object
  269. ;;
  270. ;; Named objects use the objects `name' as a slot, and that slot
  271. ;; is accessed with the `object-name' symbol.
  272. (defclass eieio-named ()
  273. ()
  274. "Object with a name.
  275. Name storage already occurs in an object. This object provides get/set
  276. access to it."
  277. :abstract t)
  278. (defmethod slot-missing ((obj eieio-named)
  279. slot-name operation &optional new-value)
  280. "Called when a non-existent slot is accessed.
  281. For variable `eieio-named', provide an imaginary `object-name' slot.
  282. Argument OBJ is the named object.
  283. Argument SLOT-NAME is the slot that was attempted to be accessed.
  284. OPERATION is the type of access, such as `oref' or `oset'.
  285. NEW-VALUE is the value that was being set into SLOT if OPERATION were
  286. a set type."
  287. (if (or (eq slot-name 'object-name)
  288. (eq slot-name :object-name))
  289. (cond ((eq operation 'oset)
  290. (if (not (stringp new-value))
  291. (signal 'invalid-slot-type
  292. (list obj slot-name 'string new-value)))
  293. (object-set-name-string obj new-value))
  294. (t (object-name-string obj)))
  295. (call-next-method)))
  296. (provide 'eieio-base)
  297. ;;; eieio-base.el ends here