1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package dom.traversal;
18
19 import java.awt.BorderLayout;
20 import java.awt.Dimension;
21 import java.awt.Font;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.util.Enumeration;
25 import java.util.Hashtable;
26
27 import javax.swing.BorderFactory;
28 import javax.swing.JButton;
29 import javax.swing.JCheckBox;
30 import javax.swing.JFrame;
31 import javax.swing.JList;
32 import javax.swing.JPanel;
33 import javax.swing.JScrollPane;
34 import javax.swing.JTextArea;
35 import javax.swing.JTextField;
36 import javax.swing.event.ListSelectionEvent;
37 import javax.swing.event.ListSelectionListener;
38 import javax.swing.event.TreeSelectionEvent;
39 import javax.swing.event.TreeSelectionListener;
40 import javax.swing.tree.DefaultTreeModel;
41 import javax.swing.tree.MutableTreeNode;
42 import javax.swing.tree.TreeNode;
43 import javax.swing.tree.TreePath;
44 import javax.swing.tree.TreeSelectionModel;
45
46 import org.apache.xerces.parsers.DOMParser;
47 import org.w3c.dom.DOMException;
48 import org.w3c.dom.Document;
49 import org.w3c.dom.Node;
50 import org.w3c.dom.traversal.DocumentTraversal;
51 import org.w3c.dom.traversal.NodeFilter;
52 import org.w3c.dom.traversal.TreeWalker;
53 import org.xml.sax.ErrorHandler;
54 import org.xml.sax.SAXException;
55 import org.xml.sax.SAXParseException;
56
57 import ui.DOMTreeFull;
58
59 /** This class shows a DOM Document in a JTree, and presents controls
60 * which allow the user to create and view the progress of a TreeWalker
61 * in the DOM tree.
62 */
63 public class TreeWalkerView
64 extends JFrame
65 implements ActionListener {
66
67 private static final long serialVersionUID = 3257566187583189559L;
68
69 Document document;
70 TreeNode lastSelected;
71 DOMParser parser;
72 JTextArea messageText;
73 JScrollPane messageScroll;
74 DOMTreeFull jtree;
75 TreeWalker treeWalker;
76 NameNodeFilter nameNodeFilter;
77
78 JButton nextButton;
79 JButton prevButton;
80 JButton removeButton;
81 JButton addButton;
82 JTextField addText;
83 JButton newIterator;
84 JList whatToShow;
85 JCheckBox match;
86 JTextField nameFilter;
87
88 // treeWalker specific buttons
89 JButton parentButton;
90 JButton nextSiblingButton;
91 JButton previousSiblingButton;
92 JButton firstChildButton;
93 JButton lastChildButton;
94 JButton currentButton;
95
96 String whatArray[] = new String [] {
97 "ALL",
98 "ELEMENT",
99 "ATTRIBUTE",
100 "TEXT",
101 "CDATA_SECTION",
102 "ENTITY_REFERENCE",
103 "ENTITY",
104 "PROCESSING_INSTRUCTION",
105 "COMMENT",
106 "DOCUMENT",
107 "DOCUMENT_TYPE",
108 "DOCUMENT_FRAGMENT",
109 "NOTATION"
110 };
111 JCheckBox expandERs;
112
113
114 /** main */
115 public static void main (String args[]) {
116
117 if (args.length > 0) {
118 String filename = args[0];
119 try {
120 TreeWalkerView frame = new TreeWalkerView(filename) ;
121 frame.addWindowListener(new java.awt.event.WindowAdapter() {
122 public void windowClosing(java.awt.event.WindowEvent e) {
123 System.exit(0);
124 }
125 });
126 frame.setSize(640, 700);
127 frame.setVisible(true);
128 } catch (Exception e) {
129 e.printStackTrace(System.err);
130 }
131 }
132 }
133
134 Hashtable treeNodeMap = new Hashtable();
135
136 /** Constructor */
137 public TreeWalkerView (String filename) {
138 super("TreeWalkerView: "+filename);
139 try {
140 parser = new DOMParser();
141 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
142 parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
143 Errors errors = new Errors();
144 parser.setErrorHandler(errors);
145 parser.parse(filename);
146 document = parser.getDocument();
147
148 if (!document.isSupported("Traversal", "2.0")) {
149 // This cannot happen with the DOMParser...
150 throw new RuntimeException("This DOM Document does not support Traversal");
151
152 }
153
154 // jtree UI setup
155 jtree = new DOMTreeFull((Node)document);
156 jtree.getSelectionModel().setSelectionMode
157 (TreeSelectionModel.SINGLE_TREE_SELECTION);
158
159 // Listen for when the selection changes, call nodeSelected(node)
160 jtree.addTreeSelectionListener(
161 new TreeSelectionListener() {
162 public void valueChanged(TreeSelectionEvent e) {
163 TreePath path = (TreePath)e.getPath();
164 TreeNode treeNode = (TreeNode)path.getLastPathComponent();
165 if(jtree.getSelectionModel().isPathSelected(path))
166 nodeSelected(treeNode);
167 }
168 }
169 );
170
171 //
172 // controls
173 //
174
175 BorderLayout borderLayout = new BorderLayout();
176
177 //iterate panel
178 JPanel iteratePanel = new JPanel();
179 iteratePanel.setBorder(BorderFactory.createCompoundBorder(
180 BorderFactory.createTitledBorder("Document Order Traversal"),
181 BorderFactory.createEmptyBorder(4, 4, 4, 4)
182 ));
183
184 prevButton = new JButton("Previous");
185 iteratePanel.add(prevButton);
186 prevButton.addActionListener(this);
187
188 nextButton = new JButton("Next");
189 iteratePanel.add(nextButton);
190 nextButton.addActionListener(this);
191
192 //walkerPanel
193 JPanel walkerPanel = new JPanel();
194 walkerPanel.setLayout(new BorderLayout());
195 walkerPanel.setBorder(BorderFactory.createCompoundBorder(
196 BorderFactory.createTitledBorder("Walk"),
197 BorderFactory.createEmptyBorder(4, 4, 4, 4)
198 ));
199
200 parentButton = new JButton("Parent");
201 walkerPanel.add(parentButton, BorderLayout.NORTH);
202 parentButton.addActionListener(this);
203
204
205 JPanel childPanel = new JPanel();
206 firstChildButton = new JButton("First Child");
207 childPanel.add(firstChildButton);
208 firstChildButton.addActionListener(this);
209
210 lastChildButton = new JButton("Last Child");
211 childPanel.add(lastChildButton);
212 lastChildButton.addActionListener(this);
213 walkerPanel.add(childPanel, BorderLayout.SOUTH);
214
215 nextSiblingButton = new JButton("Next Sibling");
216 walkerPanel.add(nextSiblingButton, BorderLayout.EAST);
217 nextSiblingButton.addActionListener(this);
218
219 previousSiblingButton = new JButton("Previous Sibling");
220 walkerPanel.add(previousSiblingButton, BorderLayout.WEST);
221 previousSiblingButton.addActionListener(this);
222
223 //DOM tree panel
224 JPanel domPanel = new JPanel();
225 domPanel.setLayout(new BorderLayout());
226 domPanel.setBorder(BorderFactory.createCompoundBorder(
227 BorderFactory.createTitledBorder("Selected Node"),
228 BorderFactory.createEmptyBorder(4, 4, 4, 4)
229 ));
230
231 JPanel buttonPanel = new JPanel();
232 currentButton = new JButton("Current");
233 buttonPanel.add(currentButton);
234 currentButton.addActionListener(this);
235
236 removeButton = new JButton("Remove");
237 buttonPanel.add(removeButton);
238 removeButton.addActionListener(this);
239
240 addButton = new JButton("Append Text");
241 addText = new JTextField(10);
242 buttonPanel.add(addButton);
243 domPanel.add(buttonPanel, BorderLayout.NORTH);
244 domPanel.add(addText, BorderLayout.CENTER);
245 addButton.addActionListener(this);
246
247 // treeWalker settings
248 JPanel settingsPanel = new JPanel();
249 settingsPanel.setLayout(new BorderLayout());
250 settingsPanel.setBorder(BorderFactory.createCompoundBorder(
251 BorderFactory.createTitledBorder("Filter Settings"),
252 BorderFactory.createEmptyBorder(4, 4, 4, 4)
253 ));
254 JPanel treeWalkerPanel = new JPanel();
255 treeWalkerPanel.setLayout(new BorderLayout());
256
257 newIterator = new JButton("createTreeWalker");
258 treeWalkerPanel.add(newIterator, BorderLayout.NORTH);
259 expandERs = new JCheckBox("expandEntityReferences");
260 expandERs.setSelected(true);
261 treeWalkerPanel.add(expandERs, BorderLayout.SOUTH);
262 settingsPanel.add(treeWalkerPanel, BorderLayout.NORTH);
263 newIterator.addActionListener(this);
264
265 JPanel whatPanel = new JPanel();
266 whatPanel.setBorder(BorderFactory.createCompoundBorder(
267 BorderFactory.createTitledBorder("whatToShow"),
268 BorderFactory.createEmptyBorder(0, 0, 0, 0)
269 ));
270 whatToShow = new JList(whatArray);
271 JScrollPane whatScroll =
272 new JScrollPane(whatToShow) {
273 private static final long serialVersionUID = 3545240236637305138L;
274 public Dimension getPreferredSize(){
275 return new Dimension(200, 65 );
276 }
277 };
278
279 whatPanel.add(whatScroll);
280
281
282 JPanel filterPanel = new JPanel();
283 filterPanel.setBorder(BorderFactory.createCompoundBorder(
284 BorderFactory.createTitledBorder("NodeNameFilter"),
285 BorderFactory.createEmptyBorder(4, 4, 4, 4)
286 ));
287 filterPanel.setLayout(new BorderLayout());
288 match = new JCheckBox("match/ignore node name", true);
289 nameFilter = new JTextField(10);
290 filterPanel.add(match, BorderLayout.NORTH);
291 filterPanel.add(nameFilter, BorderLayout.SOUTH);
292
293 settingsPanel.add(treeWalkerPanel, BorderLayout.NORTH);
294 settingsPanel.add(whatPanel, BorderLayout.CENTER);
295 settingsPanel.add(filterPanel, BorderLayout.SOUTH);
296
297
298 // Listen for when the selection changes, call nodeSelected(node)
299 whatToShow.addListSelectionListener(
300 new ListSelectionListener() {
301 public void valueChanged(ListSelectionEvent e) {
302 // do nothing on selection...
303 }
304 }
305 );
306
307
308 JPanel controlsPanel = new JPanel(new BorderLayout());
309 controlsPanel.setFont(new Font("Dialog", Font.PLAIN, 8));
310 JPanel buttonsPanel = new JPanel(new BorderLayout());
311 buttonsPanel.add(iteratePanel, BorderLayout.NORTH);
312 buttonsPanel.add(walkerPanel, BorderLayout.CENTER);
313 buttonsPanel.add(domPanel, BorderLayout.SOUTH);
314 controlsPanel.add(buttonsPanel, BorderLayout.NORTH);
315 controlsPanel.add(settingsPanel, BorderLayout.CENTER);
316
317 controlsPanel.setBorder(BorderFactory.createCompoundBorder(
318 BorderFactory.createTitledBorder("Controls"),
319 BorderFactory.createEmptyBorder(4, 4, 4, 4)
320 ));
321
322
323 // tree panel
324 JPanel treePanel = new JPanel(new BorderLayout());
325
326 JScrollPane treeScroll = new JScrollPane(jtree) ;
327 treeScroll.setBorder(BorderFactory.createCompoundBorder(
328 BorderFactory.createTitledBorder("Tree View"),
329 BorderFactory.createEmptyBorder(4, 4, 4, 4)
330 ));
331
332 // message text UI setup
333 messageText = new JTextArea(3,5);
334
335 JPanel messagePanel = new JPanel(new BorderLayout());
336 messageScroll = new JScrollPane(messageText);
337 messagePanel.add(messageScroll);
338 messagePanel.setBorder(BorderFactory.createCompoundBorder(
339 BorderFactory.createTitledBorder("Messages"),
340 BorderFactory.createEmptyBorder(4, 4, 4, 4)
341 ));
342
343 JPanel mainPanel = new JPanel();
344 mainPanel.setLayout(new BorderLayout());
345 mainPanel.add(controlsPanel, BorderLayout.EAST);
346 mainPanel.add(treeScroll, BorderLayout.CENTER);
347 mainPanel.add(messagePanel, BorderLayout.SOUTH);
348 getContentPane().add(mainPanel);
349
350 Hashtable errorNodes = errors.getErrorNodes();
351 Enumeration elements = errorNodes.elements();
352 while (elements.hasMoreElements()) {
353 //*** append errors to messageText
354 messageText.append( (String)elements.nextElement());
355 }
356
357 boolean expand = expandERs.isSelected();
358 treeWalker = ((DocumentTraversal)document).
359 createTreeWalker(
360 document,
361 NodeFilter.SHOW_ALL,
362 new NameNodeFilter(),
363 expand);
364
365 } catch (Exception e) {
366 e.printStackTrace(System.err);
367 }
368 }
369
370 public void actionPerformed(ActionEvent e) {
371
372 if (e.getSource() == newIterator) {
373
374 TreeNode treeNode = (TreeNode)jtree.getLastSelectedPathComponent();
375 if (treeNode == null) {
376 messageText.append("Must select a tree component.");
377 return;
378 }
379
380 Node node = jtree.getNode(treeNode);
381 if (node == null) {
382 setMessage("No current Node in TreeNode: "+node);
383 }
384
385 // whatToShow section
386 int [] indices = whatToShow.getSelectedIndices();
387 int mask = 0x0;
388 for (int i = 0; i < indices.length; i++) {
389 if (indices[i] == 0) {
390 mask = 0xFFFF;
391 break;
392 } else {
393 mask = mask | 1<<indices[i]-1;
394 }
395 }
396
397 // filter section
398 String nameText = nameFilter.getText();
399 boolean matched = match.isSelected();
400 if (nameNodeFilter==null) {
401 nameNodeFilter = new NameNodeFilter();
402 }
403 if (nameText.equals("")) {
404 setMessage("NodeNameFilter name is \"\". Assuming null.");
405 nameText = null;
406 }
407 nameNodeFilter.setName(nameText);
408 nameNodeFilter.setMatch(matched);
409
410 // expand Entity References?
411 boolean expand = expandERs.isSelected();
412
413 treeWalker = ((DocumentTraversal)document).
414 createTreeWalker(
415 node,
416 mask,
417 nameNodeFilter,
418 expand);
419 setMessage("createTreeWalker:"+
420 " root="+node+
421 ", whatToShow="+mask+
422 ", match="+matched+
423 ", name="+nameText
424 );
425 return;
426
427 }
428
429 if (e.getSource() == currentButton) {
430
431 TreeNode treeNode = (TreeNode)jtree.getLastSelectedPathComponent();
432 if (treeNode == null) {
433 messageText.append("Must select a tree component.");
434 return;
435 }
436
437 Node node = jtree.getNode(treeNode);
438 if (node == null) {
439 setMessage("No current Node in TreeNode: "+node);
440 }
441 treeWalker.setCurrentNode(node);
442 return;
443
444 }
445 if (e.getSource() == addButton) {
446
447 String text = addText.getText();
448
449 if (text==null) return;
450
451 TreeNode treeNode = (TreeNode)jtree.getLastSelectedPathComponent();
452 if (treeNode == null) {
453 messageText.append("Must select a tree component to add a child to it.");
454 return;
455 }
456 TreePath path = new TreePath(
457 ((DefaultTreeModel)jtree.getModel()).getPathToRoot(treeNode));
458 if (path == null) {
459 setMessage("Could not create a path.");
460 return;
461 }
462 if(!jtree.getSelectionModel().isPathSelected(path))
463 return;
464 Node node = jtree.getNode(treeNode);
465 Node textNode = document.createTextNode(text);
466 try {
467 node.appendChild(textNode);
468 } catch (DOMException dome) {
469 setMessage("DOMException:"+dome.code+", "+dome);
470 return;
471 }
472 ((DOMTreeFull.Model)jtree.getModel()).insertNode(textNode, (MutableTreeNode)treeNode);
473
474 return;
475 }
476
477 if (e.getSource() == removeButton) {
478
479 /** If the node is not selected don't remove. */
480 TreeNode treeNode = (TreeNode)jtree.getLastSelectedPathComponent();
481 if (treeNode == null) {
482 messageText.append("Must select a tree component to remove it.");
483 return;
484 }
485 TreePath path = new TreePath(
486 ((DefaultTreeModel)jtree.getModel()).getPathToRoot(treeNode));
487 if (path == null) {
488 setMessage("Could not create a path.");
489 return;
490 }
491 if(!jtree.getSelectionModel().isPathSelected(path))
492 return;
493 Node node = jtree.getNode(treeNode);
494 if (node == null) return;
495 Node parent = node.getParentNode();
496 if (parent == null) return;
497
498 parent.removeChild(node);
499
500 ((DefaultTreeModel)jtree.getModel()).removeNodeFromParent((MutableTreeNode)treeNode);
501 return;
502 }
503
504 if (e.getSource() == previousSiblingButton) {
505 Node node = treeWalker.previousSibling();
506 handleButton(node, "previousSibling()");
507 return;
508 }
509
510 if (e.getSource() == firstChildButton) {
511 Node node = treeWalker.firstChild();
512 handleButton(node, "firstChild()");
513 return;
514 }
515
516 if (e.getSource() == lastChildButton) {
517 Node node = treeWalker.lastChild();
518 handleButton(node, "lastChild()");
519 return;
520 }
521
522 if (e.getSource() == nextSiblingButton) {
523 Node node = treeWalker.nextSibling();
524 handleButton(node, "nextSibling()");
525 return;
526 }
527
528 if (e.getSource() == parentButton) {
529 Node node = treeWalker.parentNode();
530 handleButton(node, "parentNode()");
531 return;
532 }
533
534 if (e.getSource() == nextButton) {
535 Node node = treeWalker.nextNode();
536 handleButton(node, "nextNode()");
537 return;
538 }
539
540 if (e.getSource() == prevButton) {
541 Node node = treeWalker.previousNode();
542 handleButton(node, "previousNode()");
543 return;
544 }
545
546 }
547
548 /** handle a button press: output messages and select node. */
549 void handleButton( Node node, String function) {
550
551 setMessage("treeWalker."+function+" == "+node);
552
553 if (node==null) return;
554
555 TreeNode treeNode = jtree.getTreeNode(node);
556 if (treeNode == null) {
557 setMessage("No JTree TreeNode for Node name:" + node.getNodeName());
558 return;
559 }
560
561 TreePath path = new TreePath(
562 ((DefaultTreeModel)jtree.getModel()).getPathToRoot(treeNode));
563 jtree.requestFocus();
564 jtree.setSelectionPath(path);
565 jtree.scrollPathToVisible(path);
566 }
567
568 /** Helper function to set messages */
569 void setMessage(String string) {
570 messageText.selectAll();
571 messageText.cut();
572 messageText.append(string);
573 messageText.setCaretPosition(0);
574 }
575
576 /** called when our JTree's nodes are selected.
577 */
578 void nodeSelected(TreeNode treeNode) {
579
580 lastSelected = treeNode;
581 Node node = jtree.getNode(treeNode);
582
583 if (node == null) return;
584
585 setMessage(DOMTreeFull.toString(node));
586 }
587
588 /** Utility function to expand the jtree */
589 void expandTree() {
590 for (int i = 0; i < jtree.getRowCount(); i++) {
591 jtree.expandRow(i);
592 }
593 }
594
595 class Errors implements ErrorHandler {
596
597 Hashtable errorNodes = new Hashtable();
598
599 public void warning(SAXParseException ex) {
600 store(ex, "[Warning]");
601 }
602
603 public void error(SAXParseException ex) {
604 store(ex, "[Error]");
605 }
606
607 public void fatalError(SAXParseException ex) throws SAXException {
608 store(ex, "[Fatal Error]");
609 }
610
611 public Hashtable getErrorNodes() {
612 return errorNodes;
613 }
614
615 public Object getError(Node node) {
616 return errorNodes.get(node);
617 }
618
619 public void clearErrors() {
620 errorNodes.clear();
621 }
622
623 void store(SAXParseException ex, String type) {
624
625 // build error text
626 String errorString= type+" at line number, "+ex.getLineNumber()
627 +": "+ex.getMessage()+"\n";
628
629 Node currentNode = null;
630 try {
631 currentNode = (Node)parser.getProperty("http://apache.org/xml/properties/dom-node");
632 } catch (SAXException se) {
633 System.err.println(se.getMessage());
634 return;
635 }
636 if (currentNode == null) return;
637
638 // accumulate any multiple errors per node in the Hashtable.
639 String previous = (String) errorNodes.get(currentNode);
640 if (previous != null)
641 errorNodes.put(currentNode, previous +errorString);
642 else
643 errorNodes.put(currentNode, errorString);
644 }
645 }
646
647 }