In this chapter, we will see how to programmatically call TAXI to edit an XML document for which you have a DOM representation.
Let's have an example to show how you can do that.
Save the following code under editor/test/XMLEditorTest.java
:
import fr.loria.taxi.editor.*; import fr.loria.taxi.parser.*; import javax.swing.*; import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.*; import org.w3c.dom.*; public class XMLEditorTest extends JFrame { // Constantes private static final String DEF_FILE = "../XML/Generic/generic.def"; private static final String DOCUMENT_NAME = "test.xml"; private static final String XSLT_FILE = "../XML/Generic/genericHorizontal.xsl"; // Variables private Document document; public XMLEditorTest() throws Exception { super("XMLEditorTest"); // Loading the document loadDocument(); // GUI behavior setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { System.exit(0); } }); // Content JPanel contentPane = new JPanel(new BorderLayout(5, 5), false); setContentPane(contentPane); // Tree showing the XML document final JTree tree = new JTree(createTreeModel()); contentPane.add(BorderLayout.CENTER, new JScrollPane(tree)); // South Panel JPanel southPanel = new JPanel(new GridLayout(0, 1), false); contentPane.add(BorderLayout.SOUTH, southPanel); // Checkbox to ask for the transformation of the XML document final JCheckBox transformCheckBox = new JCheckBox("Transform document on opening", true); southPanel.add(transformCheckBox); // Button to launch the edition final JButton editButton = new JButton("Edit"); southPanel.add(editButton); editButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // We disable the button the user clicked on editButton.setEnabled(false); // We instanciate e new Thread to free the AWT-EventThread new Thread() { public void run() { // Launching the edition Editor.editDOM(document, XSLT_FILE, DEF_FILE, transformCheckBox.isSelected()); // Updating the tree tree.setModel(createTreeModel()); editButton.setEnabled(true); } }.start(); } }); pack(); setSize(300, 200); } /** * Create a DefaultMutableTreeNode that reflects the structure of parameter node. */ private DefaultMutableTreeNode createTreeNode(Node node) { DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(node.getNodeName()); // Dealing with children NodeList children = node.getChildNodes(); int size = children.getLength(); Node child; for (int i = 0; i < size; i++) { child = children.item(i); if (child instanceof Element) treeNode.add(createTreeNode(child)); } return treeNode; } /** * Create a DefaultTreeModel that reflects the structure of the XML document. */ private DefaultTreeModel createTreeModel() { DefaultMutableTreeNode root = createTreeNode(document.getDocumentElement()); DefaultTreeModel treeModel = new DefaultTreeModel(root); return treeModel; } /** * Loads the XML document. */ private void loadDocument() throws Exception { Parser parser = Parser.getInstance(); parser.setValidate(true); parser.setStopAfterError(true); parser.parse(DOCUMENT_NAME); document = parser.getDocument(); } // Main method public static void main(String[] args) throws Exception { XMLEditorTest editorTest = new XMLEditorTest(); editorTest.setVisible(true); } }
Open a console, go to directory editor/test
. Compile XMLEditorTest.java
with the following command :
javac -classpath ../lib/taxi.jar;../lib/xerces.jar XMLEditorTest.java
To compile the code, you need to put in your classpath :
taxi.jar
that contains the packages
editor
and parser
,xerces.jar
that contains the package org.w3c.dom
.The code is a little long, but not difficult, and there is only one line we are really interested in : this line is in bold in the code above.
main(String[] args)
This method instanciates an XMLEditorTest
and simply displays it.
XMLEditorTest
XMLEditorTest
extends javax.swing.JFrame
. In the constructor,
we prepare the frame that will be displayed. First, we load the XML document we will edit
by calling the method loadDocument()
. Then we prepare the content of the frame.
We place inside :
javax.swing.JTree
that will show the structure of our XML document
loaded previously,javax.swing.JCheckBox
that will let the user decide whether or not
s/he wants the XML document to be transformed as soon as TAXI is opened,javax.sying.JButton
to launch TAXI.When we create the button, we place a java.awt.ActionListener
on it to listen
to when the user clicks on it. When this event occurs, method actionPerformed(ActionEvent e)
is called on that java.awt.ActionListener
. In that method, we will call XML
editor to edit the document loaded and shown by the javax.swing.JTree
. First,
we disable the button the user clicked on so that s/he won't be able to launch the edition
of the document until the edition we are about to launch has ended. Then, very important
in that case (explained later), we create a new Thread
that we immediately
start. This Thread
launches TAXI with the following line of code :
Editor.editDOM(document, XSLT_FILE, DEF_FILE, transformCheckBox.isSelected());
The static method editDOM()
of object editor.Editor
takes 4
arguments :
This method does not return until the user closes the edition window used for editing
document
inside TAXI.
When the method returns, we create a new javax.swing.DefaultTreeModel
to
reflect the possible modifications made to document
and we update the tree :
tree.setModel(createTreeModel());
And then, we enable the button to let the user the possibility to launch another edition.
Why did we instantiate a new Thread to launch TAXI ? This is simple, when
the user clicks the button, an event is created and placed on the AWT-EventThread
queue. When AWT-EventThread
deals with this event, it calls the method
actionPerformed(ActionEvent e)
, passing the event as parameter. When we call
TAXI, events for painting the application are sent to the AWT-EventThread
.
They are placed on queue as this AWT-EventThread
is not free since it is
running our method actionPerformed(ActionEvent e)
. In other words, TAXI
won't display correctly and the user won't have the possibility to close the edition window
opened to edit the XML document. But, while this edition window is not closed, our method
actionPerformed(ActionEvent e)
does not return, and thus, does not free the
AWT-EventThread
. This is a deadlock. When you programmatically call
TAXI to edit an XML document, be carefull from where you launch it. If this is from
a method called by the AWT-EventThread
, place that call in another
Thread
.
Note : it is important to disable the button used for launching the edition to prevent the user to call TAXI for editing the same document many times.
createTreeNode(Node node)
It is used to create a javax.swing.tree.DefaultMutableTreeNode
from an
org.w3c.dom.Node
(DOM representation).
createTreeModel()
It is used to create the tree we will display in the javax.swing.JTree
from
the XML document loaded, references by the instance variable document
.
loadDocument()
Loads the document pointed by constant DOCUMENT_NAME and keeps a reference to its DOM
representation via the instance variable document
.
We need to create the XML document test.xml
. Save the following code
under editor/test/test.xml
:
<?xml version="1.0" encoding="ISO-8859-1"?> <a> <b/> <c/> <d/> </a>
Open a console, go to directory editor/test
. Run XMLEditorTest with the
following command :
java -classpath ../lib/taxi.jar;. XMLEditorTest
The following window appears :
You can see the XML document test.xml
loaded and displayed as a tree. Click
the button Edit
to launch TAXI. TAXI is opened and transforms
the document (TAXI must be well configured, see the topic Getting started
in the online help if some error occurs during the transformation) :
When the transformation is over, you should see this :
You can notice that TAXI created a temporary document to work with (you can see
it in the title bar of the edition window). On the graphic, double-click on node a
to highlight it in the selection area :
In the selection area, select node d
:
Hit the delete
key to delete the selected node and transform the document :
Close the edition window. TAXI asks you whether you want to save the document :
Click No
. We don't need to save the document as a file, it is modified in
memory and that is all we need (if you want to save it, you can do it nonetheless). The
edition window is closed. Look at the window called XMLEditorTest
:
You can see that the tree is updated (node d
is no more present), and the
button Edit
turned back available. TAXI is still open, if you click again
on the button Edit
, this instance of TAXI is used to open the edition
window.
If you close the window XMLEditorTest
, a System.exit(0);
is
performed by the java.awt.WindowAdapter
we placed on XMLEditorTest
.
Thus, TAXI is closed too since it runs under the same Java Virtual Machine
stopped by this call.
We will show here things you must not do to make the call to TAXI works correctly. There are 2 :
AWT-EventThread
,AWT-EventThread
As we explained previously, if you call TAXI from the Blocking the AWT-EventThread
,
you must ensure that you won't block it and cause a deadlock.
To show what happens if you are not carefull about it, modify the constructor of
editor/test/XMLEditorTest.java
(lines 60-70, changes are in bold) :
editButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // We disable the button the user clicked on editButton.setEnabled(false); // We instanciate e new Thread to free the AWT-EventThread /* new Thread() { public void run() { */ // Launching the edition Editor.editDOM(document, XSLT_FILE, DEF_FILE, transformCheckBox.isSelected()); // Updating the tree tree.setModel(createTreeModel()); editButton.setEnabled(true); /* } }.start(); */ } });
We simply commented the creation of the Thread
, so that the call to XML
editor is now performed inside the AWT-EventThread
.
Compile XMLEditorTest.java :
javac -classpath ../lib/taxi.jar;../lib/xerces.jar XMLEditorTest.java
Run XMLEditorTest :
java -classpath ../lib/taxi.jar;. XMLEditorTest
Click the button Edit
and see what happens.
Once you have finished with that test, don't forget to undo the changes to
editor/test/XMLEditorTest.java
.
To work with the DOM representation of the XML document, TAXI creates a temporary file that holds that document. If the document as a Doctype that points to a file as relative, then TAXI will try to locate it from the temporary directory, it will certainly fail.
In editor/test/XMLEditorTest.java
, modify the value of the constant
DOCUMENT_NAME
(line 14) :
private static final String DOCUMENT_NAME = "../XML/XML/ex1.cd";
We will try to transform the XML document editor/XML/XML/ex1.cd
shipped
with TAXI. This document states that it is ruled by the DTD cd.dtd
contained in the same directory than the document :
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE collection SYSTEM "cd.dtd"> <collection> ... </collection>
Compile XMLEditorTest.java :
javac -classpath ../lib/taxi.jar;../lib/xerces.jar XMLEditorTest.java
Run XMLEditorTest (from directory editor/test
) :
java -classpath ../lib/taxi.jar;. XMLEditorTest
Click the button Edit
. TAXI opens up, begin the transformation of
the document, and displays an error message like the following one :
TAXI opened the temporary document temp58606.xml
(on your machine,
the name will surely be different) to transform it, from temporary directory
C:\DOCUME~1\Loria\LOCALS~1\Temp
(on my Windows machine). In the same directory,
the file cd.dtd
could not be found.
Conclusion : you can programmatically call TAXI to transform an XML document, for which you have a DOM representation, ruled by a DTD. But that DTD must be accessed from the XML document, either with a URL, or with an absolute path.