Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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

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/tt1208l#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


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( )

HyperlinkEvents 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
HyperlinkEvents. 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/tt1208l#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.textl.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.textl.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.textl.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.


/ 164