Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for May 5, 2005. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). This issue covers: * Communicating With Native Applications Using JDIC * The Enhanced For Loop These tips were developed using the Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp. This issue of the Core Java Technologies Tech Tips is written by Daniel H. Steinberg, Director of Java Offerings for Dim Sum Thinking, Inc, and editor-in-chief for java.net (http://java.net). You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/JDCTechTips/2005/tt0505.html. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. For more Java technology content, visit these sites: java.sun.com - The Java technology source for developers. Get the latest Java platform releases, tutorials, newsletters and more. java.net - A web forum where enthusiasts of Java technology can collaborate and build solutions together. java.com - The ultimate marketplace promoting Java technology, applications and services. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - COMMUNICATING WITH NATIVE APPLICATIONS USING JDIC JDesktop Integration Components (JDIC) (https://jdic.dev.java.net) enable Java applications to integrate into the native desktop. This allows these applications to take advantage of functionality provided by operating system-specific programs such as web browsers or email tools. JDIC is currently supported in the Solaris 8 (or later) Operating System, the Sun Java Desktop System (JDS) Release 1 or later, various Windows operating systems (ME, NT, XP, 2003, and 2000), SuSE Linux 7.1 or later, and RedHat Linux 8 or later. Support for Mac OS X is being added for future releases. In this tip you will load a web page using the JEditorPane and consider some of the limitations of this approach. You will then use two different features of JDIC to view the web page (as part of a JFrame) in your existing web browser. You can display HTML in many Swing components. For example, you can use a JEditorPane to display HTML like this: private void loadStartingPage() { JEditorPane editor = new JEditorPane(); editor.setEditable(false); try { editor.setPage("http://www.java.net/"); } catch (IOException e) { System.err.println("can't connect"); } } If the URL is reachable, the resulting page should be displayed in the JEditorPane. Of course, you will have to provide a JFrame that contains a JScrollPane that, in turn, contains the JEditorPane. For thread safety, you should also create the GUI by scheduling a job on the event-dispatching thread. All of this is shown in the following class: import javax.swing.JScrollPane; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.SwingUtilities; import java.io.IOException; import java.awt.Dimension; public class EditorPaneHTMLViewer extends JEditorPane { private JScrollPane createScrollPane() { JScrollPane editorScrollPane = new JScrollPane(this); editorScrollPane.setPreferredSize( new Dimension(700, 500)); return editorScrollPane; } private void loadStartingPage() { setEditable(false); try { setPage("http://www.java.net/"); } catch (IOException e) { System.err.println("can't connect"); } } private void createAndShowGUI() { JFrame frame = new JFrame("EditorPaneHTMLViewer"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JScrollPane content = createScrollPane(); loadStartingPage(); frame.add(content); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: SwingUtilities.invokeLater(new Runnable() { public void run() { (new EditorPaneHTMLViewer()).createAndShowGUI(); } }); } } The good news is that when you compile and run this program, the resulting HTML is displayed in the JEditorPane. But you can also see that the current level of HTML support is not sufficient to properly display many modern standards-compliant pages. It's unfortunate that even with a perfectly good web browser installed, you are not be able to properly view web pages in a Swing component such as a JEditorPane. However, JDIC gives you a way to do this. You open your default web browser to a specified page by calling the browse() method in one of the JDIC packages, org.jdesktop.jdic.desktop.Desktop. When you call the browse() method, you need to pass in a java.net.URL object, as shown in the following example: import org.jdesktop.jdic.desktop.Desktop; import org.jdesktop.jdic.desktop.DesktopException; import java.net.URL; import java.net.MalformedURLException; public class OpenWithRegisteredApp { public static void main(String[] args) { try { Desktop.browse(new URL("http://www.java.net/")); } catch (MalformedURLException e){ System.err.println("couldn't connect"); e.printStackTrace(); } catch (DesktopException e){ e.printStackTrace(); } } } To compile the example program, you need to include the jdic.jar file in your class path. To run the program, you need the native libraries included in a place where the JVM can find it. Alternatively, you can point to the jar file and the native libraries at runtime by using -Djava.library.path= followed by the path to the directory that contains the native libraries. For example, on a Windows machine, if you copy the jar file and dll files into the same directory as the one that contains your program, you can use the following command to run the program: java -classpath jdic.jar;. -Djava.library.path=. OpenWithRegisteredApp Note that the command is shown on two lines for formatting purposes. You should enter the command on one line. As a result, your default browser should open and load the front page of java.net. The JDIC APIs also provide support for other operations you would want to perform to access and interact with native desktop applications. You can launch an editor to edit a specified file. You can launch a window to compose a new message in the default mailer, optionally filling in some of the fields in the mail message. You can launch the registered application for a given file or print the contents of a file. The details of using these methods are similar to the way in which browse() was used in the OpenWithRegisteredApp example. There are times, however, when you might prefer to display a web page from inside your Java application. Instead of bringing up an external web browser, you might want to show the web content in your JFrame without having to revert to the primitive look of the JEditorPane. Another JDIC component, the org.jdesktop.jdic.browser.WebBrowser class, was created for that reason. WebBrowser extends java.awt.Canvas and can be added directly to your JFrame. Here is an example that uses the WebBrowser class. import org.jdesktop.jdic.browser.WebBrowser; import javax.swing.JFrame; import javax.swing.SwingUtilities; import java.net.URL; import java.net.MalformedURLException; import java.awt.Dimension; public class JDICBrowser { private WebBrowser webBrowser = new WebBrowser(); private void loadStartingPage() { try { webBrowser.setURL(new URL("http://www.java.net")); } catch (MalformedURLException e) { System.out.println(e.getMessage()); } } private void createAndShowGUI() { JFrame frame = new JFrame("JDIC Browser"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setPreferredSize(new Dimension(700,500)); loadStartingPage(); frame.add(webBrowser); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: SwingUtilities.invokeLater(new Runnable() { public void run() { (new JDICBrowser()).createAndShowGUI(); } }); } } Although this code looks a lot like the first version, the result is a properly rendered view of the web page. You can add a text field and buttons to either this version or the first version. This allows the user to enter the URL of other pages and to navigate through the history. You can also take advantage of callbacks to provide a better user experience by implementing the WebBrowserListener class. Here, for example, is an adapter class that implements two of the methods. import org.jdesktop.jdic.browser.WebBrowserListener; import org.jdesktop.jdic.browser.WebBrowserEvent; public class WebBrowserAdapter implements WebBrowserListener { public void downloadStarted(WebBrowserEvent e){ System.out.println("Download Started"); } public void downloadCompleted(WebBrowserEvent e){ System.out.println("Download Completed"); } public void downloadProgress(WebBrowserEvent e){} public void downloadError(WebBrowserEvent e){} public void documentCompleted(WebBrowserEvent e){} public void titleChange(WebBrowserEvent e){} public void statusTextChange(WebBrowserEvent e){} } You can add this WebBrowserListener by modifying the createAndShowGUI() method as follows: //... loadStartingPage(); // add the following line: webBrowser.addWebBrowserListener(new WebBrowserAdapter()); ... When you rerun JDICBrowser you will see the feedback in standard out as downloads begin and complete. C:\techtips\May05>java JDICBrowser Download Started Download Completed Download Started Download Started Download Completed The point of this tip is not to demonstrate how to build a better browser. Instead the intent is to show you how to easily integrate native components using the classes in the JDIC open source project. More features are being added, and the project owners would welcome your contributions. You can learn more about JDIC on the JDIC project page (https://jdic.dev.java.net). Also see George Zhang's blog entry on this topic (http://weblogs.java.net/blog/georgez/archive/2005/03/wheres_jdic_goi.html). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - THE ENHANCED FOR LOOP Introduced as a new language feature in J2SE 5.0, the enhanced for loop allows you to iterate through a collection without having to create an Iterator or without having to calculate beginning and end conditions for a counter variable. The enhanced for loop is the easiest of the new features to immediately incorporate in your code. In this tip you will see how the enhanced for loop replaces more traditional ways of sequentially accessing elements in a collection. So what does an enhanced for loop look like? Suppose you have a collection of TechTip objects called RecentTips. You could use an enhanced for loop with the collection as follows: for (TechTip tip: RecentTips) You read this as "for each TechTip in RecentTips". Here the variable tip is used to point to the current instance of TechTip in the collection. Because of the "for each" wording, the enhanced-for construct is also referred to as the for-each construct. If you compare the enhanced for loop to the typical way of iterating over a collection, it's clear that the for each loop is simpler and can make your code more readable. Also note that the enhanced for loop is designed to simplify your work with generics. Although this tip does include two examples of using the enhanced for loop with generics, it's not the focus of the tip. Instead the objective of this tip is to introduces you to the more basic changes you can make to your code to use the for each loop. First, consider how you might use a for loop to iterate through the elements of an Array. For simplicity, load an array with six ints that represent the squares of the ints from zero to five. Here's a for loop that does the iteration: for (int i=0; i< squares.length; i++) The line illustrates the classic use of the for loop. It specifies the initial value of one or more counters, sets up a terminating condition, and describes how the counters might be incremented. Here is a short program, OldForArray, that uses the for loop. public class OldForArray { public static void main(String[] args){ int[] squares = {0,1,4,9,16,25}; for (int i=0; i< squares.length; i++){ System.out.printf("%d squared is %d.\n",i, squares[i]); } } } Compile and run the OldForArray program, and you get the following: 0 squared is 0. 1 squared is 1. 2 squared is 4. 3 squared is 9. 4 squared is 16. 5 squared is 25. If you change the test program to use the enhanced for loop, you specify the relevant variable and the collection that the variable comes from. Here is a line that's an enhanced for loop: for (int i : squares) You can read the line as "iterate on elements from the collection named squares." The current element will be referenced by the int i." You don't need to determine how many elements are in the array before looping. There is also no need to specify how to increment the current position. "Under the covers," the enhanced for loop for an array is equivalent to the for loop presented earlier. The test program NewForArray gives the same result as OldForArray. public class NewForArray { public static void main(String[] args) { int j = 0; int[] squares = {0, 1, 4, 9, 16, 25}; for (int i : squares) { System.out.printf("%d squared is %d.\n", j++, i); } } } An array is an indexed collection of elements of a single type that is specified when the array is declared. With more general collections such as ArrayLists, the elements are stored as Objects. You could use the C style for loop to iterate through a collection like this: for (int i = 0; i < list.size(); i++) You then can use list.get(i) to reference the current element. For example, the following program, OldForArrayList, uses autoboxing to fill the ArrayList and to then read from the ArrayList: import java.util.ArrayList; import java.util.List; public class OldForArrayList { private static List squares = new ArrayList(); private static void fillList() { for (int i = 0; i < 6; i++) { squares.add(i * i); } } private static void outputList() { for (int i = 0; i < squares.size(); i++) { System.out.printf("%d squared is %d.\n", i, squares.get(i)); } } public static void main(String args[]) { fillList(); outputList(); } } However, because ArrayList is part of the collections framework, it is more common to iterate through it using an Iterator in the following pattern: while( iterator.hasNext()) { doSomethingWith (iterator.next()); } You can bundle this in a for loop as shown in the following program, IteratorForArrayList: import java.util.ArrayList; import java.util.List; import java.util.Iterator; public class IteratorForArrayList { private static List squares = new ArrayList(); private static void fillList() { for (int i = 0; i < 6; i++) { squares.add(i * i); } } private static void outputList() { Iterator iterator = squares.iterator(); int j=0; for (; iterator.hasNext();) { System.out.printf("%d squared is %d.\n", j++, iterator.next()); } } public static void main(String args[]) { fillList(); outputList(); } } It looks a bit unusual to have a for loop with no first or third parameter. There is no initial condition, and the incrementing of the position in the List is performed in the body of the for loop with the call iterator.next(). The enhanced for loop makes the explicit use of an iterator unnecessary. Rather than create an Iterator object for the ArrayList and then use the iterator in the for loop, you use the following: for ( Integer square : squares) This indicates that the name of the collection is squares. It also indicates that the currently referenced item is of type Integer and is referenced by the variable square. This code will not compile because there is no way of knowing that the contents of the ArrayList is of type Integer. To fix this, you need to use another feature introduced in J2SE 5.0, namely generics. You need to specify in the declaration and definition of squares that it can only hold elements of type Integer. You do this as follows: private static List squares = new ArrayList(); The following program, NewArrayList, shows how to use the enhanced for loop together with generics: import java.util.List; import java.util.ArrayList; public class NewArrayList { private static List squares = new ArrayList(); private static void fillList() { for (int i = 0; i < 6; i++) { squares.add(i * i); } } private static void outputList() { int j=0; for (Integer square : squares) { System.out.printf("%d squared is %d.\n", j++, square); } } public static void main(String args[]) { fillList(); outputList(); } } This NewArrayList example is a bit simplistic, but it demonstrates the syntactic differences between using the classic for loop and the enhanced for loop. Here is another example that compares the syntactic differences between the loops. The example is excerpted from a talk given by Joshua Bloch and Neil Gafter at the 2004 JavaOne Conference. In the example, a method is applied to each element in a collection. To start, the example uses an Iterator like this: void cancelAll (Collection c) { for (Iterator i = c.iterator(); i.hasNext(); ) { TimerTask tt = (TimerTask) i.next(); tt.cancel(); } } Next, an enhanced for loop is introduced to eliminate the use of the Iterator: void cancelAll( Collection c ) { for (Object o : c) ( (TimerTask) o). cancel(); } There is still a matter of having to treat the elements of the collection as being of type Object and then casting them to type TimerTask. This is fixed by introducing generics like this: void cancelAll( Collection c ) { for (TimerTask task : c) task.cancel(); } It's important to note that the enhanced for loop can't be used everywhere. You can't use the enhanced for loop: o To remove elements as you traverse collections o To modify the current slot in an array or list o To iterate over multiple collections or arrays However aside from these cases, you should try to use the enhanced for loop code to simplify your code. As with anything new, the syntax for the enhanced for loop might seem unfamiliar and difficult to read. You have probably used the C style for loop for many years and possibly in more than one language. It is, however, cleaner not having to create a counter variable or an Iterator. You also do not need to worry about where your collection begins and ends to set up the initial value and loop termination conditions. For more information on the enhanced for loop, see "The For-Each Loop" (http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TRY THE NEW SEARCH FACILITY Sun is working on a new search facility for its developer sites. The search facility is currently available as a beta release. Try it out and provide your feedback. You can access the search facility at http://onesearch.sun.com/search/onesearch/index.jsp?col=developer-all&qt=java . . . . . . . . . . . . . . . . . . . . . . . PRIVACY STATEMENT: Sun respects your online time and privacy (http://sun.com/privacy). You have received this based on your e-mail preferences. If you would prefer not to receive this information, please follow the steps at the bottom of this message to unsubscribe. Please read our Terms of Use and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other SDN publications: - Go to the Sun Developer Network - Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Submit". - To unsubscribe, go to the Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Submit". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/TechTips/index.html - COPYRIGHT Copyright 2005 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html Core Java Technologies Tech Tips May 5, 2005 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.