8.2 JEditorPane

     

If you need a more interactive, complete implementation of HTML 3.2, you can use a javax.swing.JEditorPane . This class provides an even more complete HTML 3.2 renderer that can handle frames , forms, hyperlinks , and parts of CSS Level 1. The JEditorPane class also supports plain text and basic RTF, though the emphasis in this book will be on using it to display HTML.

JEditorPane supports HTML in a fairly intuitive way. You simply feed its constructor a URL or a large string containing HTML, then display it like any other component. There are four constructors in this class:

 public JEditorPane( ) public JEditorPane(URL initialPage) throws IOException public JEditorPane(String url)  throws IOException public JEditorPane(String mimeType, String text) 

The noargs constructor simply creates a JEditorPane with no initial data. You can change this later with the setPage() or setText( ) methods :

 public void setPage(URL page) throws IOException public void setPage(String url) throws IOException public void setText(String html) 

Example 8-2 shows how to use this constructor to display a web page. JEditorPane is placed inside a JScrollPane to add scrollbars; JFrame provides a home for the JScrollPane . Figure 8-2 shows this program displaying the O'Reilly home page.

Example 8-2. Using a JEditorPane to display a web page
 import javax.swing.text.*; import javax.swing.*; import java.io.*; import java.awt.*; public class OReillyHomePage {   public static void main(String[] args) {               JEditorPane jep = new JEditorPane( );      jep.setEditable(false);               try {        jep.setPage("http://www.oreilly.com");      }      catch (IOException ex) {        jep.setContentType("text/html");        jep.setText("<html>Could not load http://www.oreilly.com </html>");      }              JScrollPane scrollPane = new JScrollPane(jep);           JFrame f = new JFrame("O'Reilly & Associates");      f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);      f.setContentPane(scrollPane);      f.setSize(512, 342);      f.show( );        } } 

Figure 8-2. The O'Reilly home page shown in a JEditorPane
figs/jnp3_0802.gif

Figure 8-2 shows how good (or bad) Swing really is at displaying HTML. On the whole, it correctly renders this page containing tables, images, links, colors, fonts, and more with almost no effort from the programmer. However, it has some trouble with table widths, and there are a number of artifacts I can't explain. Generally, the simpler and more basic the page, the better Swing renders it.

What is missing, though, is precisely what's not obvious from this static image: the activity. The links are blue and underlined , but clicking on one won't change the page that's displayed. JavaScript and applets will not run. Shockwave animations and QuickTime movies won't play. Password-protected web pages will be off-limits because there's no way to provide a username and password. You can add all this yourself, but it will require extra code to recognize the relevant parts of the HTML and behave accordingly . Different active content requires different levels of support. Supporting hypertext linking, for example, is fairly straightforward, as we'll explore later. Applets aren't that hard to add either, mostly requiring you to simply parse the HTML to find the tags and parameters and provide an instance of the AppletContext interface. Adding JavaScript is only a little harder, provided that someone has already written a JavaScript interpreter you can use. Fortunately, the Mozilla Project has written the Open Source Rhino (http://www.mozilla.org/ rhino /) JavaScript interpreter, which you can use in your own work. Apple's QuickTime for Java (http://www.apple.com/quicktime/qtjava/) makes QuickTime support almost a no-brainer on Mac and Windows. (A Unix version is sorely lacking, though.) I'm not going to discuss all (or even most) of these in this chapter or this book. Nonetheless, they're available if you need them.

The second JEditorPane constructor accepts a URL object as an argument. It connects to the specified URL, downloads the page it finds, and attempts to display it. If this attempt fails, an IOException is thrown. For example:

 JFrame f = new JFrame("O'Reilly & Associates");      try {   URL u = new URL("http://www.oreilly.com");   JEditorPane jep = new JEditorPane(u);   jep.setEditable(false);      JScrollPane scrollPane = new JScrollPane(jep);        f.setContentPane(scrollPane); } catch (IOException ex) {   f.getContentPane( ).add(    new Label("Could not load http://www.oreilly.com")); }         f.setSize(512, 342); f.show( ); 

The third JEditorPane constructor is almost identical to the second except that it takes a String form of the URL rather than a URL object as an argument. One of the IOExceptions it can throw is a MalformedURLException if it doesn't recognize the protocol. Otherwise, its behavior is the same as the second constructor. For example:

 try {   JEditorPane jep = new JEditorPane("http://www.oreilly.com");   jep.setEditable(false);      JScrollPane scrollPane = new JScrollPane(jep);        f.setContentPane(scrollPane);           } catch (IOException ex) {   f.getContentPane( ).add(    new Label("Could not load http://www.oreilly.com")); } 

Neither of these constructors requires you to call setText( ) or setPage( ) , since that information is provided in the constructor. However, you can still call these methods to change the page or text that's displayed.

8.2.1 Constructing HTML User Interfaces on the Fly

The fourth JEditorPane constructor does not connect to a URL. Rather, it gets its data directly from the second argument. The MIME type of the data is determined by the first argument. For example:

 JEditorPane jep = new JEditorPane("text/html",   "<html><h1>Hello World!</h1> <h2>Goodbye World!</h2></html>"); 

This may be useful when you want to display HTML created programmatically or read from somewhere other than a URL. Example 8-3 shows a program that calculates the first 50 Fibonacci numbers and then displays them in an HTML ordered list. Figure 8-3 shows the output.

Example 8-3. Fibonacci sequence displayed in HTML
 import javax.swing.text.*; import javax.swing.*; import java.net.*; import java.io.*; import java.awt.*; public class Fibonacci {  public static void main(String[] args) {               StringBuffer result =        new StringBuffer("<html><body><h1>Fibonacci Sequence</h1><ol>");               long f1 = 0;      long f2 = 1;               for (int i = 0; i < 50; i++) {        result.append("<li>");        result.append(f1);        long temp = f2;        f2 = f1 + f2;        f1 = temp;      }                 result.append("</ol></body></html>");            JEditorPane jep = new JEditorPane("text/html", result.toString( ));      jep.setEditable(false);                 JScrollPane scrollPane = new JScrollPane(jep);           JFrame f = new JFrame("Fibonacci Sequence");      f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);      f.setContentPane(scrollPane);      f.setSize(512, 342);      EventQueue.invokeLater(new FrameShower(f));        }      // This inner class avoids a really obscure race condition.   // See http://java.sun.com/developer/JDCTechTips/2003/tt1208.html#1   private static class FrameShower implements Runnable {        private final Frame frame;          FrameShower(Frame frame) {       this.frame = frame;     }          public void run( ) {      frame.setVisible(true);     }        } } 

Figure 8-3. The Fibonacci sequence displayed as HTML using a JEditorPane
figs/jnp3_0803.gif

The significance of this should be apparent. Your programs now have access to a very powerful styled text engine. That the format used on the backend is HTML is a nice fringe benefit. It means you can use a familiar, easy-to-write format to create a user interface that uses styled text. You don't have quite all the power of QuarkXPress here (nor should you, since HTML doesn't have it), but this is more than adequate for 99% of text display needs, whether those needs are simple program output, help files, database reports , or something more complex.

8.2.2 Handling Hyperlinks

When the user clicks on a link in a noneditable JEditorPane , the pane fires a HyperlinkEvent . As you might guess, this is responded to by any registered HyperlinkListener objects. This follows the same variation of the Observer design pattern used for AWT events and JavaBeans. The javax.swing.event.HyperlinkListener interface defines a single method, hyperlinkUpdate( ) :

 public void hyperlinkUpdate(HyperlinkEvent evt) 

Inside this method, you'll place the code that responds to the HyperlinkEvent . The HyperlinkEvent object passed to this method contains the URL of the event, which is returned by its getURL( ) method:

 public URL getURL( ) 

HyperlinkEvent s are fired not just when the user clicks the link, but also when the mouse enters or exits the link area. Thus, you'll want to check the type of the event before changing the page with the getEventType() method:

 public HyperlinkEvent.EventType getEventType( ) 

This will return one of the three mnemonic constants HyperlinkEvent.EventType.EXITED , HyperlinkEvent.EventTypeENTERED , or HyperlinkEvent.EventType.ACTIVATED . These are not numbers but static instances of the EventType inner class in the HyperlinkEvent class. Using these instead of integer constants allows for more careful compile-time type checking.

Example 8-4 is an implementation of the HyperLinkListener interface that checks the event fired and, if it's an activated event, switches to the page in the link. A reference to the JEditorPane is stored in a private field in the class so that a callback to make the switch can be made.

Example 8-4. A basic HyperlinkListener class
 import javax.swing.*; import javax.swing.event.*; public class LinkFollower implements HyperlinkListener {   private JEditorPane pane;      public LinkFollower(JEditorPane pane) {     this.pane = pane;   }   public void hyperlinkUpdate(HyperlinkEvent evt) {          if (evt.getEventType( ) == HyperlinkEvent.EventType.ACTIVATED) {       try {         pane.setPage(evt.getURL( ));               }       catch (Exception ex) {         pane.setText("<html>Could not load " + evt.getURL( ) + "</html>");       }      }        } } 

Example 8-5 is a very simple web browser. It registers an instance of the LinkFollower class of Example 8-4 to handle any HyperlinkEvent s. It doesn't have a Back button, a Location bar, bookmarks, or any frills at all. But it does let you surf the Web by following links. The remaining aspects of the user interface you'd want in a real browser are mostly just exercises in GUI programming, so I'll omit them. But it really is amazing just how easy Swing makes it to write a web browser.

Example 8-5. SimpleWebBrowser
 import javax.swing.text.*; import javax.swing.*; import java.net.*; import java.io.*; import java.awt.*; public class SimpleWebBrowser {   public static void main(String[] args) {              // get the first URL     String initialPage = "http://www.cafeaulait.org/";     if (args.length > 0) initialPage = args[0];                 // set up the editor pane     JEditorPane jep = new JEditorPane( );     jep.setEditable(false);        jep.addHyperlinkListener(new LinkFollower(jep));          try {       jep.setPage(initialPage);           }     catch (IOException ex) {       System.err.println("Usage: java SimpleWebBrowser url");        System.err.println(ex);       System.exit(-1);     }            // set up the window     JScrollPane scrollPane = new JScrollPane(jep);          JFrame f = new JFrame("Simple Web Browser");     f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);     f.setContentPane(scrollPane);     f.setSize(512, 342);     EventQueue.invokeLater(new FrameShower(f));        }      // Helps avoid a really obscure deadlock condition.   // See http://java.sun.com/developer/JDCTechTips/2003/tt1208.html#1   private static class FrameShower implements Runnable {        private final Frame frame;          FrameShower(Frame frame) {       this.frame = frame;     }          public void run( ) {      frame.setVisible(true);     }        } } 

The one thing this browser doesn't do is follow links to named anchors inside the body of a particular HTML page. There is a protected scrollToReference( ) method in JEditorPane that can find the specified named anchor in the currently displayed HTML document and reposition the pane at that point; you can use this method to add this functionality if you so desire :

 protected void scrollToReference(String reference) 

8.2.3 Reading HTML Directly

The JEditorPane class mostly assumes that you're going to provide either a URL or the string form of a URL and let it handle all the details of retrieving the data from the network. However, it contains one method that allows you to read HTML directly from an input stream. That method is read( ) :

 public void read(InputStream in, Object document) throws IOException 

This method may be useful if you need to read HTML from a chain of filter streams; for instance, unzipping it before you read it. It could also be used when you need to perform some custom handshaking with the server, such as providing a username and password, rather than simply letting the default connection take place.

The first argument is the stream from which the HTML will be read. The second argument should be an instance of javax.swing.text.html.HTMLDocument . (You can use another type, but if you do, the JEditorPane will treat the stream as plain text rather than HTML.) Although you could use the HTMLDocument( ) noargs constructor to create the HTMLDocument object, the document it creates is missing a lot of style details. You're better off letting a javax.swing.text.html.HTMLEditorKit create the document for you. You get an HTMLEditorKit by passing the MIME type you want to edit (text/html in this case) to the JEditorPane getEditorKitForContentType( ) method like this:

 EditorKit htmlKit = jep.getEditorKitForContentType("text/html"); 

Finally, before reading from the stream, you have to use the JEditorPane 's setEditorKit( ) method to install a javax.swing.text.html.HTMLEditorKit . For example:

 jep.setEditorKit(htmlKit); 

This code fragment uses these techniques to load the web page at http://www.elharo.com. Here the stream comes from a URL anyway, so this is really more trouble than it's worth compared to the alternative. However, this approach would also allow you to read from a gzipped file, a file on the local drive, data written by another thread, a byte array, or anything else you can hook a stream to:

 JEditorPane jep = new JEditorPane( ); jep.setEditable(false);    EditorKit htmlKit = jep.getEditorKitForContentType("text/html"); HTMLDocument doc = (HTMLDocument) htmlKit.createDefaultDocument( ); jep.setEditorKit(htmlKit);       try {   URL u = new URL("http://www.elharo.com");   InputStream in = u.openStream( );   jep.read(in, doc); } catch (IOException ex) {   System.err.println(ex); }         JScrollPane scrollPane = new JScrollPane(jep);      JFrame f = new JFrame("Macfaq"); f.setContentPane(scrollPane); f.setSize(512, 342); EventQueue.invokeLater(new FrameShower(f)); 

This would also be useful if you need to interpose your program in the stream to perform some sort of filtering. For example, you might want to remove IMG tags from the file before displaying it. The methods of the next section can help you do this.



Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net