/Users/lyon/j4p/src/classUtils/pack/beans/xml/XmlTree.java
|
1 package classUtils.pack.beans.xml;
2
3 import classUtils.pack.util.ListEnumeration;
4 import org.xml.sax.Attributes;
5 import org.xml.sax.SAXException;
6 import org.xml.sax.helpers.DefaultHandler;
7
8 import javax.swing.ImageIcon;
9 import javax.swing.JFrame;
10 import javax.swing.JTree;
11 import javax.swing.tree.DefaultTreeCellRenderer;
12 import javax.swing.tree.DefaultTreeModel;
13 import javax.swing.tree.TreeNode;
14 import javax.swing.tree.TreePath;
15 import javax.xml.parsers.ParserConfigurationException;
16 import javax.xml.parsers.SAXParser;
17 import javax.xml.parsers.SAXParserFactory;
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.List;
25
26 /**
27 * <font color=red>NOT FUNCTIONAL YET</font>. A JavaBean to display and manage XML in a tree.
28 *
29 *
30 * @author cris
31 */
32 public class XmlTree extends JTree {
33
34 private static SAXParserFactory defaultSAXParserFactory;
35 private TreeNode rootNode;
36
37
38 /**
39 * Constructor XmlTree.
40 * @param xml
41 */
42 public XmlTree(SAXParserFactory spf, String xml) throws SAXException, ParserConfigurationException, IOException {
43 rootNode=new DocumentRootXmlTreeNode();
44 setModel(new DefaultTreeModel(rootNode));
45 SAXParser sp = spf.newSAXParser();
46 XmlTreeContentHandler ch = new XmlTreeContentHandler(this);
47 sp.parse(new ByteArrayInputStream(xml.getBytes()), ch);
48
49 ((DefaultTreeCellRenderer)getCellRenderer()).setClosedIcon(new ElementXmlIcon());
50 ((DefaultTreeCellRenderer)getCellRenderer()).setOpenIcon(new ElementXmlIcon());
51 ((DefaultTreeCellRenderer)getCellRenderer()).setLeafIcon(new ElementXmlIcon());
52
53 }
54
55
56 public static void main(String[] args) throws Exception {
57
58 String xml =
59 "<?xml version=\"1.0\"?>"+
60 "<sadun:test xmlns:sadun=\"http://www.sadun.org\">"+
61 " <sadun:internal-element>"+
62 " TextNode"+
63 " </sadun:internal-element>"+
64 "</sadun:test>";
65
66 JFrame frame = new JFrame();
67 SAXParserFactory spf = SAXParserFactory.newInstance();
68 spf.setValidating(false);
69 XmlTree tree = new XmlTree(spf, xml);
70 frame.getContentPane().add(tree);
71 frame.pack();
72 frame.setVisible(true);
73
74
75 }
76
77 }
78
79 abstract class BaseXmlTreeNode implements TreeNode {
80
81 private List children=new ArrayList();
82 private TreeNode parent;
83
84 protected BaseXmlTreeNode(TreeNode parent) {
85 this.parent=parent;
86 }
87
88 public void appendChild(BaseXmlTreeNode node) {
89 insertChild(node, children.size());
90 }
91
92 public void insertChildOnTop(BaseXmlTreeNode node) {
93 insertChild(node, 0);
94 }
95
96 public void insertChild(TreeNode node, int index) {
97 children.add(index, node);
98 }
99
100 public void removeChild(int index) {
101 children.remove(index);
102 }
103
104 public void removeChild(TreeNode node) {
105 int i;
106 if ((i=getIndex(node))==-1) return;
107 removeChild(i);
108 }
109
110 /**
111 * @see javax.swing.tree.TreeNode#children()
112 */
113 public Enumeration children() {
114 return new ListEnumeration(children);
115 }
116
117 /**
118 * @see javax.swing.tree.TreeNode#getChildAt(int)
119 */
120 public TreeNode getChildAt(int index) {
121 return (TreeNode)children.get(index);
122 }
123
124 /**
125 * @see javax.swing.tree.TreeNode#getChildCount()
126 */
127 public int getChildCount() {
128 return children.size();
129 }
130
131 /**
132 * @see javax.swing.tree.TreeNode#getIndex(TreeNode)
133 */
134 public int getIndex(TreeNode node) {
135 return children.indexOf(node);
136 }
137
138 /**
139 * @see javax.swing.tree.TreeNode#getParent()
140 */
141 public TreeNode getParent() {
142 return parent;
143 }
144
145 /**
146 * Return the parent, as a BaseXmlTreeNode
147 */
148 public BaseXmlTreeNode getXmlParent() {
149 return (BaseXmlTreeNode)parent;
150 }
151
152
153
154 /**
155 * @see javax.swing.tree.TreeNode#isLeaf()
156 */
157 public boolean isLeaf() {
158 return children.size()==0;
159 }
160
161 /**
162 * @see javax.swing.tree.TreeNode#getAllowsChildren()
163 */
164 public boolean getAllowsChildren() {
165 return true;
166 }
167
168 /**
169 * Sets the parent.
170 * @param parent The parent to set
171 */
172 public void setParent(TreeNode parent) {
173 if (parent==null) throw new RuntimeException("Cannot set parent node as null");
174 this.parent = parent;
175 }
176
177 protected abstract String getLabel();
178
179 public String toString() { return getLabel(); }
180
181 }
182
183 class DocumentRootXmlTreeNode extends BaseXmlTreeNode {
184
185 public DocumentRootXmlTreeNode() {
186 super(null);
187 }
188
189 protected String getLabel() { return "/"; }
190
191 }
192
193 abstract class BaseXmlTreeNodeWithNamespace extends BaseXmlTreeNode {
194
195 private String nameSpaceURI;
196
197 protected BaseXmlTreeNodeWithNamespace(TreeNode parent, String nameSpaceURI) {
198 super(parent);
199 this.nameSpaceURI=nameSpaceURI;
200 }
201
202 public boolean isPrefixed() {
203 return nameSpaceURI!=null;
204 }
205
206 /**
207 * Returns the nameSpaceURI.
208 * @return String
209 */
210 public String getNameSpaceURI() {
211 return nameSpaceURI;
212 }
213
214 /**
215 * Sets the nameSpaceURI.
216 * @param nameSpaceURI The nameSpaceURI to set
217 */
218 public void setNameSpaceURI(String nameSpaceURI) {
219 this.nameSpaceURI = nameSpaceURI;
220 }
221
222 protected String getNsPrefix() {
223 if (isPrefixed()) return nameSpaceURI+":";
224 else return "";
225 }
226
227
228 }
229
230 class ElementXmlTreeNode extends BaseXmlTreeNodeWithNamespace {
231
232 private String tag;
233
234 public ElementXmlTreeNode(BaseXmlTreeNode parent, String nameSpaceURI, String tag) {
235 super(parent, nameSpaceURI);
236 this.tag=tag;
237 }
238
239 /**
240 * Returns the tag.
241 * @return String
242 */
243 public String getTag() {
244 return tag;
245 }
246
247 /**
248 * Sets the tag.
249 * @param tag The tag to set
250 */
251 public void setTag(String tag) {
252 this.tag = tag;
253 }
254
255 protected String getLabel() { return "<"+getNsPrefix()+tag+">"; }
256
257 }
258
259 class AttributeXmlTreeNode extends BaseXmlTreeNodeWithNamespace {
260
261 private String name;
262 private String value;
263
264 public AttributeXmlTreeNode(BaseXmlTreeNode parent, String name, String value) {
265 super(parent, null);
266 this.name=name;
267 this.value=value;
268 }
269
270 /**
271 * Returns the name.
272 * @return String
273 */
274 public String getName() {
275 return name;
276 }
277
278 /**
279 * Returns the value.
280 * @return String
281 */
282 public String getValue() {
283 return value;
284 }
285
286 /**
287 * Sets the name.
288 * @param name The name to set
289 */
290 public void setName(String name) {
291 this.name = name;
292 }
293
294 /**
295 * Sets the value.
296 * @param value The value to set
297 */
298 public void setValue(String value) {
299 this.value = value;
300 }
301
302 protected String getLabel() { return "@"+name+"="+value; }
303 }
304
305 class TextXmlTreeNode extends BaseXmlTreeNode {
306
307 private String text;
308
309 public TextXmlTreeNode(BaseXmlTreeNode parent, String text) {
310 super(parent);
311 this.text=text;
312 }
313 /**
314 * Returns the text.
315 * @return String
316 */
317 public String getText() {
318 return text;
319 }
320
321 /**
322 * Sets the text.
323 * @param text The text to set
324 */
325 public void setText(String text) {
326 this.text = text;
327 }
328
329 protected String getLabel() { return "TEXT:"+text; }
330 }
331
332 class XmlTreeContentHandler extends DefaultHandler {
333
334 private StringBuffer sb = new StringBuffer();
335 private JTree tree;
336 private BaseXmlTreeNode currentNode;
337 private TreePath currentPath;
338
339
340 public XmlTreeContentHandler(JTree tree) {
341 this.tree=tree;
342 // Expect the root node to be DocumentRootXmlTreeNode
343 if (! (tree.getModel() instanceof DefaultTreeModel))
344 throw new RuntimeException("The given JTree has not a DefaultTreeModel");
345 DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
346 if (model.getRoot() == null)
347 model.setRoot(new DocumentRootXmlTreeNode());
348 else
349 if (!(model.getRoot() instanceof DocumentRootXmlTreeNode))
350 throw new RuntimeException("The given JTree has not a DocumentRootXmlTreeNode object as a root node");
351 this.currentNode=(DocumentRootXmlTreeNode)model.getRoot();
352 this.currentPath=new TreePath(currentNode);
353 }
354
355 /**
356 * @see org.xml.sax.ContentHandler#characters(char[], int, int)
357 */
358 public void characters(char[] chars, int start, int len)
359 throws SAXException {
360 for(int i=start;i<start+len;i++) {
361 sb.append(chars[i]);
362 }
363 }
364
365 /**
366 * @see org.xml.sax.ContentHandler#endDocument()
367 */
368 public void endDocument() throws SAXException {
369 currentNode=null;
370 }
371
372 /**
373 * @see org.xml.sax.ContentHandler#endElement(String, String, String)
374 */
375 public void endElement(String nameSpaceURI, String localName, String qName)
376 throws SAXException {
377 TextXmlTreeNode txtNode = new TextXmlTreeNode(currentNode, sb.toString());
378 currentNode.appendChild(txtNode);
379 sb.delete(0, sb.length());
380 //System.out.println("Expanding "+currentPath.pathByAddingChild(txtNode));
381 //tree.expandPath(currentPath.pathByAddingChild(txtNode));
382 currentNode=currentNode.getXmlParent();
383 currentPath=currentPath.getParentPath();
384 }
385
386 /**
387 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
388 */
389 public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
390 throws SAXException {
391 }
392
393 /**
394 * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
395 */
396 public void processingInstruction(String arg0, String arg1)
397 throws SAXException {
398 }
399
400 /**
401 * @see org.xml.sax.ContentHandler#startDocument()
402 */
403 public void startDocument() throws SAXException {
404
405 }
406
407 /**
408 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
409 */
410 public void startElement(
411 String nameSpaceURI,
412 String localName,
413 String qName,
414 Attributes atts)
415 throws SAXException {
416
417 String []tmp=split(qName);
418 ElementXmlTreeNode elemNode = new ElementXmlTreeNode(
419 currentNode,
420 tmp[0],
421 tmp[1]);
422 currentNode.appendChild(elemNode);
423 currentPath=currentPath.pathByAddingChild(currentNode);
424 tree.expandPath(currentPath);
425 for(int i=0;i<atts.getLength();i++) {
426 tmp=split(atts.getQName(i));
427
428 AttributeXmlTreeNode attrNode = new AttributeXmlTreeNode(
429 elemNode,
430 tmp[1],
431 atts.getValue(i)
432 );
433 elemNode.appendChild(attrNode);
434 }
435 currentNode=elemNode;
436 }
437
438 private String [] split(String qName) throws SAXException {
439 int z;
440 String ns=null, name;
441 if ((z=qName.indexOf(":"))!=-1) {
442 ns=qName.substring(0, z);
443 if (qName.length()>=z)
444 name=qName.substring(z+1);
445 else throw new SAXException("Name can't be empty");
446 } else name=qName;
447 return new String[] { ns, name };
448 }
449
450 /**
451 * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
452 */
453 public void startPrefixMapping(String arg0, String arg1)
454 throws SAXException {
455 }
456
457 }
458
459 abstract class XmlTreeIcon extends ImageIcon {
460 protected XmlTreeIcon(String imgName) throws IOException {
461 super(getImageBytes(imgName));
462 }
463
464 private static byte [] getImageBytes(String imgName) throws IOException {
465
466 ByteArrayOutputStream os = new ByteArrayOutputStream();
467 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(imgName);
468 int c;
469 while((c=is.read())!=-1)
470 os.write(c);
471 is.close();
472 os.close();
473 return os.toByteArray();
474 }
475 }
476
477 class ElementXmlIcon extends XmlTreeIcon {
478 public ElementXmlIcon() throws IOException { super("element.jpg"); }
479 }
480
481 class AttributeXmlIcon extends XmlTreeIcon {
482 public AttributeXmlIcon() throws IOException { super("attribute.gif"); }
483 }
484
485 class TextXmlIcon extends XmlTreeIcon {
486 public TextXmlIcon() throws IOException { super("text.gif"); }
487 }