rlm@10
|
1 ; Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
2 ; The use and distribution terms for this software are covered by the
|
rlm@10
|
3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
4 ; which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
5 ; By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
6 ; the terms of this license.
|
rlm@10
|
7 ; You must not remove this notice, or any other, from this software.
|
rlm@10
|
8
|
rlm@10
|
9 (ns ^{:doc "Graphical object inspector for Clojure data structures."
|
rlm@10
|
10 :author "Rich Hickey"}
|
rlm@10
|
11 clojure.inspector
|
rlm@10
|
12 (:import
|
rlm@10
|
13 (java.awt BorderLayout)
|
rlm@10
|
14 (java.awt.event ActionEvent ActionListener)
|
rlm@10
|
15 (javax.swing.tree TreeModel)
|
rlm@10
|
16 (javax.swing.table TableModel AbstractTableModel)
|
rlm@10
|
17 (javax.swing JPanel JTree JTable JScrollPane JFrame JToolBar JButton SwingUtilities)))
|
rlm@10
|
18
|
rlm@10
|
19 (defn atom? [x]
|
rlm@10
|
20 (not (coll? x)))
|
rlm@10
|
21
|
rlm@10
|
22 (defn collection-tag [x]
|
rlm@10
|
23 (cond
|
rlm@10
|
24 (instance? java.util.Map$Entry x) :entry
|
rlm@10
|
25 (instance? java.util.Map x) :map
|
rlm@10
|
26 (sequential? x) :seq
|
rlm@10
|
27 :else :atom))
|
rlm@10
|
28
|
rlm@10
|
29 (defmulti is-leaf collection-tag)
|
rlm@10
|
30 (defmulti get-child (fn [parent index] (collection-tag parent)))
|
rlm@10
|
31 (defmulti get-child-count collection-tag)
|
rlm@10
|
32
|
rlm@10
|
33 (defmethod is-leaf :default [node]
|
rlm@10
|
34 (atom? node))
|
rlm@10
|
35 (defmethod get-child :default [parent index]
|
rlm@10
|
36 (nth parent index))
|
rlm@10
|
37 (defmethod get-child-count :default [parent]
|
rlm@10
|
38 (count parent))
|
rlm@10
|
39
|
rlm@10
|
40 (defmethod is-leaf :entry [e]
|
rlm@10
|
41 (is-leaf (val e)))
|
rlm@10
|
42 (defmethod get-child :entry [e index]
|
rlm@10
|
43 (get-child (val e) index))
|
rlm@10
|
44 (defmethod get-child-count :entry [e]
|
rlm@10
|
45 (count (val e)))
|
rlm@10
|
46
|
rlm@10
|
47 (defmethod is-leaf :map [m]
|
rlm@10
|
48 false)
|
rlm@10
|
49 (defmethod get-child :map [m index]
|
rlm@10
|
50 (nth (seq m) index))
|
rlm@10
|
51
|
rlm@10
|
52 (defn tree-model [data]
|
rlm@10
|
53 (proxy [TreeModel] []
|
rlm@10
|
54 (getRoot [] data)
|
rlm@10
|
55 (addTreeModelListener [treeModelListener])
|
rlm@10
|
56 (getChild [parent index]
|
rlm@10
|
57 (get-child parent index))
|
rlm@10
|
58 (getChildCount [parent]
|
rlm@10
|
59 (get-child-count parent))
|
rlm@10
|
60 (isLeaf [node]
|
rlm@10
|
61 (is-leaf node))
|
rlm@10
|
62 (valueForPathChanged [path newValue])
|
rlm@10
|
63 (getIndexOfChild [parent child]
|
rlm@10
|
64 -1)
|
rlm@10
|
65 (removeTreeModelListener [treeModelListener])))
|
rlm@10
|
66
|
rlm@10
|
67
|
rlm@10
|
68 (defn old-table-model [data]
|
rlm@10
|
69 (let [row1 (first data)
|
rlm@10
|
70 colcnt (count row1)
|
rlm@10
|
71 cnt (count data)
|
rlm@10
|
72 vals (if (map? row1) vals identity)]
|
rlm@10
|
73 (proxy [TableModel] []
|
rlm@10
|
74 (addTableModelListener [tableModelListener])
|
rlm@10
|
75 (getColumnClass [columnIndex] Object)
|
rlm@10
|
76 (getColumnCount [] colcnt)
|
rlm@10
|
77 (getColumnName [columnIndex]
|
rlm@10
|
78 (if (map? row1)
|
rlm@10
|
79 (name (nth (keys row1) columnIndex))
|
rlm@10
|
80 (str columnIndex)))
|
rlm@10
|
81 (getRowCount [] cnt)
|
rlm@10
|
82 (getValueAt [rowIndex columnIndex]
|
rlm@10
|
83 (nth (vals (nth data rowIndex)) columnIndex))
|
rlm@10
|
84 (isCellEditable [rowIndex columnIndex] false)
|
rlm@10
|
85 (removeTableModelListener [tableModelListener]))))
|
rlm@10
|
86
|
rlm@10
|
87 (defn inspect-tree
|
rlm@10
|
88 "creates a graphical (Swing) inspector on the supplied hierarchical data"
|
rlm@10
|
89 {:added "1.0"}
|
rlm@10
|
90 [data]
|
rlm@10
|
91 (doto (JFrame. "Clojure Inspector")
|
rlm@10
|
92 (.add (JScrollPane. (JTree. (tree-model data))))
|
rlm@10
|
93 (.setSize 400 600)
|
rlm@10
|
94 (.setVisible true)))
|
rlm@10
|
95
|
rlm@10
|
96 (defn inspect-table
|
rlm@10
|
97 "creates a graphical (Swing) inspector on the supplied regular
|
rlm@10
|
98 data, which must be a sequential data structure of data structures
|
rlm@10
|
99 of equal length"
|
rlm@10
|
100 {:added "1.0"}
|
rlm@10
|
101 [data]
|
rlm@10
|
102 (doto (JFrame. "Clojure Inspector")
|
rlm@10
|
103 (.add (JScrollPane. (JTable. (old-table-model data))))
|
rlm@10
|
104 (.setSize 400 600)
|
rlm@10
|
105 (.setVisible true)))
|
rlm@10
|
106
|
rlm@10
|
107
|
rlm@10
|
108 (defmulti list-provider class)
|
rlm@10
|
109
|
rlm@10
|
110 (defmethod list-provider :default [x]
|
rlm@10
|
111 {:nrows 1 :get-value (fn [i] x) :get-label (fn [i] (.getName (class x)))})
|
rlm@10
|
112
|
rlm@10
|
113 (defmethod list-provider java.util.List [c]
|
rlm@10
|
114 (let [v (if (vector? c) c (vec c))]
|
rlm@10
|
115 {:nrows (count v)
|
rlm@10
|
116 :get-value (fn [i] (v i))
|
rlm@10
|
117 :get-label (fn [i] i)}))
|
rlm@10
|
118
|
rlm@10
|
119 (defmethod list-provider java.util.Map [c]
|
rlm@10
|
120 (let [v (vec (sort (map (fn [[k v]] (vector k v)) c)))]
|
rlm@10
|
121 {:nrows (count v)
|
rlm@10
|
122 :get-value (fn [i] ((v i) 1))
|
rlm@10
|
123 :get-label (fn [i] ((v i) 0))}))
|
rlm@10
|
124
|
rlm@10
|
125 (defn list-model [provider]
|
rlm@10
|
126 (let [{:keys [nrows get-value get-label]} provider]
|
rlm@10
|
127 (proxy [AbstractTableModel] []
|
rlm@10
|
128 (getColumnCount [] 2)
|
rlm@10
|
129 (getRowCount [] nrows)
|
rlm@10
|
130 (getValueAt [rowIndex columnIndex]
|
rlm@10
|
131 (cond
|
rlm@10
|
132 (= 0 columnIndex) (get-label rowIndex)
|
rlm@10
|
133 (= 1 columnIndex) (print-str (get-value rowIndex)))))))
|
rlm@10
|
134
|
rlm@10
|
135 (defmulti table-model class)
|
rlm@10
|
136
|
rlm@10
|
137 (defmethod table-model :default [x]
|
rlm@10
|
138 (proxy [AbstractTableModel] []
|
rlm@10
|
139 (getColumnCount [] 2)
|
rlm@10
|
140 (getRowCount [] 1)
|
rlm@10
|
141 (getValueAt [rowIndex columnIndex]
|
rlm@10
|
142 (if (zero? columnIndex)
|
rlm@10
|
143 (class x)
|
rlm@10
|
144 x))))
|
rlm@10
|
145
|
rlm@10
|
146 ;(defn make-inspector [x]
|
rlm@10
|
147 ; (agent {:frame frame :data x :parent nil :index 0}))
|
rlm@10
|
148
|
rlm@10
|
149
|
rlm@10
|
150 (defn inspect
|
rlm@10
|
151 "creates a graphical (Swing) inspector on the supplied object"
|
rlm@10
|
152 {:added "1.0"}
|
rlm@10
|
153 [x]
|
rlm@10
|
154 (doto (JFrame. "Clojure Inspector")
|
rlm@10
|
155 (.add
|
rlm@10
|
156 (doto (JPanel. (BorderLayout.))
|
rlm@10
|
157 (.add (doto (JToolBar.)
|
rlm@10
|
158 (.add (JButton. "Back"))
|
rlm@10
|
159 (.addSeparator)
|
rlm@10
|
160 (.add (JButton. "List"))
|
rlm@10
|
161 (.add (JButton. "Table"))
|
rlm@10
|
162 (.add (JButton. "Bean"))
|
rlm@10
|
163 (.add (JButton. "Line"))
|
rlm@10
|
164 (.add (JButton. "Bar"))
|
rlm@10
|
165 (.addSeparator)
|
rlm@10
|
166 (.add (JButton. "Prev"))
|
rlm@10
|
167 (.add (JButton. "Next")))
|
rlm@10
|
168 BorderLayout/NORTH)
|
rlm@10
|
169 (.add
|
rlm@10
|
170 (JScrollPane.
|
rlm@10
|
171 (doto (JTable. (list-model (list-provider x)))
|
rlm@10
|
172 (.setAutoResizeMode JTable/AUTO_RESIZE_LAST_COLUMN)))
|
rlm@10
|
173 BorderLayout/CENTER)))
|
rlm@10
|
174 (.setSize 400 400)
|
rlm@10
|
175 (.setVisible true)))
|
rlm@10
|
176
|
rlm@10
|
177
|
rlm@10
|
178 (comment
|
rlm@10
|
179
|
rlm@10
|
180 (load-file "src/inspector.clj")
|
rlm@10
|
181 (refer 'inspector)
|
rlm@10
|
182 (inspect-tree {:a 1 :b 2 :c [1 2 3 {:d 4 :e 5 :f [6 7 8]}]})
|
rlm@10
|
183 (inspect-table [[1 2 3][4 5 6][7 8 9][10 11 12]])
|
rlm@10
|
184
|
rlm@10
|
185 )
|