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 
18 package ui;
19 
20 import java.awt.BorderLayout;
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.Dimension;
24 import java.awt.Font;
25 import java.awt.Image;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.event.TextEvent;
29 import java.awt.event.TextListener;
30 import java.io.BufferedReader;
31 import java.io.File;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.InputStreamReader;
35 import java.io.UnsupportedEncodingException;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.util.EventObject;
39 import java.util.Hashtable;
40 import java.util.Iterator;
41 import java.util.Map;
42 import java.util.Vector;
43 
44 import javax.swing.BorderFactory;
45 import javax.swing.ImageIcon;
46 import javax.swing.JButton;
47 import javax.swing.JComponent;
48 import javax.swing.JFrame;
49 import javax.swing.JLabel;
50 import javax.swing.JMenu;
51 import javax.swing.JMenuBar;
52 import javax.swing.JMenuItem;
53 import javax.swing.JPanel;
54 import javax.swing.JScrollPane;
55 import javax.swing.JSplitPane;
56 import javax.swing.JTextArea;
57 import javax.swing.JTextField;
58 import javax.swing.JTree;
59 import javax.swing.event.TreeSelectionEvent;
60 import javax.swing.event.TreeSelectionListener;
61 import javax.swing.tree.DefaultTreeCellRenderer;
62 import javax.swing.tree.TreeNode;
63 import javax.swing.tree.TreeSelectionModel;
64 
65 import org.w3c.dom.Attr;
66 import org.w3c.dom.Document;
67 import org.w3c.dom.Element;
68 import org.w3c.dom.Node;
69 import org.xml.sax.ErrorHandler;
70 import org.xml.sax.SAXException;
71 import org.xml.sax.SAXParseException;
72 
73 /**
74  * A sample DOM Tree Viewer. This sample program illustrates how to
75  * traverse a DOM tree and display it in a Swing JTree View.
76  *
77  * @version $Id: TreeView.java 778248 2009-05-24 22:39:36Z mrglavas $
78  */
79 public class TreeView extends JFrame implements ActionListener, TextListener {
80 
81     private static final long serialVersionUID = 3688504394090098738L;
82 
83     //
84     // Constants
85     //
86 
87     static final boolean DEBUG = true;
88 
89     /** Default parser name. */
90     static final String
91         DEFAULT_PARSER_NAME = "org.apache.xerces.parsers.DOMParser";
92     static int WARNING = 0;
93     static int ERROR=1;
94     static int FATAL_ERROR=2;
95 
96 
97     static final String title = "TreeViewer";
98     static final String openString = "Open";
99     static final String quitString = "Quit";
100     static final String reloadString = "Reload current XML file";
101     static final String expandString = "Expand Tree";
102     static final String collapseString = "Collapse Tree";
103 
104     //
105     // Data
106     //
107 
108     ErrorStorer ef;
109     String fname;
110     DOMTree m_tree;
111     JTextArea sourceText, messageText;
112     Vector textLine;
113     FileNameInput fni;
114     DOMParserSaveEncoding parser;
115     Image openFolder;
116     Image closedFolder;
117     Image leafImage;
118 
119     /**
120      *  Constructor
121      */
TreeView()122     public TreeView() {
123         this(null);
124     }
125 
126     /**
127      *  Constructor
128      */
TreeView(String uri)129     public TreeView(String uri) {
130         super(uri);
131         openFolder = DefaultImages.createOpenFolderImage();
132         closedFolder = DefaultImages.createClosedFolderImage();
133         leafImage = DefaultImages.createLeafImage();
134         parser = new DOMParserSaveEncoding();
135         ef = new ErrorStorer();
136         fname = uri;
137         JMenuBar jmb = new JMenuBar();
138         JMenu fileMenu = new JMenu("File");
139         JMenuItem item;
140 
141         item = new JMenuItem(openString);
142         fileMenu.add(item);
143         item.addActionListener(this);
144 
145         item = new JMenuItem(quitString);
146         fileMenu.add(item);
147         item.addActionListener(this);
148 
149         JMenu shortcutMenu = new JMenu("Shortcuts");
150 
151         item = new JMenuItem(expandString);
152         shortcutMenu.add(item);
153         item.addActionListener(this);
154 
155         item = new JMenuItem(collapseString);
156         shortcutMenu.add(item);
157         item.addActionListener(this);
158 
159         item = new JMenuItem(reloadString);
160         shortcutMenu.add(item);
161         item.addActionListener(this);
162 
163         jmb.add(fileMenu);
164         jmb.add(shortcutMenu);
165         setJMenuBar(jmb);
166 
167         getContentPane().add(createUI(fname));
168 
169     }
170 
171     /** create and return the entire UI from the root TreeNode
172      */
createUI(String filename)173     JComponent createUI(String filename) {
174         if (DEBUG) System.out.println("START createUI:"+filename);
175 
176         // create the message panel first so we can send messages to it...
177         messageText = new JTextArea(3,40);
178         messageText.setFont(new Font("dialog", Font.PLAIN, 12));
179         JPanel messagePanel = new JPanel(new BorderLayout());
180         messagePanel.add(new JScrollPane(messageText) {
181             private static final long serialVersionUID = 3978426918603075632L;
182             public Dimension getPreferredSize(){
183                 Dimension size = TreeView.this.getSize();
184                 return new Dimension(size.width, size.height / 4);
185                 }
186             public Dimension getMinimumSize(){
187                 return new Dimension(100, 100);
188                 }
189             },
190             BorderLayout.CENTER);
191         messagePanel.setBorder(BorderFactory.createCompoundBorder(
192             BorderFactory.createTitledBorder("Messages"),
193             BorderFactory.createEmptyBorder(4, 4, 4, 4)
194             ));
195 
196         // create the TextArea for XML source
197         sourceText = new JTextArea();
198         sourceText.setFont(new Font("monospaced", Font.PLAIN, 12));
199         sourceText.setBackground(Color.white);
200         sourceText.setForeground(Color.black);
201         sourceText.setSelectedTextColor(Color.black);
202         sourceText.setSelectionColor(Color.red);
203         sourceText.setEditable(false);
204         JPanel sourcePanel = new JPanel(new BorderLayout());
205         sourcePanel.add(new JScrollPane(sourceText){
206             private static final long serialVersionUID = 4121135831458068789L;
207             public Dimension getPreferredSize(){
208                 Dimension size = TreeView.this.getSize();
209                 return new Dimension(size.width / 2, size.height * 3 / 5);
210                 }
211             public Dimension getMinimumSize(){
212                 return new Dimension(100, 100);
213                 }
214             },
215             BorderLayout.CENTER);
216         sourcePanel.setBorder(BorderFactory.createCompoundBorder(
217             BorderFactory.createTitledBorder("Source View"),
218             BorderFactory.createEmptyBorder(4, 4, 4, 4)
219             ));
220 
221         // create the JTree and scroll pane.
222         JPanel treePanel = new JPanel(new BorderLayout());
223         m_tree = new DOMTree();
224         m_tree.setCellRenderer(new XMLTreeCellRenderer());
225         m_tree.getSelectionModel().setSelectionMode
226             (TreeSelectionModel.SINGLE_TREE_SELECTION);
227 
228         // Listen for when the selection changes, call nodeSelected(node)
229         m_tree.addTreeSelectionListener(
230             new TreeSelectionListener() {
231                 public void valueChanged(TreeSelectionEvent e) {
232                     TreeNode node = (TreeNode)
233                         (e.getPath().getLastPathComponent());
234 
235                     nodeSelected(node);
236                 }
237             }
238         );
239         m_tree.setRowHeight(18);
240         m_tree.setFont(new Font("dialog", Font.PLAIN, 12));
241 
242         treePanel.add(new JScrollPane(m_tree) {
243             private static final long serialVersionUID = 3977860665971126320L;
244             public Dimension getPreferredSize(){
245                 Dimension size = TreeView.this.getSize();
246                 return new Dimension(size.width / 2, size.height * 3 / 5);
247                 }
248             public Dimension getMinimumSize(){
249                 return new Dimension(100, 100);
250                 }
251             },
252             BorderLayout.CENTER);
253 
254         treePanel.setBorder(BorderFactory.createCompoundBorder(
255             BorderFactory.createTitledBorder("Tree View"),
256             BorderFactory.createEmptyBorder(4, 4, 4, 4)
257             ));
258 
259         // refreshUI loads everthything!
260         refreshUI(filename);
261 
262         // use the new JSplitPane to dynamically resize...
263         JComponent split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
264             true, treePanel, sourcePanel);
265 
266         JComponent mainSplitPane =
267             new JSplitPane(JSplitPane.VERTICAL_SPLIT,
268                            true, split, messagePanel);
269 
270         if (DEBUG) System.out.println("END createUI:"+filename);
271         return mainSplitPane;
272     }
273 
274     /** refreshUI is called when we have a new filename to parse.
275      */
refreshUI(String filename)276     void refreshUI(String filename) {
277         if (DEBUG) System.out.println("START refreshUI:"+filename);
278 
279         messageText.selectAll();
280         messageText.cut();
281 
282         if (filename == null || filename.length() == 0) {
283             messageText.setForeground(Color.red);
284             messageText.append("No input XML filename specified:"+filename+"\n");
285             return;
286         }
287 
288         fname = filename;
289         Document newRoot = getRoot(filename);
290         if (newRoot == null) {
291             messageText.setForeground(Color.red);
292             messageText.append("Unable to get new DOM Tree for:"+filename+"\n");
293             return;
294         }
295         m_tree.setDocument(newRoot);
296 
297         // new Source
298         sourceText.selectAll();
299         sourceText.cut();
300         readXMLFile(fname, sourceText);
301 
302         setTitle(title+": "+filename);
303 
304         if (m_tree!= null)
305             expandTree();
306 
307 
308         if (ef != null && ef.getErrorNodes()!=null
309                     && ef.getErrorNodes().size() > 0 ) {
310             messageText.setForeground(Color.red);
311             messageText.append("XML source, "+fname+" has errors.\n");
312             messageText.append("Please click on red Tree View items for details.\n");
313             /***/
314             Hashtable errors = ef.getErrorNodes();
315             Iterator entries = errors.entrySet().iterator();
316             while (entries.hasNext()) {
317                 Map.Entry entry = (Map.Entry) entries.next();
318                 Node node = (Node) entry.getKey();
319                 ParseError parseError = (ParseError) entry.getValue();
320                 messageText.append("node="+node.getNodeName()
321                 +", error="+parseError.getMsg()+"\n");
322             }
323         }
324         if (DEBUG) System.out.println("END refreshUI:"+filename);
325     }
326 
327     /**
328      *  Invoke the Parser on fname and return the root TreeNode.
329      */
getRoot(String filename)330     public Document getRoot(String filename) {
331         if (DEBUG) System.out.println("START getRoot:"+filename);
332 
333         if (filename == null || filename.length() == 0)
334         return null;
335 
336         try {
337             //
338             // Reset the Error Storage and handling
339             //
340 
341             ef.resetErrors();
342             parser.setErrorHandler(ef);
343             parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false); // otherwise parser.getCurrentNode() == null
344             parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
345             parser.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
346             parser.parse(filename);
347             Document document = parser.getDocument();
348             /***/
349             return document;
350         } catch (Exception e) {
351              System.err.println( "Error: Invalid XML document could not get ROOT" );
352              System.exit( 1 );
353             //e.printStackTrace(System.err);
354         }
355         return null;
356     }
357 
358     /** read the xml file from filename and append it to the JTextArea
359      */
readXMLFile(String filename, JTextArea ta)360     synchronized void readXMLFile(String filename, JTextArea ta) {
361 
362         if (DEBUG) System.out.println("START readXMLFile"+filename);
363         if (filename == null || filename.length() == 0)
364             return;
365         InputStream fis = null;
366         BufferedReader dis = null;
367         try {
368             java.net.URL file = createURL(filename);
369             fis = file.openStream();
370 
371             String javaEncoding = parser.getJavaEncoding(); // get saved java encoding
372             try
373             {
374             dis = new BufferedReader(new InputStreamReader(fis, javaEncoding ));
375             }
376             catch( UnsupportedEncodingException ex )
377             {
378             dis = new BufferedReader(new InputStreamReader(fis ));
379             }
380         } catch (Exception ex) {
381             System.err.println("ERROR: Xerces.readXMLFile: "+ex);
382             return;
383         }
384 
385         String line;
386         int len = 0;
387         textLine = new Vector();
388         String nl = "\n";
389         int nllen = nl.length();
390         StringBuffer sb = new StringBuffer();
391 
392         try{
393             readline: while ((line = dis.readLine()) != null) {
394                 sb.append(line+nl);
395                 textLine.addElement(new Integer(len));
396                 len += line.length()+nllen;
397             }
398             ta.append(sb.toString());
399         } catch (IOException io) {
400             System.err.println(io);
401             return;
402         }
403 
404         // relayout because contents have changed
405         //ta.revalidate();
406 
407         if (DEBUG) System.out.println("END readXMLFile"+filename);
408         return;
409 
410     }
411 
412     /** called when our JTree's nodes are selected.
413      */
nodeSelected(TreeNode treeNode)414     void nodeSelected(TreeNode treeNode) {
415 
416         Node node = m_tree.getNode(treeNode);
417 
418         if( node == null )     // It is possible to get a null node
419             return;
420 
421         StringBuffer sb = new StringBuffer();
422         messageText.selectAll();
423         messageText.cut();
424 
425 
426         //fix
427 
428        //JTextArea sourceText = sourceText;
429        Object errorObject = ef == null ? null : ef.getError(node);
430        if (errorObject != null) {
431            // There *is* an error in this node.
432            messageText.setForeground(Color.red);
433            ParseError eip = (ParseError)errorObject;
434            sb.append("Error: "+eip.getMsg()+"\n");
435            int lineNo = eip.getLineNo();
436            int pos  = 0;
437            int next = 0;
438            int sizeOfTextLine = textLine.size();
439 
440            if( lineNo < sizeOfTextLine )
441               {
442               pos = ((Integer)textLine.elementAt(lineNo-1)).intValue();
443               next = (lineNo == sizeOfTextLine ) ?
444                pos :
445                (((Integer)textLine.elementAt(lineNo)).intValue());
446               }
447            else
448               {
449               pos  = (( Integer) textLine.elementAt( sizeOfTextLine - 1 )).intValue();
450               next = pos + 2;
451               }
452 
453            sourceText.select(pos, next );
454            //m_textScrollPane.repaint();
455        } else {
456            messageText.setForeground(Color.black);
457            sourceText.select(0, 0 );
458        }
459 
460         //fix
461 
462 
463         if (node.getNodeType() == Node.ELEMENT_NODE
464                     || node.getNodeType() == Node.TEXT_NODE
465                 || node.getNodeType() == Node.CDATA_SECTION_NODE )
466                  {
467                     sb.append(node.toString());
468             }
469 
470         messageText.append(sb.toString());
471     }
472 
473     /** called when a the text value has changed in the FileNameInput.
474      *  read in new XML file.
475      */
textValueChanged(TextEvent e)476     public void textValueChanged(TextEvent e) {
477         try {
478             if (fni != null)
479                 fni.setVisible(false);
480             fname = ((JTextField)e.getSource()).getText();
481             if (DEBUG) System.out.println("textValueChanged:"+fname);
482             refreshUI(fname);
483 
484         } catch (Exception ex) {
485            System.err.println( "Error: while trying to refresh gui" );
486            System.exit( 1 );
487         //    ex.printStackTrace();
488         }
489     }
490 
491     /** called to handle menu actions.
492      */
actionPerformed(java.awt.event.ActionEvent e)493     public void actionPerformed(java.awt.event.ActionEvent e) {
494         if (DEBUG) System.err.println("ACTION: "+e.getActionCommand()+", "+e.paramString());
495 
496         if (e.getActionCommand().equals(quitString)) {
497           System.exit(0);
498         }
499         else if (e.getActionCommand().equals(openString)) {
500 
501             fni = new FileNameInput("Open File");
502             fni.addTextListener(this);
503             fni.setVisible(true);
504         }
505         else if (e.getActionCommand().equals(expandString)) {
506             expandTree();
507         }
508         else if (e.getActionCommand().equals(collapseString)) {
509             int rows = m_tree.getRowCount();
510             for (int i = 0; i < rows; i++) {
511                 m_tree.collapseRow(i);
512             }
513         }
514         else
515         //if (e.getActionCommand().equals(reloadString)) {
516             refreshUI(fname);
517         //}
518     }
519 
expandTree()520     void expandTree() {
521         int rows = 0;
522         for (int levels=0; levels <= 4; levels++) {
523             rows=m_tree.getRowCount();
524             for (int i = 0; i < rows; i++) {
525                 m_tree.expandRow(i);
526             }
527         }
528     }
529 
530     /*
531      * The XMLTreeCellRenderer is an inner class which enables the
532      * highlighting of errors in the tree and shows the gender values
533      * as different icons.
534      */
535     class XMLTreeCellRenderer extends DefaultTreeCellRenderer {
536 
537         private static final long serialVersionUID = 3761130444229720113L;
538 
getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus)539         public Component getTreeCellRendererComponent(JTree tree, Object value,
540                           boolean selected, boolean expanded,
541                           boolean leaf, int row,
542                               boolean hasFocus)
543         {
544             Node node = ((DOMTree)tree).getNode(value);
545             Component comp = super.getTreeCellRendererComponent(tree, value,
546                            selected,  expanded, leaf,  row, hasFocus);
547             if (selected) {
548                 comp.setBackground(Color.blue);
549             }
550             if (ef != null
551             && ef.getErrorNodes() != null
552             && value != null
553             && node != null
554             && ef.getErrorNodes().containsKey( node )) {
555                 comp.setForeground(Color.red);
556             }
557 
558             if (node != null) {
559                 if (leaf) {
560                     setIcon(new ImageIcon(leafImage));
561                 } else if (expanded) {
562                     setIcon(new ImageIcon(openFolder));
563                 } else {
564                     setIcon(new ImageIcon(closedFolder));
565                 }
566             }
567             if (node != null && node instanceof Element) {
568 
569                 Element txNode = (Element)node;
570                 Attr txAtt = (Attr)txNode.getAttributeNode("gender");
571                 if (txAtt != null) {
572                     if (txAtt.getValue().equals("male")) {
573                         setIcon(new ImageIcon("male.gif"));
574                     } else
575                     if (txAtt.getValue().equals("female")) {
576                         setIcon(new ImageIcon("female.gif"));
577                     }
578                 }
579             }
580 
581             return comp;
582         }
583     }
584 
585     /*
586      * The FileNameInput is an inner class which allows the user
587      * to enter a filename. It exists due to a Swing bug which
588      * has problems with the real file input panel.
589      */
590     class FileNameInput extends JFrame implements ActionListener {
591 
592         private static final long serialVersionUID = 3257562893292615472L;
593 
594         JLabel fileLabel;
595         JTextField textField;
596         JButton ok;
597         JButton cancel;
598         Vector textListeners;
599 
FileNameInput()600         public FileNameInput() {
601             this("");
602         }
603 
FileNameInput(String title)604         public FileNameInput(String title) {
605 
606             super(title);
607 
608             fileLabel = new JLabel("Enter XML file name:");
609             textField = new JTextField();
610             textField.addActionListener(this);
611             ok = new JButton("ok");
612             cancel = new JButton("cancel");
613             JPanel buttonPanel = new JPanel();
614             buttonPanel.add(ok);
615             buttonPanel.add(cancel);
616             ok.addActionListener(this);
617             cancel.addActionListener(this);
618             getContentPane().add(fileLabel, BorderLayout.NORTH);
619             getContentPane().add(textField, BorderLayout.CENTER);
620             getContentPane().add(buttonPanel, BorderLayout.SOUTH);
621             setSize(400,100);
622         }
623 
actionPerformed(ActionEvent e)624         public void actionPerformed(ActionEvent e) {
625 
626             if (e.getSource() == ok || e.getSource() == textField) {
627                 System.out.println("FileNameInput: pressed OK");
628                     TextEvent event = new TextEvent(textField, TextEvent.TEXT_VALUE_CHANGED);
629                     deliverEvent(event);
630                     setVisible(false);
631             } else
632             if (e.getSource() == cancel) {
633                 System.out.println("FileNameInput: pressed cancel");
634                     setVisible(false);
635             }
636         }
637 
638         /**
639          * Adds a TextListener event listener.
640          *
641          * @param listener  The listener to add.
642          *
643          * @see #removeTextListener
644          */
addTextListener(TextListener listener)645         public void addTextListener(TextListener listener) {
646 
647             // is there anything to do?
648             if (listener == null)
649                 return;
650 
651             if (textListeners == null)
652                textListeners = new Vector();
653 
654             // add listener
655             textListeners.addElement(listener);
656             }
657 
658         /**
659          * Removes a TextListener event listener.
660          *
661          * @param listener  The listener to remove.
662          *
663          * @see #addTextListener
664          */
removeTextListener(TextListener listener)665         public void removeTextListener(TextListener listener) {
666 
667             // is there anything to do?
668             if (listener == null || textListeners == null)
669                 return;
670 
671             // add listener
672             textListeners.removeElement(listener);
673             }
674 
675 
676         /**
677          * This function delivers TextListener events, when the ok
678          * button is clicked.
679          *
680          * @param evt The event to deliver.
681          */
deliverEvent(EventObject evt)682         protected void deliverEvent(EventObject evt) {
683 
684             if (evt instanceof TextEvent) {
685                 TextEvent event = (TextEvent)evt;
686 
687                 Vector l;
688                 synchronized (textListeners) { l = (Vector)textListeners.clone(); }
689 
690                 int size = l.size();
691                 for (int i = 0; i < size; i++)
692                     ((TextListener)l.elementAt(i)).textValueChanged(event);
693                 }
694             }
695     }
696 
697     //
698     // Create a URL object from either a URL string or a plain file name.
699     //
createURL(String name)700     static URL createURL(String name) throws Exception {
701         try {
702                 URL u = new URL(name);
703                 return u;
704         } catch (MalformedURLException ex) {
705         }
706         URL u = new URL("file:" + new File(name).getAbsolutePath());
707         return u;
708     }
709 
710     /**
711      * The ErrorStorer maps Nodes to errors. It receives a reference
712      * to the ErrorTreeFactory in the Constructor.
713      *
714      * When error is called, it asks the
715      * ErrorTreeFactory for the current node, and uses this as the
716      * "key" of a Hashtable, with the error as a value. The error
717      * value is wrapped up nicely in an ParseError object.
718      *
719      * It is used in the XML Tutorial to illustrate how to implement
720      * the ErrorListener to provide error storage for later reference.
721      *
722      */
723     class ErrorStorer
724         implements ErrorHandler
725 
726     {
727 
728         //
729         // Data
730         //
731         Hashtable errorNodes = null;
732 
733         /**
734          * Constructor
735          */
ErrorStorer()736         public ErrorStorer() {
737         }
738 
739         /**
740          * The client is is allowed to get a reference to the Hashtable,
741          * and so could corrupt it, or add to it...
742          */
getErrorNodes()743         public Hashtable getErrorNodes() {
744             return errorNodes;
745         }
746 
747         /**
748          * The ParseError object for the node key is returned.
749          * If the node doesn't have errors, null is returned.
750          */
getError(Node node)751         public Object getError(Node node) {
752             if (errorNodes == null)
753                 return null;
754             return errorNodes.get(node);
755         }
756 
757         /**
758          * Reset the error storage.
759          */
resetErrors()760         public void resetErrors() {
761             if (errorNodes != null)
762             errorNodes.clear();
763         }
764 
765         /***/
warning(SAXParseException ex)766         public void warning(SAXParseException ex) {
767             handleError(ex, WARNING);
768         }
769 
error(SAXParseException ex)770         public void error(SAXParseException ex) {
771             handleError(ex, ERROR);
772         }
773 
fatalError(SAXParseException ex)774         public void fatalError(SAXParseException ex) throws SAXException {
775             handleError(ex, FATAL_ERROR);
776         }
777 
handleError(SAXParseException ex, int type)778         private void handleError(SAXParseException ex, int type) {
779             System.out.println("!!! handleError: "+ex.getMessage());
780 
781             StringBuffer errorString = new StringBuffer();
782             errorString.append("at line number, ");
783             errorString.append(ex.getLineNumber());
784             errorString.append(": ");
785             errorString.append(ex.getMessage());
786 
787             // Node current = parser.getCurrentNode();
788 
789             Node current = null ;
790 
791             try
792               {
793               current = ( Node ) parser.getProperty( "http://apache.org/xml/properties/dom/current-element-node" );
794 
795               }
796             catch( SAXException exception  )
797               {
798                ;
799               }
800 
801             if (current == null) {
802                 System.err.println("Error in handleError. getCurrentNode()==null!");
803                 return;
804             }
805 
806             if (errorNodes == null)
807                 errorNodes = new Hashtable();
808             ParseError previous = (ParseError) errorNodes.get(current);
809             ParseError eip  = null;
810             // if a Node already has an error, we accumulate the text here...
811             if (previous != null) {
812                 eip = previous;
813                 errorString = new StringBuffer(previous.getMsg()+"\n"+errorString.toString());
814                 eip.setMsg(errorString.toString());
815             } else {
816                 eip = new
817                     ParseError(
818                         ex.getSystemId(),
819                         ex.getLineNumber(),
820                         ex.getColumnNumber(),
821                         "",
822                         errorString.toString());
823             }
824 
825             // put it in the Hashtable.
826             errorNodes.put(current, eip);
827         }
828 
829     }
830 
831     /**
832      * The ParseError class wraps up all the error info from
833      * the ErrorStorer's error method.
834      *
835      * @see ErrorStorer
836      */
837     class ParseError extends Object {
838 
839         //
840         // Data
841         //
842 
843         String fileName;
844         int lineNo;
845         int charOffset;
846         Object key;
847         String msg;
848 
849         /**
850          * Constructor
851          */
ParseError(String fileName, int lineNo, int charOffset, Object key, String msg)852         public ParseError(String fileName, int lineNo, int charOffset,
853                            Object key,
854                            String msg)
855         {
856             this. fileName=fileName;
857             this. lineNo=lineNo;
858             this. charOffset=charOffset;
859             this. key=key;
860             this. msg=msg;
861         }
862 
863         //
864         // Getters...
865         //
getFileName()866         public String getFileName() { return fileName; }
getLineNo()867         public int getLineNo() { return lineNo; }
getCharOffset()868         public int getCharOffset() { return charOffset;}
getKey()869         public Object getKey() { return key; }
getMsg()870         public String getMsg() { return msg; }
setMsg(String s)871         public void setMsg(String s) { msg = s; }
872     }
873 
874 
875     //
876     // Main
877     //
878 
879     /** Main program entry point. */
main(String argv[])880     public static void main(String argv[]) {
881 
882         // vars
883         int parserNameIndex = -1;
884         String parserName = DEFAULT_PARSER_NAME;
885 
886         // check parameters
887         for (int i = 0; i < argv.length; i++) {
888             String arg = argv[i];
889 
890             // options
891             if (arg.startsWith("-")) {
892                 if (arg.equals("-p")) {
893                     if (i == argv.length - 1) {
894                         System.err.println("error: missing parser class");
895                         System.exit(1);
896                     }
897                     parserName = argv[++i];
898                     parserNameIndex = i;
899                     continue;
900                 }
901 
902                 if (arg.equals("-h")) {
903                     printUsage();
904                     System.exit(1);
905                 }
906             }
907 
908             // print uri
909             System.err.println(arg+':');
910 
911             JFrame frame = null;
912             if (parserNameIndex == argv.length-1) {
913                 // null behaviour is blank screen - eg no JTree, or file dispalyed
914                 frame = new TreeView("");
915             } else {
916                 frame = new TreeView(arg);
917             }
918             frame.addWindowListener(new java.awt.event.WindowAdapter() {
919              public void windowClosing(java.awt.event.WindowEvent e) {
920                  System.exit(0);
921              }
922             });
923             frame.setSize(790, 590);
924             frame.setVisible(true);
925         }
926     } // main(String[])
927 
928     /** Prints the usage. */
printUsage()929     private static void printUsage() {
930 
931         System.err.println("usage: java ui.TreeViewer (options) uri ...");
932         System.err.println();
933         System.err.println("options:");
934         System.err.println("  -p name  Specify DOM parser class by name.");
935         System.err.println("           Default parser: "+DEFAULT_PARSER_NAME);
936         System.err.println("  -h       This help screen.");
937 
938     } // printUsage()
939 
940 } // class TreeViewer
941