(defvar *treeview-depth* 3) (defvar *want-hrule* t) (defvar *uri-name-is-uri-hack* nil) (defvar *inhibit-property-info-in-toolips* nil) (defvar *toolip-font-size* 11) (defvar *flush-annotation-value-regexp* "") (defvar *classtree-preferred-language* nil) (defvar *uri-name-html* t) (defvar *annotation-query-reasoner* :none) (defvar *super-label* "super") (defvar *show-logical-in-tooltip* t) (defvar *built-in-labels* nil) (defun built-in-labels () (or *built-in-labels* (progn (setq *built-in-labels* (make-hash-table)) (setf (gethash !owl:AnnotationProperty *built-in-labels*) "Annotation Property") (setf (gethash !owl:DatatypeProperty *built-in-labels*) "Datatype Property") (setf (gethash !owl:ObjectProperty *built-in-labels*) "Object Property") *built-in-labels*))) (defun uri-name-for-node-with-equivalents (uri kb labels &optional do) (or (gethash uri (built-in-labels)) (if (or (search *blankprefix* (uri-full uri)) (search "bNode" (uri-full uri))) (or (and labels (gethash uri labels)) (format nil "Blank~a" (#"replaceAll" (uri-full uri) "^.*[/#:](?=.)" ""))) (let ((equivalents (and do (equivalents uri kb)))) (if (and do (> (length equivalents) 1)) (values (format nil "[~{~a~^, ~}]" (loop for uri in equivalents collect (or (and labels (car (gethash uri labels))) (let ((string (maybe-abbreviate-namespace (uri-full uri)))) (#"replaceAll" string "^.*[/#:](?=.)" "") )))) equivalents) (or (and labels (car (gethash uri labels))) (let ((string (maybe-abbreviate-namespace (uri-full uri)))) (#"replaceAll" string "^.*[/#:](?=.)" "") (#"replaceAll" string "^.*[/#:](?=.)" "")))) ) ))) (defun clean-label (label &optional (ignore-if-xml nil)) (let ((cleaned (and label (#"replaceAll" (#"replaceAll" (#"replaceAll" (#"replaceFirst" (#0"replaceFirst" label "^\\s+" "") "\\s+$" "") "<" "<") ">" ">") "\"" """)))) (when cleaned (if (and (> (length cleaned) 35) (char= (char cleaned 0) #\&)) (if ignore-if-xml nil cleaned) (if (equal cleaned "") nil cleaned) )))) (defun rdfs-label (uri kb) (or (gethash (if (stringp uri) (make-uri uri) uri) (rdfs-labels kb)) (list (#"replaceAll" (if (stringp uri) uri (uri-full uri)) ".*[/#]" "")))) (defun rdfs-labels (kb) (or (v3kb-uri2label kb) (flet ((get-labels (clauses) (or (and *classtree-preferred-language* (sparql `(:select (?uri ?label) () ,@clauses (:filter (and (equal (lang ?label) ,*classtree-preferred-language*) (not (isblank ?uri))))) :kb kb :use-reasoner *annotation-query-reasoner*)) (sparql `(:select (?uri ?label) () ,@clauses (:filter (not (isblank ?uri)))) :kb kb :use-reasoner *annotation-query-reasoner*)))) (setf (v3kb-uri2label kb) (loop with table = (make-hash-table) for (uri label) in (or (get-labels '((?uri !rdfs:label ?label))) (get-labels '((?uri !foaf:name ?label))) (get-labels '((?uri !swan:title ?label)))) for clean-label = (clean-label label t) when clean-label do (pushnew clean-label (gethash uri table ) :test 'equalp) finally (return table)))))) (defvar *temp-directory* (pathname-directory (make-temp-file))) (defun temp-directory-path (fname) (namestring (merge-pathnames fname (make-pathname :directory *temp-directory*)))) (defparameter *classtree-treeml-header* " ") (defun asserted-instances (node kb) (if (stringp node) (setq node (make-uri node))) (sparql `(:select (?i) (:distinct t) (?i !rdf:type ,node)) :use-reasoner :none :kb kb :flatten t)) (defun asserted-subclasses (node kb) (if (stringp node) (setq node (make-uri node))) (if (equal node !owl:Thing) (sparql `(:select (?i) (:distinct t) (?i !rdf:type !owl:Class) (:optional (?i !rdfs:subClassOf ?j)) (:filter (and (not (bound ?j)) (not (isblank ?i))))) :use-reasoner :none :kb kb :flatten t) (sparql `(:select (?i) (:distinct t) (?i !rdfs:subClassOf ,node)) :use-reasoner :none :kb kb :flatten t))) (defmethod write-classtree-treeml ((kb v3kb) &key root ;; top of the tree to display from (fname (temp-directory-path "kb-treeml.xml")) ;; where to write to include-instances ;; whether to include instances in the tree merge-same ;; whether equivalentClasses / sameIndividual should be collapsed to a single node dont-show ;; a list of nodes that we don't want to display sort-function ;; if supplied, at each level subclasses are sorted top-bottom using this function (use-labels t) ;; use labels vs. fragments max-tree-size ;; don't include more then this many siblings in a branch include-tooltips ;; whether to include tooltips in the saved file (inferred t)) ;; whether subclasses/instances are inferred or asserted. Latter is faster but not necessarily correct (let ((unsatisfiable (if inferred (equivalents !owl:Nothing kb) nil)) (equivalents-seen (make-hash-table :test 'equalp)) (labels (and use-labels (rdfs-labels kb))) (treekb (if inferred kb (weaken-to-only-subclasses kb 'treekb)))) (instantiate-reasoner treekb :pellet nil) ;; accessors for subclasses and instances of a node. Take care here if we are using inferred or uninferred (flet ((satisfiable-children (node) (set-difference (children node treekb) unsatisfiable :test 'eq)) (direct-instances (node) (direct-instances node treekb)) (maybe-tooltip (kb node) (if include-tooltips (format nil "" (encode-tooltip-for-xml-attribute (tree-tooltip kb node))) "")) (node-name (kb node) (multiple-value-bind (name equivs) (uri-name-for-node-with-equivalents node kb labels (and inferred merge-same)) (loop for equiv in equivs do (setf (gethash equiv equivalents-seen) t)) name))) (with-open-file (f fname :direction :output :if-exists :supersede :if-does-not-exist :create) (write-string *classtree-treeml-header* f) ;; will be calling this recursively as we descend down from root (labels ((subtree (node) ;; unless we don't want to see this node, or we've already included it somewhere in the graph (unless (or (gethash node equivalents-seen) (member node dont-show)) (let ((children (satisfiable-children node))) (when sort-function (setq children (funcall sort-function children))) ;; put unsatisfiable classes below !owl:Nothing (when (and (equal node !owl:Thing) unsatisfiable) (setq children (append children (list !owl:Nothing)))) (cond ;; no children - subclasses or instances. So we are a leaf. ((and (null children) (or (not include-instances) (not (direct-instances node)))) (format f "~a~%" (node-name kb node) (uri-full node) (maybe-tooltip kb node))) ;; No subclassses, but there are instances, so we are a branch. ((and (null children) include-instances (direct-instances node)) (format f "~a~%" (node-name kb node) (uri-full node) (maybe-tooltip kb node)) (loop for i in (direct-instances node) for count from 0 unless (and max-tree-size (> count max-tree-size)) do (format f "~a~%" (uri-name-for-node-with-equivalents i kb labels (and inferred merge-same )) (uri-full i) (maybe-tooltip kb i))) (format f "~%")) ;; subclasses, so we are a branch (t (format f "~a~%" (format nil "~a (~a)" (node-name kb node) (length (satisfiable-children node))) (uri-full node) (maybe-tooltip kb node)) (loop for child in children for count from 0 unless (and max-tree-size (> count max-tree-size)) do (subtree child)) (when include-instances (loop for i in (direct-instances node) for count from 0 unless (and max-tree-size (> count max-tree-size)) do (format f "~a~%" (uri-name-for-node-with-equivalents i kb labels (and inferred merge-same)) (uri-full i) (maybe-tooltip kb i)))) (format f "~%"))))))) (subtree (or root !owl:Thing))) (write-string " " f) (values fname treekb))))) (defun annotation-property-values-or-labels (entity &optional (kb *default-kb*)) (remove-if (lambda(el) (member (car el) `(,!rdfs:comment ,!oboinowl:hasDefinition))) ; these already in comment (sparql `(:select (?p ?v) (:distinct t) (:union ((?p :a !owl:AnnotationProperty) (,(make-uri entity) ?p ?v) (:filter (not (isblank ?v)))) ((?p :a !owl:AnnotationProperty) (,(make-uri entity) ?p ?v1) (?v1 !rdfs:label ?v) (:filter (isblank ?v1))) )) :kb kb :use-reasoner :none))) (defun make-treeview (file name kb &optional (depth *treeview-depth*)) (#"setDismissDelay" (#"sharedInstance" 'tooltipmanager) 300000) ;; make the tooltips last longer (#"setInitialDelay" (#"sharedInstance" 'tooltipmanager) 10) (let ((jpanel (#"demo" 'org.sc.prefuse.TreeView (format nil "file://~a" file) "name" "2" (format nil "~a" (max 3 (classtree-depth kb))))) (jframe (new 'JFrame (string-capitalize name)))) (setq panel jpanel) (let ((visualization (#"getVisualization" (#"getComponent" jpanel 0) ))) (setq vis visualization) (#"addControlListener" (#"getComponent" jpanel 0) (make-tooltip-control kb 'tree-tooltip)) (#"setDefaultFont" (#"get" (#"getAction" visualization "filter") 1) (#"getFont" 'FontLib "Tahoma" 12 12) ) ;smaller font (#"run" visualization "filter") (#"run" visualization "treeLayout") (#"setContentPane" jframe jpanel) (#"pack" jframe) (#"setVisible" jframe t) #+darwin (run-shell-command "osascript -e 'tell application \"/usr/bin/java\"' -e \"activate\" -e \"end tell\"") )) kb) (defmethod show-classtree ((url string) &rest stuff) (apply 'show-classtree (load-ontology url) stuff)) (defmethod show-classtree ((symbol symbol) &rest stuff) (apply 'show-classtree (load-ontology symbol) (append (standard-ontology-classtree-options symbol) stuff ))) (defmethod show-classtree ((kb v3kb) &rest stuff) (multiple-value-bind (fname treekb) (apply 'write-classtree-treeml kb stuff) (make-treeview fname (string (v3kb-name kb)) treekb (or (getf stuff :depth) *treeview-depth*)))) (defmethod show-propertytree ((symbol symbol) &rest stuff) (apply 'show-propertytree (load-ontology symbol) stuff)) (defmethod show-propertytree ((url string) &key root include-instances merge-same (include-annotation-properties t) (use-labels t) (dont-show t)) (show-propertytree (load-ontology url) :root root :include-instances include-instances :merge-same merge-same :include-annotation-properties include-annotation-properties)) (defmethod show-propertytree ((kb v3kb) &key root include-instances merge-same (include-annotation-properties t) (use-labels t) (dont-show nil)) (make-treeview (write-propertytree-treeml kb :root root :include-instances include-instances :merge-same merge-same :include-annotation-properties include-annotation-properties :dont-show dont-show) (string(v3kb-name kb)) kb)) ;; http://sourceforge.net/forum/message.php?msg_id=3339297 ;; RE: directed layered (hierarchical) layout ;; 2005-09-14 15:10 ;; There's no existing layout that will do this exactly. Some options include hacking VerticalTreeLayout to create a new layout that does what you want, or playing with the ForceDirectedLayout and adding a downstream Action instance that forces nodes to stay within horizontal "bands" depending on the depth level of the node. ;; Hope that helps, ;; -jeff (defun make-tooltip-control (kb tooltip-function &optional (tooltip-ok? (lambda(item) (#"canGetString" item "entity"))) (tooltip-arg (lambda(item) (#"getString" item "entity")))) (let ((fields (jnew-array "java.lang.String" 1))) (jarray-set fields "tooltip" 0) (let ((wrapped (new 'tooltipcontrol fields)) (interfaces (jclass-all-interfaces 'tooltipcontrol))) (jdelegating-interface-implementation (car interfaces) wrapped "itemEntered" (let ((kb kb)) (lambda (item event &aux display) (unless (#"isShiftDown" event) (handler-case (multiple-value-bind (value errorp) (ignore-errors (setq display (#"getSource" event)) (when (funcall tooltip-ok? item) (funcall tooltip-function kb (funcall tooltip-arg item)))) (and display (if (and value (not errorp)) (#"setToolTipText" display value) (if errorp (#"setToolTipText" display (format nil "Error creating tooltip for ~a" (#"getSourceTuple" item))))))) (condition () nil) )))) "itemExited" (lambda (item event &aux display) (handler-case (multiple-value-bind (value errorp) (ignore-errors (setq display (#"getSource" event))) (and display (if (not errorp) (unless (#"isShiftDown" event) (#"setToolTipText" display (make-immediate-object nil :ref))) ))) (condition () nil) )) )))) (defun rdfs-comment (entity kb) (let ((uri (make-uri entity))) (format nil "~{~a~^; ~}" (or (or (and *classtree-preferred-language* (sparql `(:select (?comment) () (,uri !oboinowl:hasDefinition ?def) (?def !rdfs:label ?comment) (:filter (and (equal (lang ?comment) ,*classtree-preferred-language*) (not (equal ?comment ""))))) :use-reasoner *annotation-query-reasoner* :kb kb :flatten t)) (sparql `(:select (?comment) () (,uri !oboinowl:hasDefinition ?def) (?def !rdfs:label ?comment) (:filter (not (equal ?comment "")))) :use-reasoner *annotation-query-reasoner* :kb kb :flatten t) ) (or (and *classtree-preferred-language* (sparql `(:select (?comment) () (,uri !rdfs:comment ?comment) (:filter (and (equal (lang ?comment) ,*classtree-preferred-language*) (not (equal ?comment ""))))) :kb kb :use-reasoner *annotation-query-reasoner* :flatten t)) (sparql `(:select (?comment) () (,uri !rdfs:comment ?comment) (:filter (not (equal ?comment "")))) :kb kb :use-reasoner *annotation-query-reasoner* :flatten t)) (or (and *classtree-preferred-language* (sparql `(:select (?comment) () (:union ((,uri !obi:OBI_0000291 ?comment)) ((,uri !obi:IAO_0000115 ?comment))) (:filter (and (equal (lang ?comment) ,*classtree-preferred-language*) (not (equal ?comment ""))))) :kb kb :use-reasoner *annotation-query-reasoner* :flatten t)) (sparql `(:select (?comment) () (:union ((,uri !obi:OBI_0000291 ?comment)) ((,uri !obi:IAO_0000115 ?comment))) (:filter (not (equal ?comment "")))) :kb kb :use-reasoner *annotation-query-reasoner* :flatten t)) )))) (defun tree-tooltip (kb entity) (rdfs-comment entity kb)) (defun encode-tooltip-for-xml-attribute (tooltip) (xml-encode-string-with-unicode (#"replaceAll" tooltip "\\n" "
"))) (defun uris-for-entity-html (entity kb) (format nil "~{~a
~}" (loop for e in (if (eq type :class) (or (equivalents (make-uri entity) kb) (list (make-uri entity))) (if (member type '(:object-property :datatype-property)) (cons (make-uri entity) (same-properties (make-uri entity) kb)) (if (or (null type) (eq type :annotation-property)) (list (make-uri entity)) (cons (make-uri entity) (sames (make-uri entity) kb))))) collect (format nil "~a~a" (if type (concatenate 'string (string-capitalize (string type)) ": ") "") (uri-full e))) )) (defun uris-for-entity-html (entity kb) (format nil "~a
" (html-quote (if (uri-p entity) (uri-full entity) entity)))) (defun html-for-class-parents (entity kb) (let* ((asserted (sparql `(:select (?parent) () (,entity !rdfs:subClassOf ?parent) (:filter (not (isblank ?parent)))) :kb kb :use-reasoner :none :flatten t)) (inferred (set-difference (parents entity kb) asserted)) (mi? (> (length asserted) 1))) (if (or inferred asserted) (format nil "~{~a~^, ~}" (append (mapcar (lambda(c) (if mi? (format nil "~a" (car (rdfs-label c kb)) ) (car (rdfs-label c kb)))) (remove-duplicates asserted :test 'equalp)) (mapcar (lambda(c) (format nil "~a" (car (rdfs-label c kb)) inferred)) (remove-duplicates inferred :test 'equalp)) )) "Thing"))) (defun html-for-class (entity kb) (let ((parents (html-for-class-parents (make-uri entity) kb))) (with-output-to-string (s) (unless (equal entity (uri-full !owl:Nothing)) (format s "~a: ~a
" *super-label* parents)) (loop for sentence in (manchester-logical-axioms-for-class entity kb) do (format s "  ~a
" sentence)) (let* ((apv (annotation-property-values-or-labels entity kb))) (loop for (p val) in apv with need-hrule = t when (and val (not (eq p !rdfs:comment)) (not (eq p !rdfs:label))) unless (and (not (uri-p val)) (#"matches" val *flush-annotation-value-regexp*)) do (let ((p-label (car (rdfs-label (uri-full p) kb)))) (when (and need-hrule *want-hrule*) (format s "
") (setq need-hrule nil)) (format s "~a: ~a
" p-label (if (uri-p val) (or (car (rdfs-label (uri-full p) kb)) val) val)))) )))) (defun html-quote (string) (and string (#"replaceAll" string "<" "<"))) (defun tree-tooltip-css () "") (defun tree-tooltip (kb entity &key (width 600) (type :class)) (setq @ (make-uri entity)) (let* ((*default-kb* kb) (comment (html-quote (rdfs-comment entity kb))) (labels (rdfs-label entity kb))) (format nil "~a
~a~a~a~a
" (tree-tooltip-css) *toolip-font-size* width (uris-for-entity-html entity kb) (if labels (format nil "~{~a~^
~}" labels) "") (if (equal entity (uri-full !owl:Nothing)) "
These classes are unsatisfiable!
" (if (and comment (not (equal comment ""))) (format nil "
~a
" comment) (if labels "
" ""))) (cond ((eq type :class) (html-for-class entity kb)) ((eq type :individual) (html-for-individual entity kb) ) ((member type '(:object-property :datatype-property)) (html-for-property entity kb)) (t ""))))) (defun unhtml-tooltip (tooltip) (prog1 @@ (setq @@ (#"replaceAll" (#"replaceAll" (#"replaceAll" (#"replaceAll" @@ "<(\/div|br)>" (load-time-value (concatenate 'string (string #\linefeed) (string #\linefeed)))) "<\\S+?>" "") "(<(div|font) .*?>)" "") "&\\S+?;" " "))) ) (defun simple-subclassof-axiom? (ax) (and (jinstance-of-p ax (find-java-class 'OWLSubClassOfAxiom )) (= (#"size" (#"getClassesInSignature" ax)) 2) (every (lambda(e) (jinstance-of-p e (find-java-class 'OWLClass))) (set-to-list (#"getClassExpressions" ax))))) (defun manchester-logical-axioms-for-class (class ont) (setq ont (or (v3kb-weakened-from ont) ont)) (if (stringp class) (setq class (make-uri class))) (if (or (not (get-entity class :class ont)) (equal class !owl:Nothing)) nil (remove-duplicates (let* ((class-shortform (quote-for-regex (#"getShortForm" (short-form-provider ont) (get-entity class :class ont)))) (this-axiom-header (format nil "~a <[^>]*?>SubClassOf<[^>]*?>\\s+" class-shortform)) (equivalent-classes-axiom (format nil "~a <[^>]*?>EquivalentTo.*" class-shortform))) (loop for (sentence ax) in (get-rendered-referencing-axioms class :class ont) unless (or (simple-subclassof-axiom? ax) (jinstance-of-p ax (find-java-class 'OWLDeclarationAxiom ))) do (setq sentence (#"replaceAll" sentence "\\n" "
     ")) (if (#"matches" sentence equivalent-classes-axiom) (setq sentence (#"replaceFirst" sentence class-shortform "")) (setq sentence (#"replaceFirst" sentence "EquivalentTo" "EquivalentTo
     ")) ) and collect (string-trim " " (if (#"matches" sentence (format nil "~a.*" this-axiom-header)) (#"replaceFirst" sentence this-axiom-header "") (#"replaceFirst" sentence "SubClassOf" "SubClassOf
     "))))) :test 'equal))) (defparameter *obo-noise-classes* (list !oboinowl:DbXref !oboinowl:Definition !oboinowl:ObsoleteClass !oboinowl:ObsoleteProperty !oboinowl:Subset !oboinowl:Synonym !oboinowl:SynonymType !protegeowl:PAL-CONSTRAINT !protegeowl:DIRECTED-BINARY-RELATION !protegeowl:TO !protegeowl:FROM !protegeowl:SLOT-CONSTRAINTS !protegeowl:PAL-NAME !protegeowl:PAL-CONSTRAINTS !protegeowl:PAL-STATEMENT !protegeowl:PAL-RANGE !protegeowl:PAL-DESCRIPTION)) (defparameter *obi-noise-classes* (append *obo-noise-classes* (list !obi:OBI_0000449 !obi:OBI_0000233 !obi:OBI_0000683 !obi:OBI_0600065)))