From: vnestr@netvision.net.il (Vladimir Nesterovsky) Newsgroups: autodesk.autocad.customization Subject: Re: Saving *any* Lisp values with a drawing. Date: Thu, 27 Nov 1997 00:17:29 GMT X-Newsreader: Forte Agent 1.5/32.451 Here's something that will let us store/retrieve arbitrary lists of values of (almost) *any* structure in DWG *)) with two simple function calls like this: (dict-put "name" "key" any-value) (dict-get "name" "key") These two will be implemented using dictionaries in R14 but can be done via EED just as well. -------- *)) The limitation is that dotted pairs in your data aren't handled. Use regular lists here or improve this code! :) Lists however may be of any structure, maybe deeply nested lists etc. ================================ So here's something to use. (I've finally got some free time to write this down) :) --- How to save Lisp values with drawing? --- We have two possibilities in R14 -- dictionaries and EED, extended entity data. Both provide us with a means of storing information in a DWG persistently. We can store a series of code pairs in EED, as usual, only the codes are 1000, 1040 etc. BUT -- in a real Lisp programs we often have variables that are a *lists* of data, and in Lisp a list is actually a tree in that it can contain another list and so on. So how can we store lists in EED (or in Xrecords to be put into dictionary)? We must have some way to encode it into the possible code groups, and later to decode it back while retrieving the info. Both EED and Xrecords are very similar in that, and in both cases we must use this en/decoding. How can we do that? We need to use some special marker to mark a beginning/ending of a lists. Let's use LB as a List Begin marker and LE as a list end. This all means that we need to find a way to convert e.g. (1 (2 (3 "4") 5)) into LB 1 LB 2 LB 3 "4" LE 5 LE LE and vice versa. In other words, we want to find the conversion between Lisp tree-like lists and this special one-dimentional list representation. For that we can use something similar to that RE-LIST function that I have posted several times in its various modifications. Let's use here "{" as LB and "}" as LE. RE-LIST is decoding a list back into its original LISP form and may be done like this: ;; all functions here are (C.) 1997 by Vladimir Nesterovsky, ;; vnestr@netvision.net.il, All Rights Reserved ;; please keep this notice intact (defun re-list ( lst / tok ) (re-list-aux)) (defun re-list-aux() (cond ((atom lst) lst) ((/= "}" (setq tok (pop 'lst))) (cons (if (= "{" tok) (re-list-aux) tok) (re-list-aux))))) { I'm using here a general utility POP function which is stripping first element off the list, returning its value: (defun pop(a / b) (setq b (eval a)) (set a (cdr b)) (car b)) } Its counterpart, EN-LIST, is for encoding: (defun en-list ( lst ) (cond ((null lst) lst) ((atom lst) (list lst)) ((cons "{" ;; add the markers around the list! (reverse (cons "}" (reverse (apply 'append ;; open the lists (mapcar 'en-list lst) ;; recursion! )))))))) Both don't deal with dotted pairs for simplicity. {A word of caution -- both functions may be memory hungry as being recursive, which can lead to problems when using big lists, like Autolisp stack overflow etc. I heard that every recursive function can be done iteratively, so if you have a very big lists in your application, you may need to try to prove this theorem in this case. :) } Now we've seen this encoding/decoding in action and can do any alterations suitable for EED or Xrecords (the actual codes for EED are different and long integers must be encoded as reals and converted to integers back on decoding). Here's a function for Xrecord encoding, making code groups automatically: (defun x-enlist ( lst ) ;; encode! (cond ((null lst) lst) ((atom lst) ;; automatic code groups (cond ((= 'REAL (type lst)) (list (cons 40 lst))) ((= 'INT (type lst)) (list (cons 71 lst))) ((= 'STR (type lst)) (list (cons 1 lst))) (T nil))) ((and (cdr lst) (atom (cdr lst))) (list lst)) ;; pass dotted pair AS IS -- must be valid!! ((and (= (length lst) 3) (apply 'and (mapcar 'numberp lst))) (list (cons 10 lst))) ((cons '(2 . "{") (reverse (cons '(2 . "}") (reverse (apply 'append (mapcar 'x-enlist lst) )))))))) (defun x-relist ( lst / tok ) ;; decode it! (car (x-relist-aux))) (defun x-relist-aux() (cond ((null lst) nil) ((not (equal '(2 . "}") (setq tok (pop 'lst)))) (cons (if (equal '(2 . "{") tok) (x-relist-aux) (cdr tok) ) (x-relist-aux))))) I will post the file SAVEDATA.LSP on my home page in near days with all these functions and the ones for EED with special handling of 1070 and 1071 code groups. The handling of long strings can be done just as easily via the use of special code 1001 for text parts (the short string is encoded under 1000 for EED). The rest is just plain dictionary functions for R14: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; dictionary functions for R14 ;; POSSIBLE IMPROVEMENTS: support trees of dictionaries ;; now: all custom dictionaries are under the Root. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Get Dictionary Object Name From Root Dictionary (defun dict-name ( name / d0 d ) ;; name As string (if (Setq d0 (namedobjdict) d (dictsearch d0 name)) (cdr (assoc -1 d)))) ;; Remove Dictionary From Root Dictionary (defun dict-remove ( name / d0 ) ;; name As string (if (dictsearch (Setq d0 (namedobjdict)) name) (dictremove d0 name)) ) ;; Make A New Empty Dictionary; Reset Existing. (defun dict-new ( name / d0 ) ;; name As string (if (dictsearch (Setq d0 (namedobjdict)) name) (dictremove d0 name)) (dictadd d0 name (entmakex '((0 . "DICTIONARY")(100 . "AcDbDictionary"))))) ;; List A Dictionary As Pairs {Name . Object Name} (defun dict-list ( dname / d nl ) ;; dname As object name or string else ROOT (cond ((and (Setq d (cond ((= 'ENAME (type dname)) dname) ((= 'STR (type dname)) (dict-name dname)) (T (namedobjdict)))) (Setq d (entget d))) (while (setq d (member (assoc 3 d) d)) (if (= 350 (caadr d)) (setq nl (cons (cons (cdar d) (cdadr d)) nl))) (setq d (cdr d))) (reverse nl)))) ;; Get Raw Dictionary Data for (defun dict-getrawdata ( name key / d ) ;; name and key As strings (if (setq d (dict-name name)) (dictsearch d key))) ;; Clear The From Dictionary (defun dict-clear ( name key / d) ;; name and key As strings (if (setq d (dict-name name)) (dictremove d key))) ;;;;;;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~;;;;;;; ;;;;;;; The two working functions: ;;;;;;; ;;;;;;;____________________________;;;;;;; ;; Get A Value From Dictionary (defun dict-get ( name key ) ;; name and key As strings (x-relist ;; decode the data! (cdr (member '(100 . "AcDbXrecord") (dict-getrawdata name key))))) ;; Put A ue Into Dictionary Under ;; Dictionary is created automatically if was not existing (defun dict-put ( name key val ) ;; three arguments As strings (dict-clear name key) ;; clear the old value under this Key (dictadd (cond ((dict-name name)) ((dict-new name)) ) key (entmakex (cons '(0 . "XRECORD") (cons '(100 . "AcDbXrecord") (x-enlist val)))) )) ;; encode the data! ;; ;; POSSIBLE IMPROVEMENTS: ;; implement dict-append ( APPEND PREPEND ) ;;;;;;;;;;;;;;;;;;;;;;; Sample use: (dict-put "mydict" "mykey" '(1 (2 ("3" (44.4) (5.5 6.6 7.7) 8)) 999999)) (dict-get "mydict" "mykey") ;;;;;;;-------;;;;;;; So here we have developed a set of functions that let us store and retrieve an arbitrary values of *any* structure persistently with the DWG (with a possible exception of dotted pairs). In fact it may be easily altered to store even *functions* with the DWG as functions are _lists_ in AutoLISP and all that's left to do is to define some special code for string converted symbols to be stored in Xrecord, e.g. 3, and just use SYM2STR and READ in the en/decoding functions. Enjoy! Comments and improvements are welcome. LISP code is at http://members.tripod.com/~vnestr/SaveData.lsp ------- Original message: ------- On Thu, 13 Nov 1997 15:21:32 -0500, - MozartZ wrote in autodesk.autocad.customization: >I need to know if there is a quike and eazy way to save LISp symble >values along >with the the drawing so each time the drawing is opened the LISp >routines that I'm >creating be able to remember there state the last time the drawing was >in the database > >MozartZ