[s-xml-cvs] CVS update: s-xml/src/dom.lisp s-xml/src/lxml-dom.lisp s-xml/src/sxml-dom.lisp s-xml/src/xml-struct-dom.lisp s-xml/src/xml.lisp

Sven Van Caekenberghe scaekenberghe at common-lisp.net
Mon Aug 29 15:01:50 UTC 2005


Update of /project/s-xml/cvsroot/s-xml/src
In directory common-lisp.net:/tmp/cvs-serv14302/src

Modified Files:
	dom.lisp lxml-dom.lisp sxml-dom.lisp xml-struct-dom.lisp 
	xml.lisp 
Log Message:
redesigned the namespaces implementation
separated namespace definition (incl package mapping, default prefix) from namespace binding (using xmlns attributes)
cleanup of printing code (added ns-awareness)
added *ignore-namespaces* switch to disable ns-awareness (backward compatibility)

Date: Mon Aug 29 17:01:48 2005
Author: scaekenberghe

Index: s-xml/src/dom.lisp
diff -u s-xml/src/dom.lisp:1.1.1.1 s-xml/src/dom.lisp:1.2
--- s-xml/src/dom.lisp:1.1.1.1	Mon Jun  7 20:49:56 2004
+++ s-xml/src/dom.lisp	Mon Aug 29 17:01:47 2005
@@ -1,6 +1,6 @@
 ;;;; -*- mode: lisp -*-
 ;;;;
-;;;; $Id: dom.lisp,v 1.1.1.1 2004/06/07 18:49:56 scaekenberghe Exp $
+;;;; $Id: dom.lisp,v 1.2 2005/08/29 15:01:47 scaekenberghe Exp $
 ;;;;
 ;;;; This is the generic simple DOM parser and printer interface.
 ;;;;
@@ -46,5 +46,30 @@
   "Generate XML output to a string from a DOM of input-type (:lxml by default), optionally pretty printing (off by default)"
   (with-output-to-string (stream)
     (print-xml dom :stream stream :pretty pretty :input-type input-type)))
+
+;;; shared/common support functions
+
+(defun print-spaces (n stream &optional (preceding-newline t))
+  (when preceding-newline 
+    (terpri stream))
+  (loop :repeat n 
+        :do (write-char #\Space stream)))
+
+(defun print-solitary-tag (tag stream)
+  (write-char #\< stream) 
+  (print-identifier tag stream) 
+  (write-string "/>" stream))
+
+(defun print-closing-tag (tag stream)
+  (write-string "</" stream) 
+  (print-identifier tag stream) 
+  (write-char #\> stream))
+  
+(defun print-attribute (name value stream)
+  (write-char #\space stream)
+  (print-identifier name stream t)
+  (write-string "=\"" stream)
+  (print-string-xml value stream)
+  (write-char #\" stream))
 
 ;;;; eof


Index: s-xml/src/lxml-dom.lisp
diff -u s-xml/src/lxml-dom.lisp:1.3 s-xml/src/lxml-dom.lisp:1.4
--- s-xml/src/lxml-dom.lisp:1.3	Mon Aug 29 10:54:41 2005
+++ s-xml/src/lxml-dom.lisp	Mon Aug 29 17:01:47 2005
@@ -1,6 +1,6 @@
 ;;;; -*- mode: lisp -*-
 ;;;;
-;;;; $Id: lxml-dom.lisp,v 1.3 2005/08/29 08:54:41 scaekenberghe Exp $
+;;;; $Id: lxml-dom.lisp,v 1.4 2005/08/29 15:01:47 scaekenberghe Exp $
 ;;;;
 ;;;; LXML implementation of the generic DOM parser and printer.
 ;;;;
@@ -44,40 +44,40 @@
 				       :finish-element-hook #'lxml-finish-element-hook
 				       :text-hook #'lxml-text-hook))))
 
+(defun plist->alist (plist)
+  (when plist 
+    (cons (cons (first plist) (second plist))
+          (plist->alist (rest (rest plist))))))
+
 (defmethod print-xml-dom (dom (input-type (eql :lxml)) stream pretty level)
-  (cond ((symbolp dom) (format stream "<~a/>" (print-identifier dom nil)))
+  (declare (special *namespaces))
+  (cond ((symbolp dom) (print-solitary-tag dom stream))
 	((stringp dom) (print-string-xml dom stream))
 	((consp dom)
 	 (let (tag attributes)
-	   (cond ((symbolp (car dom)) (setf tag (car dom)))
-		 ((consp (car dom)) (setf tag (caar dom) attributes (cdar dom)))
+	   (cond ((symbolp (first dom)) (setf tag (first dom)))
+		 ((consp (first dom)) (setf tag (first (first dom)) 
+                                            attributes (plist->alist (rest (first dom)))))
 		 (t (error "Input not recognized as LXML ~s" dom)))
-	   (format stream "<~a" (print-identifier tag nil))
-	   (labels ((print-attributes (attributes)
-				      (unless (null attributes)
-					(format stream " ~a=\"" (print-identifier (car attributes) nil t))
-					(print-string-xml (cadr attributes) stream)
-					(format stream "\"")
-					(print-attributes (cddr attributes)))))
-	     (when attributes (print-attributes attributes)))
-	   (if (cdr dom)
-	       (let ((children (cdr dom)))
-		 (format stream ">")
-		 (if (and (= (length children) 1) (stringp (first children)))
-		     (print-string-xml (first children) stream)
-		   (progn
-		     (dolist (child children)
-		       (when pretty
-			 (terpri stream)
-			 (dotimes (i (* 2 level)) (write-char #\space stream)))
-		       (if (stringp child)
-			   (print-string-xml child stream)
-			 (print-xml-dom child input-type stream pretty (1+ level))))
-		     (when pretty
-		       (terpri stream)
-		       (dotimes (i (* 2 (1- level))) (write-char #\space stream)))))
-		 (format stream "</~a>" (print-identifier tag nil)))
-	     (format stream "/>"))))
+           (let ((*namespaces* (extend-namespaces attributes *namespaces*)))
+             (write-char #\< stream) 
+             (print-identifier tag stream)
+             (loop :for (name . value) :in attributes 
+                   :do (print-attribute name value stream))
+             (if (rest dom)
+                 (let ((children (rest dom)))
+                   (write-char #\> stream)
+                   (if (and (= (length children) 1) (stringp (first children)))
+                       (print-string-xml (first children) stream)
+                     (progn
+                       (dolist (child children)
+                         (when pretty (print-spaces (* 2 level) stream))
+                         (if (stringp child)
+                             (print-string-xml child stream)
+                           (print-xml-dom child input-type stream pretty (1+ level))))
+                       (when pretty (print-spaces (* 2 level) stream))))
+                   (print-closing-tag tag stream))
+               (write-string "/>" stream)))))
 	(t (error "Input not recognized as LXML ~s" dom))))
   
 ;;;; eof


Index: s-xml/src/sxml-dom.lisp
diff -u s-xml/src/sxml-dom.lisp:1.2 s-xml/src/sxml-dom.lisp:1.3
--- s-xml/src/sxml-dom.lisp:1.2	Wed Aug 17 10:06:01 2005
+++ s-xml/src/sxml-dom.lisp	Mon Aug 29 17:01:47 2005
@@ -1,6 +1,6 @@
 ;;;; -*- mode: lisp -*-
 ;;;;
-;;;; $Id: sxml-dom.lisp,v 1.2 2005/08/17 08:06:01 scaekenberghe Exp $
+;;;; $Id: sxml-dom.lisp,v 1.3 2005/08/29 15:01:47 scaekenberghe Exp $
 ;;;;
 ;;;; LXML implementation of the generic DOM parser and printer.
 ;;;;
@@ -40,38 +40,37 @@
 				       :text-hook #'sxml-text-hook))))
 
 (defmethod print-xml-dom (dom (input-type (eql :sxml)) stream pretty level)
+  (declare (special *namespaces))
   (cond ((stringp dom) (print-string-xml dom stream))
 	((consp dom)
-	 (let ((tag (car dom))
+	 (let ((tag (first dom))
 	       attributes
 	       children)
-	   (if (and (consp (cadr dom)) (eq (caadr dom) :@))
-	       (setf attributes (cdadr dom)
-		     children (cddr dom))
-	     (setf children (cdr dom)))
-	   (format stream "<~a" tag)
-	   (dolist (pair attributes)
-	     (format stream " ~a=\"" (car pair))
-	     (print-string-xml (cadr pair) stream)
-	     (format stream "\""))
-	   (if children
-	       (progn
-		 (format stream ">")
-		 (if (and (= (length children) 1) (stringp (first children)))
-		     (print-string-xml (first children) stream)
-		   (progn
-		     (dolist (child children)
-		       (when pretty
-			 (terpri stream)
-			 (dotimes (i (* 2 level)) (write-char #\space stream)))
-		       (if (stringp child)
-			   (print-string-xml child stream)
-			 (print-xml-dom child input-type stream pretty (1+ level))))
-		     (when pretty
-		       (terpri stream)
-		       (dotimes (i (* 2 (1- level))) (write-char #\space stream)))))
-		 (format stream "</~a>" tag))
-	     (format stream "/>"))))
+	   (if (and (consp (second dom)) (eq (first (second dom)) :@))
+	       (setf attributes (rest (second dom))
+		     children (rest (rest dom)))
+	     (setf children (rest dom)))
+           (let ((*namespaces* (extend-namespaces (loop :for (name value) :in attributes 
+                                                        :collect (cons name value))
+                                                  *namespaces*)))
+             (write-char #\< stream)
+             (print-identifier tag stream)
+             (loop :for (name value) :in attributes
+                   :do (print-attribute name value stream))
+             (if children
+                 (progn
+                   (write-char #\> stream)
+                   (if (and (= (length children) 1) (stringp (first children)))
+                       (print-string-xml (first children) stream)
+                     (progn
+                       (dolist (child children)
+                         (when pretty (print-spaces (* 2 level) stream))
+                         (if (stringp child)
+                             (print-string-xml child stream)
+                           (print-xml-dom child input-type stream pretty (1+ level))))
+                       (when pretty (print-spaces (* 2 level) stream))))
+                   (print-closing-tag tag stream))
+               (write-string "/>" stream)))))
 	(t (error "Input not recognized as SXML ~s" dom))))
 
 ;;;; eof


Index: s-xml/src/xml-struct-dom.lisp
diff -u s-xml/src/xml-struct-dom.lisp:1.1.1.1 s-xml/src/xml-struct-dom.lisp:1.2
--- s-xml/src/xml-struct-dom.lisp:1.1.1.1	Mon Jun  7 20:49:57 2004
+++ s-xml/src/xml-struct-dom.lisp	Mon Aug 29 17:01:47 2005
@@ -1,6 +1,6 @@
 ;;;; -*- mode: lisp -*-
 ;;;;
-;;;; $Id: xml-struct-dom.lisp,v 1.1.1.1 2004/06/07 18:49:57 scaekenberghe Exp $
+;;;; $Id: xml-struct-dom.lisp,v 1.2 2005/08/29 15:01:47 scaekenberghe Exp $
 ;;;;
 ;;;; XML-STRUCT implementation of the generic DOM parser and printer.
 ;;;;
@@ -75,30 +75,28 @@
 ;;; printing xml structures
 
 (defmethod print-xml-dom (xml-element (input-type (eql :xml-struct)) stream pretty level)
-  (format stream "<~a" (xml-element-name xml-element))
-  (dolist (attribute (xml-element-attributes xml-element))
-    (format stream " ~a=\"" (car attribute))
-    (print-string-xml (cdr attribute) stream)
-    (format stream "\""))
-  (let ((children (xml-element-children xml-element))) 
-    (if children
-	(progn
-	  (format stream ">")
-	  (if (and (= (length children) 1) (stringp (first children)))
-	      (print-string-xml (first children) stream)
-	    (progn
-	      (dolist (child children)
-		(when pretty
-		  (terpri stream)
-		  (dotimes (i (* 2 level)) (write-char #\space stream)))
-		(if (stringp child)
-		    (print-string-xml child stream)
-		  (print-xml-dom child input-type stream pretty (1+ level))))
-	      (when pretty
-		(terpri stream)
-		(dotimes (i (* 2 (1- level))) (write-char #\space stream)))))
-	  (format stream "</~a>" (xml-element-name xml-element)))
-      (format stream "/>"))))
+  (declare (special *namespaces*))
+  (let ((*namespaces* (extend-namespaces (xml-element-attributes xml-element)
+                                         *namespaces*)))
+    (write-char #\< stream)
+    (print-identifier (xml-element-name xml-element) stream)
+    (loop :for (name . value) :in (xml-element-attributes xml-element)
+          :do (print-attribute name value stream))
+    (let ((children (xml-element-children xml-element))) 
+      (if children
+          (progn
+            (write-char #\> stream)
+            (if (and (= (length children) 1) (stringp (first children)))
+                (print-string-xml (first children) stream)
+              (progn
+                (dolist (child children)
+                  (when pretty (print-spaces (* 2 level) stream))
+                  (if (stringp child)
+                      (print-string-xml child stream)
+                    (print-xml-dom child input-type stream pretty (1+ level))))
+                (when pretty (print-spaces (* 2 level) stream))))
+            (print-closing-tag (xml-element-name xml-element) stream))
+        (write-string "/>" stream)))))
 
 ;;; the standard hooks to generate xml-element structs
 


Index: s-xml/src/xml.lisp
diff -u s-xml/src/xml.lisp:1.9 s-xml/src/xml.lisp:1.10
--- s-xml/src/xml.lisp:1.9	Mon Aug 29 10:54:42 2005
+++ s-xml/src/xml.lisp	Mon Aug 29 17:01:47 2005
@@ -1,6 +1,6 @@
 ;;;; -*- mode: lisp -*-
 ;;;;
-;;;; $Id: xml.lisp,v 1.9 2005/08/29 08:54:42 scaekenberghe Exp $
+;;;; $Id: xml.lisp,v 1.10 2005/08/29 15:01:47 scaekenberghe Exp $
 ;;;;
 ;;;; This is a Common Lisp implementation of a basic but usable XML parser.
 ;;;; The parser is non-validating and not complete (no CDATA).
@@ -137,11 +137,14 @@
 
 ;;; namespace support
 
+(defvar *ignore-namespaces* nil
+  "When t, namespaces are ignored like in the old version of S-XML")
+
 (defclass xml-namespace ()
   ((uri :documentation "The URI used to identify this namespace"
         :accessor get-uri
         :initarg :uri)
-   (prefix :documentation "The prefix assigned to this namespace"
+   (prefix :documentation "The preferred prefix assigned to this namespace"
            :accessor get-prefix
            :initarg :prefix
            :initform nil)
@@ -157,11 +160,37 @@
                                          :package (find-package :keyword))
   "The local (global default) XML namespace")
 
-(defvar *namespaces* (list *local-namespace*)
-  "Ordered list of XML namespaces currently in effect")
+(defvar *known-namespaces* (list *local-namespace*)
+  "The list of known/defined namespaces")
+
+(defun find-namespace (uri)
+  "Find a registered XML namespace identified by uri"
+  (find uri *known-namespaces* :key #'get-uri :test #'string-equal))
+
+(defun register-namespace (uri prefix package)
+  "Register a new or redefine an existing XML namespace defined by uri with prefix and package"
+  (let ((namespace (find-namespace uri)))
+    (if namespace
+        (setf (get-prefix namespace) prefix
+              (get-package namespace) (find-package package))
+      (push (setf namespace (make-instance 'xml-namespace
+                                           :uri uri
+                                           :prefix prefix
+                                           :package (find-package package)))
+            *known-namespaces*))
+    namespace))
+
+(defvar *namespaces* `(("" . *local-namespace*))
+  "Ordered list of (prefix . XML-namespace) bindings currently in effect - special variable")
+
+(defun find-namespace-binding (prefix namespaces)
+  "Find the XML namespace currently bound to prefix in the namespaces bindings"
+  (cdr (assoc prefix namespaces :test #'string-equal)))
 
 (defun split-identifier (identifier)
   "Split an identifier 'prefix:name' and return (values prefix name)"
+  (when (symbolp identifier)
+    (setf identifier (symbol-name identifier)))
   (let ((colon-position (position #\: identifier :test #'char=)))
     (if colon-position
         (values (subseq identifier 0 colon-position)
@@ -175,71 +204,78 @@
   "If t, export newly interned symbols form their packages")
 
 (defun resolve-identifier (identifier namespaces &optional as-attribute)
-  "Resolve the string identifier in the list of namespaces"
-  (flet ((intern-symbol (string package)
-           (if *require-existing-symbols*
-               (let ((symbol (find-symbol string package)))
-                 (or symbol
-                     (error "Symbol ~s does not exist in ~s" string package)))
-             (let ((symbol (intern string package)))
-               (when (and *auto-export-symbols* 
-                          (not (eql package (find-package :keyword))))
-                 (export symbol package))
-               symbol))))
-    (multiple-value-bind (prefix name)
-        (split-identifier identifier)
-      (if (or (null prefix) (string= prefix "xmlns"))
-          (if as-attribute
-              (intern (if (string= prefix "xmlns") identifier name) (get-package *local-namespace*))
-            (let ((default-namespace (find "" namespaces :key #'get-prefix :test #'string-equal)))
-              (intern-symbol name (get-package default-namespace))))
-        (let ((namespace (find prefix namespaces :key #'get-prefix :test #'string-equal)))
-          (if namespace
-              (intern-symbol name (get-package namespace))
-            (error "namespace not found for prefix ~s" prefix)))))))
+  "Resolve the string identifier in the list of namespace bindings"
+  (if *ignore-namespaces*
+      (intern identifier :keyword)
+    (flet ((intern-symbol (string package) ; intern string as a symbol in package
+             (if *require-existing-symbols*
+                 (let ((symbol (find-symbol string package)))
+                   (or symbol
+                       (error "Symbol ~s does not exist in ~s" string package)))
+               (let ((symbol (intern string package)))
+                 (when (and *auto-export-symbols* 
+                            (not (eql package (find-package :keyword))))
+                   (export symbol package))
+                 symbol))))
+      (multiple-value-bind (prefix name)
+          (split-identifier identifier)
+        (if (or (null prefix) (string= prefix "xmlns"))
+            (if as-attribute
+                (intern (if (string= prefix "xmlns") identifier name) (get-package *local-namespace*))
+              (let ((default-namespace (find-namespace-binding "" namespaces)))
+                (intern-symbol name (get-package default-namespace))))
+          (let ((namespace (find-namespace-binding prefix namespaces)))
+            (if namespace
+                (intern-symbol name (get-package namespace))
+              (error "namespace not found for prefix ~s" prefix))))))))
 
 (defvar *auto-create-namespace-packages* t
   "If t, new packages will be created for namespaces, if needed, named by the prefix")
 
 (defun extend-namespaces (attributes namespaces)
-  "Given possible 'xmlns[:prefix]' attributes, extend namespaces"
-  (let (default-namespace-uri)
-    (loop :for (key . value) :in attributes
-          :do (if (string= key "xmlns")
-                  (setf default-namespace-uri value)
-                (multiple-value-bind (prefix name)
-                    (split-identifier key)
-                  (when (string= prefix "xmlns")
-                    (push (make-instance 'xml-namespace
-                                         :uri value
-                                         :prefix name
-                                         :package (or (find-package name)
-                                                      (if *auto-create-namespace-packages*
-                                                          (make-package name :nicknames (list (string-upcase name)))
-                                                        (error "Cannot find or create package ~s" name))))
-                          namespaces)))))
-    (when default-namespace-uri
-      (let ((namespace (find default-namespace-uri namespaces :key #'get-uri :test #'string-equal)))
-        (if namespace
-            (push (make-instance 'xml-namespace
-                                 :uri (get-uri namespace)
-                                 :prefix ""
-                                 :package (get-package namespace))
-                  namespaces)
-          (error "No prefix found for default namespace ~s" default-namespace-uri)))))
+  "Given possible 'xmlns[:prefix]' attributes, extend the namespaces bindings"
+  (unless *ignore-namespaces*
+    (let (default-namespace-uri)
+      (loop :for (key . value) :in attributes
+            :do (if (string= key "xmlns")
+                    (setf default-namespace-uri value)
+                  (multiple-value-bind (prefix name)
+                      (split-identifier key)
+                    (when (string= prefix "xmlns")
+                      (let* ((uri value)
+                             (prefix name)
+                             (namespace (find-namespace uri)))
+                        (unless namespace
+                          (setf namespace 
+                                (register-namespace uri
+                                                    prefix
+                                                    (or (find-package prefix)
+                                                        (if *auto-create-namespace-packages*
+                                                            (make-package prefix :nicknames `(,(string-upcase prefix)))
+                                                          (error "Cannot find or create package ~s" prefix))))))
+                        (push `(,prefix . ,namespace) namespaces))))))
+      (when default-namespace-uri
+        (let ((namespace (find-namespace default-namespace-uri)))
+          (if namespace
+              (push `("" . namespace) namespaces)
+            (error "No prefix found for default namespace ~s" default-namespace-uri))))))
   namespaces)
 
 (defun print-identifier (identifier stream &optional as-attribute)
   "Print identifier on stream using namespace conventions"
-  (declare (ignore as-attribute))
-  (let (prefix name)
+  (declare (ignore as-attribute) (special *namespaces*))
+  (if *ignore-namespaces*
+      (princ identifier stream)
     (if (symbolp identifier)
-        (setf prefix (package-name (symbol-package identifier))
-              name (symbol-name identifier))
-      (setf (values prefix name) (split-identifier identifier)))
-    (if (equal prefix "KEYWORD")
-        (format stream "~a" name)
-      (format stream "~a:~a" prefix name))))
+        (let ((package (symbol-package identifier))
+              (name (symbol-name identifier)))
+          (let* ((namespace (find package *known-namespaces* :key #'get-package))
+                 (prefix (or (car (find namespace *namespaces* :key #'cdr))
+                             (get-prefix namespace))))
+            (if (string= prefix "")
+                (princ name stream)
+              (format stream "~a:~a" prefix name))))
+      (princ identifier stream))))
 
 ;;; the parser state
 




More information about the S-xml-cvs mailing list