Home > Articles > Programming > Java

  • Print
  • + Share This
From the author of

XSLT Example 1: XSLT Document Editor

Listing 2 shows a simple Swing document editor that presents three tabbed panes: one for an XML document, one for an XSL style sheet, and one for a resulting XSLT-transformed document. Both the XML and XSL document panes are editable, so after you load the XML and XSL files from disk you can edit the documents directly. Each tabbed pane contains a scrollable DocumentPane that inherits from a JEditorPane (see Listing 3). The XML and XSL panes are treated as plain text, and the XSLT pane is treated as HTML. If an XML file and XSL file are loaded, selecting the XSLT tab will invoke an XslTransformer (Listing 1) to process the XML file by using the style sheet, and present the results as HTML in the XSLT document pane.

NOTE

This Swing application also requires the following classes: ExtensionFileFilter.java, WindowUtilities.java, and ExitListener.java. These files are included in the source file for this article.

Listing 2—XsltExample.java. A Swing application that provides three document panes: one for the XML document, one for the XSL style sheet, and one for the XSLT result.

import javax.xml.transform.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;

/** A document editor to process XML and XSL text using
 * XSLT and presenting the results as HTML. Three tabbed panes
 * are presented: an editable text pane for the XML document,
 * an editable text pane for the XSL style sheet, and a non-
 * editable HTML pane for the HTML result. If an XML and XSL
 * file are loaded, then selecting the XSLT tab will perform
 * the transformation and present the results. If there is
 * a problem processing the XML or XSL document, then a
 * message box is popped up describing the problem.
 */

public class XsltExample extends JFrame
             implements ChangeListener {
 private static final int XML = 0;
 private static final int XSL = 1;
 private static final int XSLT = 2;
 private static final String DEFAULT_TITLE = "XSLT Example";
 private static final String[] tabTitles =
                 { "XML", "XSL", "XSLT" };
 private static final String[] extensions =
                 { "xml", "xsl", "html" };
 private Action openAction, saveAction, exitAction;
 private JTabbedPane tabbedPane;
 private DocumentPane[] documents;
 private XslTransformer transformer;

 public XsltExample() {
  super(DEFAULT_TITLE);
  transformer = new XslTransformer();
  WindowUtilities.setNativeLookAndFeel();
  Container content = getContentPane();
  content.setBackground(SystemColor.control);

  // Set up menus
  JMenuBar menubar = new JMenuBar();
  openAction = new OpenAction();
  saveAction = new SaveAction();
  exitAction = new ExitAction();
  JMenu fileMenu = new JMenu("File");
  fileMenu.add(openAction);
  fileMenu.add(saveAction);
  fileMenu.add(exitAction);
  menubar.add(fileMenu);
  setJMenuBar(menubar);

  // Set up tabbed panes
  tabbedPane = new JTabbedPane();
  documents = new DocumentPane[3];
  for(int i=0; i<3; i++) {
   documents[i] = new DocumentPane();
   JPanel panel = new JPanel();
   JScrollPane scrollPane = new JScrollPane(documents[i]);
   panel.add(scrollPane);
   tabbedPane.add(tabTitles[i], scrollPane);
  }
  documents[XSLT].setContentType(DocumentPane.HTML);
  // JEditorPane has a bug, whereas the setText method does
  // not properly recognize an HTML document that has a META
  // element containing a CONTENT-TYPE, unless the EditorKit
  // is first created through setPage. Xalan automatically
  // adds a META CONTENT-TYPE to the document.
  documents[XSLT].loadFile("XSLT-Instructions.html");
  documents[XSLT].setEditable(false);
  tabbedPane.addChangeListener(this);
  content.add(tabbedPane, BorderLayout.CENTER);

  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  setSize(450, 350);
  setVisible(true);
 }

 /** Checks to see which tabbed pane was selected by the
  * user. If the XML and XSL panes hold a document, then
  * selecting the XSLT tab will perform the transformation.
  */

 public void stateChanged(ChangeEvent event) {
  int index = tabbedPane.getSelectedIndex();
  switch (index) {
   case XSLT: if (documents[XML].isLoaded() &&
           documents[XSL].isLoaded()) {
          doTransform();
         }
   case XML:
   case XSL: updateMenuAndTitle(index);
         break;
   default:
  }
 }

 /** Retrieve the documents in the XML and XSL pages
  * as text (String), pipe into a StringReader, and
  * perform the XSLT transformation. If an exception
  * occurs, present the problem in a message dialog.
  */

 private void doTransform() {
  StringWriter strWriter = new StringWriter();
  try {
   Reader xmlInput =
    new StringReader(documents[XML].getText());
   Reader xslInput =
    new StringReader(documents[XSL].getText());
   transformer.process(xmlInput, xslInput, strWriter);
  } catch(TransformerException te) {
   JOptionPane.showMessageDialog(this,
          "Error: " + te.getMessage());
  }
  documents[XSLT].setText(strWriter.toString());
 }

 /** Update the title of the application to present
  * the name of the file loaded into the selected
  * tabbed pane. Also, update the menu options (Save,
  * Load) based on which tab is selected.
  */

 private void updateMenuAndTitle(int index) {
  if ((index > -1) && (index < documents.length)) {
   saveAction.setEnabled(documents[index].isLoaded());
   openAction.setEnabled(documents[index].isEditable());
   String title = DEFAULT_TITLE;
   String filename = documents[index].getFilename();
   if (filename.length() > 0) {
    title += " - [" + filename + "]";
   }
   setTitle(title);
  }
 }

 /** Open a file dialog to either load a new file or save
  * the existing file in the present document pane.
  */

 private void updateDocument(int mode) {
  int index = tabbedPane.getSelectedIndex();
  String description = tabTitles[index] + " Files";
  String filename = ExtensionFileFilter.getFileName(".",
                      description,
                      extensions[index],
                      mode);
  if (filename != null) {
   if (mode==ExtensionFileFilter.SAVE) {
    documents[index].saveFile(filename);
   } else {
    documents[index].loadFile(filename);
   }
   updateMenuAndTitle(index);
  }
 }

 public static void main(String[] args) {
  new XsltExample();
 }

 // Open menu action to load a new file into a
 // document when selected.
 class OpenAction extends AbstractAction {
  public OpenAction() {
   super("Open ...");
  }
  public void actionPerformed(ActionEvent event) {
   updateDocument(ExtensionFileFilter.LOAD);
  }
 }

 // Save menu action to save the document in the
 // selected pane to a file.
 class SaveAction extends AbstractAction {
  public SaveAction() {
   super("Save");
   setEnabled(false);
  }
  public void actionPerformed(ActionEvent event) {
   updateDocument(ExtensionFileFilter.SAVE);
  }
 }

 // Exit menu action to close the application.
 class ExitAction extends AbstractAction {
  public ExitAction() {
   super("Exit");
  }
  public void actionPerformed(ActionEvent event) {
   System.exit(0);
  }
 }
}

Listing 3—DocumentPane.java. A JEditorPane for holding either XML or HTML documents.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;

/** A JEditorPane with support for loading and saving the
 * document. The document should be one of two
 * types: "text/plain" (default) or "text/html".
 */

public class DocumentPane extends JEditorPane {
 public static final String TEXT = "text/plain";
 public static final String HTML = "text/html";

 private boolean loaded = false;
 private String filename = "";

 public DocumentPane() {
  Font font = new Font("Monospaced", Font.PLAIN, 12);
  setFont(font);
 }

 /** Set the current page displayed in the editor pane,
  * replacing the existing document.
  */

 public void setPage(URL url) {
  loaded = false;
  try {
   super.setPage(url);
   File file = new File(getPage().toString());
   setFilename(file.getName());
   loaded = true;
  } catch (IOException ioe) {
   System.err.println("Unable to set page: " + url);
  }
 }

 /** Set the text in the document page, replace the existing
  * document.
  */

 public void setText(String text) {
  super.setText(text);
  setFilename("");
  loaded = true;
 }

 /** Load a file into the editor pane.
  *
  * Note that the setPage method of JEditorPane checks the
  * URL of the currently loaded page against the URL of the
  * new page to load. If the two URLs are the same, then
  * the page is <b>not</b> reloaded.
  */

 public void loadFile(String filename) {
  try {
   File file = new File(filename);
   setPage(file.toURL());
  } catch (IOException mue) {
   System.err.println("Unable to load file: " + filename);
  }
 }

 public void saveFile(String filename) {
  try {
   File file = new File(filename);
   FileWriter writer = new FileWriter(file);
   writer.write(getText());
   writer.close();
   setFilename(file.getName());
  } catch (IOException ioe) {
   System.err.println("Unable to save file: " + filename);
  }
 }

 /** Return the name of the file loaded into the editor pane. */

 public String getFilename() {
  return(filename);
 }

 /** Set the filename of the document. */

 public void setFilename(String filename) {
  this.filename = filename;
 }

 /** Return true if a document is loaded into the editor
  * page, either through <code>setPage</code> or
  * <code>setText</code>.
  */

 public boolean isLoaded() {
  return(loaded);
 }
}

The results for the XsltExample are shown in Figures 1 through 3. Specifically, the result for the XML document pane with the loaded file perennials.xml (Listing 4) is shown in Figure 1. The XSL document pane with the loaded file perennials.xsl (Listing 5) is shown in Figure 2. Finally, the XSLT transformation of the XML document is presented in Figure 3. For this example, all daylilies awarded a Stout Medal are selected from the XML file and listed in an HTML table. For each daylily matching the criteria, the year of hybridization, cultivar name, bloom season, and cost are presented in the table.

Note that in the XSL file, if you include a doctype-public attribute for the xsl:output element, Xalan will include a DOCTYPE statement for the first line of the output document.

Listing 4—perennials.xml. XML document containing data about various award-winning daylilies.

<?xml version="1.0" standalone="yes" ?>
<!DOCTYPE DOCUMENT [
<!ELEMENT perennials (daylily)*>
<!ELEMENT daylily (cultivar, award*, bloom, cost)+>
<!ATTLIST daylily
  status (in-stock | limited | sold-out) #REQUIRED>
<!ELEMENT cultivar (#PCDATA)>
<!ELEMENT award (name, year)>
<!ELEMENT name (#PCDATA)>
<!ATTLIST name note CDATA #IMPLIED>
<!ELEMENT year (#PCDATA)>
<!ELEMENT bloom (#PCDATA)>
<!ATTLIST bloom code (E | EM | M | ML | L | E-L) #REQUIRED>
<!ELEMENT cost (#PCDATA)>
<!ATTLIST cost discount CDATA #IMPLIED>
<!ATTLIST cost currency (US | UK | CAN) "US">
]>
<perennials>
 <daylily status="in-stock">
  <cultivar>Luxury Lace</cultivar>
  <award>
   <name>Stout Medal</name>
   <year>1965</year>
  </award>
  <award>
   <name note="small-flowered">Annie T. Giles</name>
   <year>1965</year>
  </award>
  <award>
   <name>Lenington All-American</name>
   <year>1970</year>
  </award>
  <bloom code="M">Midseason</bloom>
  <cost discount="3" currency="US">11.75</cost>
 </daylily>
 <daylily status="in-stock">
  <cultivar>Green Flutter</cultivar>
  <award>
   <name>Stout Medal</name>
   <year>1976</year>
  </award>
  <award>
   <name note="small-flowered">Annie T. Giles</name>
   <year>1970</year>
  </award>
  <bloom code="M">Midseason</bloom>
  <cost discount="3+" currency="US">7.50</cost>
 </daylily>
 <daylily status="sold-out">
  <cultivar>My Belle</cultivar>
  <award>
   <name>Stout Medal</name>
   <year>1984</year>
  </award>
  <bloom code="E">Early</bloom>
  <cost currency="US">12.00</cost>
 </daylily>
 <daylily status="in-stock">
  <cultivar>Stella De Oro</cultivar>
  <award>
   <name>Stout Medal</name>
   <year>1985</year>
  </award>
  <award>
   <name note="miniature">Donn Fischer Memorial Cup</name>
   <year>1979</year>
  </award>
  <bloom code="E-L">Early to Late</bloom>
  <cost discount="10+" currency="US">5.00</cost>
 </daylily>
 <daylily status="limited">
  <cultivar>Brocaded Gown</cultivar>
  <award>
   <name>Stout Medal</name>
   <year>1989</year>
  </award>
  <bloom code="E">Early</bloom>
  <cost currency="US" discount="3+">14.50</cost>
 </daylily>
</perennials>

Listing 5—perennials.xsl. Style sheet to create an HTML table, selecting only those daylilies that have won the Stout Medal award.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html"
  doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN"/>
 <xsl:template match="/">
 <HTML>
 <HEAD>
  <TITLE>Daylilies</TITLE>
 </HEAD>
 <BODY>
  <TABLE CELLPADDING="3">
   <CAPTION>Stout Medal Award</CAPTION>
   <TR>
    <TH>Year</TH>
    <TH>Cultivar</TH>
    <TH>Bloom Season</TH>
    <TH>Cost</TH>
   </TR>
   <!-- Select daylilies awarded a Stout Medal. -->
   <xsl:apply-templates
     select="/perennials/daylily[award/name='Stout Medal']"/>
   <TR>
    <TD COLSPAN="4" ALIGN="CENTER">
      E-early M-midseason L-late</TD>
   </TR>
  </TABLE>
 </BODY>
 </HTML>
 </xsl:template>
 <xsl:template match="daylily">
  <TR>
   <TD><xsl:value-of select="award/year"/></TD>
   <TD><xsl:value-of select="cultivar"/></TD>
   <!-- Select the bloom code. -->
   <TD ALIGN="CENTER"><xsl:value-of select="bloom/@code"/></TD>
   <TD ALIGN="RIGHT"><xsl:value-of select="cost"/></TD>
  </TR>
 </xsl:template>
</xsl:stylesheet>

Figure 1 Presentation XML tabbed pane in XsltExample (Listing 1) with perennials.xml (Listing 4) loaded.

Figure 2 Presentation XSL tabbed pane in XsltExample (Listing 1) with perennials.xsl (Listing 5) loaded.

Figure 3 Result of XSLT transformation of perennials.xml (Listing 4) and perennials.xsl (Listing 5).

Due to the manner in which the JEditorPane class loads files, the HTML file, XSLT-Instructions.html, must also be located in the same directory application. This file is initially loaded into the XSLT document, when the application is first started.

  • + Share This
  • 🔖 Save To Your Account