dependents-in-lisp-answers.lisp Unix DownloadWindows Download
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; File		     - dependents-in-lisp-answers.lisp
;; Description	     - Dependents in Lisp (requires Iterators in Lisp)
;; Author	     - Tim Bradshaw (tfb at lostwithiel.tfeb.org)
;; Created On	     - Thu Jun 29 15:02:25 2000
;; Last Modified On  - Sat Jan 27 10:15:57 2001
;; Last Modified By  - Gail Anderson (ga at lostwithiel)
;; Update Count	     - 3
;; Status	     - Unknown
;; 
;; $Id: dependents-in-lisp-answers.lisp,v 1.1.1.1 2002/12/12 02:15:47 colin Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Copyright 2000,2001 Cley Limited

;;; Note that here we build on the collectors and iterators defined for the
;;; Iterator design pattern

;;;; * Dependents & dependencies.
;;;
;;; Using the objects and methods we defined for the iterator pattern,
;;; implementing objects with dependents, & objects with dependencies
;;; is very simple.  The only trick is that we should work by having
;;; the collection object be a slot of the class rather than
;;; inheriting from it, which allows us to have objects which have
;;; both dependents and dependencies.
;;;
;;; We also paramaterise the kind of collection to use by defining a
;;; GF on the class which returns a new, empty collection, as an
;;; example of the technique.
;;;

;;; Things with dependents
;;;
;;; We dont implement the whole collection thing here, to save space.

(defgeneric add-dependent (dm dependent &optional recursivep)
  ;; see below for the optional args
  (:documentation
   "Add DEPENDENT as a dependent of DM.  Return DM"))

(defgeneric delete-dependent (dm dependent &optional recursivep)
  (:documentation
   "Remove DEPENDENT from DM.  Return DM"))

;;; No DELETE-DEPENDENT-IF

(defgeneric map-dependents (f dm)
  (:documentation
   "Map F over the dependents of DM.  Return DM"))

;;; No cursors.

(defgeneric make-collection-for-dependent-mixin (dm))

(defclass dependent-mixin ()
    ;; something that has dependents.  We expose the DEPENDENTS slot.
    ((dependents :reader dependents-of)))

(defmethod make-collection-for-dependent-mixin ((dm dependent-mixin))
  (make-instance 'simple-childed-mixin))

(defmethod initialize-instance :after ((dm dependent-mixin) &key)
  (setf (slot-value dm 'dependents)
	(make-collection-for-dependent-mixin dm)))

(defmethod add-dependent ((dm dependent-mixin) dependee
			  &optional recursivep)
  (declare (ignorable recursivep))
  (add-child (dependents-of dm) dependee)
  dm)

(defmethod delete-dependent ((dm dependent-mixin) dependee
			     &optional recursivep)
  (declare (ignorable recursivep))
  (delete-child (dependents-of dm) dependee)
  dm)

(defmethod map-dependents (f (dm dependent-mixin))
  (map-over f (dependents-of dm))
  dm)

;;; Things with dependencies
;;;
;;; We don't implement the whole collection thing here, to save space.

(defgeneric add-dependency (dm dependency &optional recursivep)
  (:documentation
   "Add DEPENDENCY as a something DM depends on.  Return DM"))

(defgeneric delete-dependency (dm dependency &optional recursivep)
  (:documentation
   "Remove DEPENDENCY from DM.  Return DM"))

;;; No DELETE-DEPENDENCY-IF

(defgeneric map-dependencies (f dm)
  (:documentation
   "Map F over the things DM depends on.  Return DM"))

;;; No cursors.

(defgeneric make-collection-for-dependency-mixin (dm))

(defclass dependency-mixin ()
    ;; something that depends on someone
    ((dependencies :reader dependencies-of)))

(defmethod make-collection-for-dependency-mixin ((dm dependency-mixin))
  (make-instance 'simple-childed-mixin))

(defmethod initialize-instance :after ((dm dependency-mixin) &key)
  (setf (slot-value dm 'dependencies)
	(make-collection-for-dependency-mixin dm)))

(defmethod add-dependency ((dm dependency-mixin) dependency
			   &optional recursivep)
  (declare (ignorable recursivep))
  (add-child (dependencies-of dm) dependency)
  dm)

(defmethod delete-dependency ((dm dependency-mixin) dependency
			      &optional recursivep)
  (declare (ignorable recursivep))
  (delete-child (dependencies-of dm) dependency)
  dm)

(defmethod map-dependencies (f (dm dependency-mixin))
  (map-over f (dependencies-of dm))
  dm)


;;; And finally, we glue everything together!  If we make x be a
;;; dependent of y, then, if y is a dependency-mixin, we need to tell
;;; it this.  Similarly for deleting, and similarly the other way
;;; around.  We do this by defining after methods which specialise on
;;; the *second* argument.  We use the obscure RECURSIVEP flag to
;;; prevent uncontrolled recursion causing death.

(defmethod add-dependent :after (dm (dependee dependency-mixin)
				    &optional recursivep)
  (unless recursivep
    (add-dependency dependee dm t)))

(defmethod add-dependency :after (dm (dependency dependent-mixin)
				     &optional recursivep)
  (unless recursivep
    (add-dependent dependency dm t)))

(defmethod delete-dependent :after (dm (dependee dependency-mixin)
				       &optional recursivep)
  (unless recursivep
    (delete-dependency dependee dm t)))

(defmethod delete-dependency :after (dm (dependency dependent-mixin)
					&optional recursivep)
  (unless recursivep
    (delete-dependent dependency dm t)))

;;; The end.