// Copyright MageLang Institute; Version $Id: //depot/main/src/edu/modules/Beans/magercises/UrlHexDump/Solution/urlhexdump/UrlHexDump.java#3 $ /**** UrlHexDump.java ****/ // // UrlHexDump's main() opens a URL as an input stream and dumps an // ASCII interpretation of the data, similiar to an assembly-style // hex dump, to standard output. // // The UrlHexDump Bean can also display the text in an application // frame; see the showGraphically property. // package urlhexdump; import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; public class UrlHexDump implements Serializable { private static final String SEPARATOR_STR = "\n"; private static final String BAD_URL_MSG = "Can't open URL."; private static final int ROWS = 30; private static final int COLUMNS = 80; private static final int SEG_SIZE = 16; private String urlStr = ""; private String urlContent = ""; private String separatorStr = SEPARATOR_STR; private String message = ""; private int rows = ROWS; private int columns = COLUMNS; private boolean debug = false; private boolean submitted = false; private boolean showGraphically = false; private URL url; // // The next three variables are required for stream processing. // Also, 'eof' and 'offset' are instance-level because the // participate in the 'toString()' functionality. // private boolean eof; private int offset; private int segment[] = new int[SEG_SIZE]; public UrlHexDump() { } public UrlHexDump(String urlStr) { this.urlStr = urlStr; openUrl(); } public static void main(String args[]) { if (usageOnly(args)) System.exit(0); UrlHexDump url = new UrlHexDump(args[0]); if (url == null) System.exit(-1); // System.out.println(url.retrieveUrlContent()); url.retrieveUrlContentGraphicalApplication(); } private static boolean usageOnly(String args[]) { if (args.length == 0 || args.length > 1 || ((args.length == 1) && (args[0].equalsIgnoreCase("-usage") || args[0].equalsIgnoreCase("-help") || args[0].equalsIgnoreCase("-h")))) { System.out.println(""); System.out.println( "UrlHexDump opens a URL as an input stream and dumps an ASCII"); System.out.println( "interpretation of the data stream to standard output."); System.out.println(""); System.out.println("Usage: java UrlHexDump "); System.out.println(""); return true; } else return false; } private void openUrl() { url = null; try { url = new URL(urlStr); } catch (MalformedURLException e) { handleMessage("UrlHexDump: bad URL: " + urlStr, e); url = null; } catch (Exception e) { handleMessage("UrlHexDump: general exception opening URL.", e); url = null; } } private void handleMessage(String msg, Exception e) { if (debug) { System.out.println(msg); System.out.println(e); } message = msg; } // // retrieveUrlContent() opens a data stream to the URL, reads fixed- // size data segments, and stores the result locally. // synchronized public String retrieveUrlContent() { if (url == null) { openUrl(); if (url == null) return BAD_URL_MSG; } DataInputStream in = null; urlContent = ""; eof = false; offset = 0; try { in = new DataInputStream(url.openStream()); try { while (!eof) { readSegment(in); urlContent += formatSegment(); } } catch (Exception e) { handleMessage("retrieveUrlContent: unknown general exception.", e); urlContent += "retrieveUrlContent: unknown general exception:" + separatorStr + e; } } catch (Exception e) { handleMessage( "retrieveUrlContent: general exception opening data stream.", e); urlContent += "retrieveUrlContent: general exception opening data stream:" + separatorStr + e; } if (showGraphically) new UrlHexDumpFrame(urlContent, rows, columns, false); return urlContent; } synchronized public void retrieveUrlContentGraphical() { new UrlHexDumpFrame(retrieveUrlContent(), rows, columns, false); } synchronized public void retrieveUrlContentGraphicalApplication() { new UrlHexDumpFrame(retrieveUrlContent(), rows, columns, true); } public String toString() { return "[" + "URL string = " + urlStr + ", eof = " + eof + ", offset = " + offset + "]"; } // // properties: // public String getMessage() { return message; } public void setMessage(String message) { // do nothing -- read-only property } public boolean isSubmitted() { return submitted; } synchronized public void setSubmitted(boolean state) { if (!state) { if (submitted) setUrl(""); } else { openUrl(); retrieveUrlContent(); } } public String getUrl() { return urlStr; } synchronized public void setUrl(String str) { urlStr = str; if (str == null || str.length() == 0) url = null; else openUrl(); } public String getSeparatorStr() { return separatorStr; } synchronized public void setSeparatorStr(String str) { separatorStr = str; } public int getGraphicalRows() { return rows; } synchronized public void setGraphicalRows(int rows) { this.rows = rows; } public int getGraphicalColumns() { return columns; } synchronized public void setGraphicalColumns(int columns) { this.columns = columns; } public boolean getShowGraphically() { return showGraphically; } synchronized public void setShowGraphically(boolean state) { showGraphically = state; } public boolean getDebug() { return debug; } synchronized public void setDebug(boolean state) { debug = state; } //////////////////////////////////////////////////////////////////////// // Private, you-don't-want-to-know stuff follows //////////////////////////////////////////////////////////////////////// // // readSegment() reads the data stream as unsigned bytes. This strategy // is NOT 100-percent reliable, because servers don't always close the // connection. // private void readSegment(DataInputStream in) { int i = 0; try { for ( ; i < SEG_SIZE; i++) segment[i] = in.readUnsignedByte(); } catch (EOFException e) { eof = true; for (int j = i; i < SEG_SIZE; i++) segment[i] = 0; } catch (IOException e) { handleMessage("I/O error reading from URL: " + urlStr, e); } } // // formatSegment() builds each fixed segment of the data stream, // one per line, e.g.: // // 00000 54 68 ... 73 20--74 65 ... 30 31 |This is -text..01| // 00010 32 33 ... 38 39--0a 01 ... 64 0a |23456789-....end.| // 00020 00 00 ... 00 00--00 00 ... 00 00 |........-........| // private String formatSegment() { int i; String str = ""; // // build the offset in the data stream: // str += rightJustify(Integer.toHexString(offset * 16), 5) + " "; // // build the data as hex values: // for (i = 0; i < (SEG_SIZE / 2) - 1; i++) str += rightJustify(Integer.toHexString(segment[i]), 2) + " "; str += rightJustify(Integer.toHexString(segment[i++]), 2); str += "--"; for ( ; i < SEG_SIZE; i++) str += rightJustify(Integer.toHexString(segment[i]), 2) + " "; // // build the data as a (forced) ASCII interpretation: // str += " |"; for (i = 0; i < (SEG_SIZE / 2); i++) { char c = (char) segment[i]; if (!isPrintableAscii(c)) c = '.'; str += c; } urlContent += "-"; for ( ; i < SEG_SIZE; i++) { char c = (char) segment[i]; if (!isPrintableAscii(c)) c = '.'; str += c; } str += "|" + separatorStr; offset++; return str; } private void printSegment() { System.out.print(formatSegment()); } // // isPrintableAscii() indicates the type of data NOT to print. // private boolean isPrintableAscii(char c) { return !(Character.isISOControl(c)); } // // rightJustify() right justifies hex data in a 0-padded field // of arbitrary size. // private String rightJustify(String str, int width) { for (int i = width - str.length(); i > 0; i--) str = "0" + str; return str; } } class UrlHexDumpFrame extends Frame implements ActionListener, WindowListener { boolean closeApp = false; UrlHexDumpFrame(String urlContent, int rows, int columns, boolean closeApp) { super("UrlHexDump"); this.closeApp = closeApp; setLayout(new BorderLayout()); addWindowListener(this); Panel buttonPanel = new Panel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); Button dismiss = new Button("Dismiss"); buttonPanel.add(dismiss); dismiss.addActionListener(this); add(buttonPanel, BorderLayout.SOUTH); TextArea text = new TextArea(urlContent, rows, columns); text.setFont(new Font("Monospaced", Font.PLAIN, 12)); add(text, BorderLayout.CENTER); pack(); setSize(getPreferredSize()); setVisible(true); } public void actionPerformed(ActionEvent e) { setVisible(false); dispose(); if (closeApp) System.exit(0); } public void windowClosing(WindowEvent e) { setVisible(false); dispose(); if (closeApp) System.exit(0); } public void windowClosed(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } }