H U F F M A N Algorithm

  • 1,441 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to like this
No Downloads

Views

Total Views
1,441
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
29
Comments
1
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. BitInputStream.java import java.io.*; /** * Reads bits-at-a-time where the number of bits is between 1 and 32. * Updated for version 2.0 to extend java.io.InputStream. This class * can be used together with <code>BitOutputStream</code> to facilitate * reading and writing data several bits-at-a-time. BitInputStream objects * that are constructed from a File support <code>reset()</code>. However, * if constructed from an <code>InputStream</code> an object cannot be reset. * <P> * Any exceptions generated are rethrown as <code>RuntimeException</code> objects * so client code does not have to catch or rethrow them. (Unless the extension * of <code>InputStream</code> requires throwing as another type of exception, e.g., * as with method <code>read</code>. * <P> * @author Owen Astrachan * @version 1.0, July 2000 * @version 2.0, October 2004 */ public class BitInputStream extends InputStream { private InputStream myInput; private int myBitCount; private int myBuffer; private File myFile; private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff, 0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff, 0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff }; private static final int BITS_PER_BYTE = 8; /** * Construct a bit-at-a-time input stream from a file whose * name is supplied. * @param filename is the name of the file that will be read. * @throws RuntimeException if filename cannot be opened. */ public BitInputStream(String filename) { this(new File(filename)); } /** * Construct a bit-at-a-time input stream from <code>file</code>. * @param file is the File that is the source of the input * @throws RuntimeExceptoin if file cannot be opened. */ public BitInputStream(File file) { myFile = file;
  • 2. try { reset(); } catch (IOException e) { throw new RuntimeException("could not open file for reading bits "+e); } } /** * Open a bit-at-a-time stream that reads from supplied InputStream. If this * constructor is used the BitInputStream is not reset-able. * @param in is the stream from which bits are read. */ public BitInputStream(InputStream in){ myInput = in; myFile = null; } /** * Return true if the stream has been initialized from a File and * is thus reset-able. If constructed from an InputStream it is not reset-able. * @return true if stream can be reset (it has been constructed appropriately from a File). */ public boolean markSupported(){ return myFile != null; } /** * Reset stream to beginning. The implementation creates a new * stream. * @throws IOException if not reset-able (e.g., constructed from InputStream). */ public void reset() throws IOException { if (! markSupported()){ throw new IOException("not resettable"); } try{ close(); myInput = new BufferedInputStream(new FileInputStream(myFile)); } catch (FileNotFoundException fnf){ System.err.println("error opening " + myFile.getName() + " " + fnf); } myBuffer = myBitCount = 0; } /** * Closes the input stream. * @throws RuntimeException if the close fails */ public void close() { try{ if (myInput != null) {
  • 3. myInput.close(); } } catch (java.io.IOException ioe){ throw new RuntimeException("error closing bit stream " + ioe); } } /** * Returns the number of bits requested as rightmost bits in * returned value, returns -1 if not enough bits available to * satisfy the request. * * @param howManyBits is the number of bits to read and return * @return the value read, only rightmost <code>howManyBits</code> * are valid, returns -1 if not enough bits left */ public int readBits(int howManyBits) throws IOException { int retval = 0; if (myInput == null){ return -1; } while (howManyBits > myBitCount){ retval |= ( myBuffer << (howManyBits - myBitCount) ); howManyBits -= myBitCount; try{ if ( (myBuffer = myInput.read()) == -1) { return -1; } } catch (IOException ioe) { throw new IOException("bitreading trouble "+ioe); } myBitCount = BITS_PER_BYTE; } if (howManyBits > 0){ retval |= myBuffer >> (myBitCount - howManyBits); myBuffer &= bmask[myBitCount - howManyBits]; myBitCount -= howManyBits; } return retval; } /** * Required by classes extending InputStream, returns * the next byte from this stream as an int value. * @return the next byte from this stream */ public int read() throws IOException { return readBits(8); } }
  • 4. BitOutputStream.java /** * Write bits-at-a-time where the number of bits is between 1 and 32 * Client programs must call <code>flush</code> or * <code>close</code> when finished writing or not all bits will be written. * This class is intended to be used with <code>BitInputStream</code> to * facilitate reading and writing data in a bits-at-a-time manner. * <P> * Updated for version 2.0 to extend java.io.OutputStream * <P> * Any exceptions generated are rethrown as <code>RuntimeException</code> objects * so client code does not have to catch or rethrow them. * <P> * @author Owen Astrachan * @version 1.0, July 2000 * @version 2.0, October 2004 */ import java.io.*; public class BitOutputStream extends OutputStream { private OutputStream myOutput; private int myBuffer; private int myBitsToGo; private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff, 0x7fffff,0xffffff,0x1ffffff,0x3ffffff,0x7ffffff, 0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff }; private static final int BITS_PER_BYTE = 8; /** * Required by OutputStream subclasses, write the low * 8-bits to the underlying outputstream */ public void write(int b) throws IOException { myOutput.write(b); } /** * Create a stream that writes-through to the <code>OutputStream</code> object * passed as a parameter. * @param out is the output stream to which bits are written */ public BitOutputStream(OutputStream out){ myOutput = out; initialize(); }
  • 5. private void initialize(){ myBuffer = 0; myBitsToGo = BITS_PER_BYTE; } /** * Construct a bit-at-a-time output stream with specified file * name. * @param filename is the name of the file being written * @throws RuntimeException if opening file fails for either FileNotFound * or for Security exceptoins */ public BitOutputStream(String filename) { try{ myOutput = new BufferedOutputStream(new FileOutputStream(filename)); } catch (FileNotFoundException fnf){ throw new RuntimeException("could not create " + filename + " " + fnf); } catch(SecurityException se){ throw new RuntimeException("security exception on write " + se); } initialize(); } /** * Flushes bits not yet written, must be called by client * programs if <code>close</code> isn't called. * @throws RuntimeException if there's a problem writing bits */ public void flush() { if (myBitsToGo != BITS_PER_BYTE) { try{ write( (myBuffer << myBitsToGo) ); } catch (java.io.IOException ioe){ throw new RuntimeException("error writing bits on flush " + ioe); } myBuffer = 0; myBitsToGo = BITS_PER_BYTE; } try{ myOutput.flush(); } catch (java.io.IOException ioe){ throw new RuntimeException("error on flush " + ioe); } } /** * Releases system resources associated with file and * flushes bits not yet written. Either this function * or flush must be called or not all bits will be written * @throws RuntimeException if close fails
  • 6. */ public void close() { flush(); try{ myOutput.close(); } catch (IOException ioe){ throw new RuntimeException("error closing BitOutputStream " + ioe); } } /** * Write specified number of bits from value to a file. * @param howManyBits is number of bits to write (1-32) * @param value is source of bits, rightmost bits are written * @throws RuntimeException if there's an I/O problem writing bits */ public void writeBits(int howManyBits, int value) { value &= bmask[howManyBits]; // only right most bits valid while (howManyBits >= myBitsToGo){ myBuffer = (myBuffer << myBitsToGo) | (value >> (howManyBits - myBitsToGo)); try{ write(myBuffer); } catch (java.io.IOException ioe){ throw new RuntimeException("error writing bits " + ioe); } value &= bmask[howManyBits - myBitsToGo]; howManyBits -= myBitsToGo; myBitsToGo = BITS_PER_BYTE; myBuffer = 0; } if (howManyBits > 0) { myBuffer = (myBuffer << howManyBits) | value; myBitsToGo -= howManyBits; } } }
  • 7. Diff.java import java.io.*; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.ProgressMonitorInputStream; public class Diff { protected static JFileChooser ourChooser = new JFileChooser("."); public Diff(){ } public static void showMessage(String message){ JOptionPane.showMessageDialog(null, message,"Diff Output", JOptionPane.INFORMATION_MESSAGE); } public static boolean doDiffer(File[] files){ try { ProgressMonitorInputStream stream1 = new ProgressMonitorInputStream( null, "reading "+files[0].getName(), new FileInputStream(files[0])); ProgressMonitorInputStream stream2 = new ProgressMonitorInputStream( null, "reading "+files[1].getName(), new FileInputStream(files[1])); BitInputStream b1 = new BitInputStream(stream1); BitInputStream b2 = new BitInputStream(stream2); while (true) { int x = b1.readBits(8); int y = b2.readBits(8); if (x == -1) return y == -1; if (y == -1) return false; if (x != y) return false; } // never reached } catch (IOException e) { e.printStackTrace(); JOptionPane.showMessageDialog(null,"trouble reading","Diff Error", JOptionPane.ERROR_MESSAGE); return false; } } public static void main(String[] args){ ourChooser.setMultiSelectionEnabled(true); ourChooser.setDialogTitle("Diff: choose two files"); int retval = ourChooser.showOpenDialog(null); if (retval == JFileChooser.APPROVE_OPTION){ File[] files = ourChooser.getSelectedFiles();
  • 8. if (files.length != 2){ JOptionPane.showMessageDialog(null,"Choose Two Files", "Diff Error",JOptionPane.ERROR_MESSAGE); } else { if (doDiffer(files)){ showMessage("Files are the same"); } else { showMessage("Files DIFFER somewhere"); } } } System.exit(0); } } Huff.java /** * Main/launch program for Huff assignment. A better * comment than this is warranted. * @author YOU THE STUDENT * */ public class Huff { public static void main(String[] args){ HuffViewer sv = new HuffViewer("Duke Compsci Huffing"); IHuffProcessor proc = new SimpleHuffProcessor(); sv.setModel(proc); } }
  • 9. HuffMark.java import javax.swing.JFileChooser; import javax.swing.JOptionPane; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class HuffMark { protected static JFileChooser ourOpenChooser = new JFileChooser(System .getProperties().getProperty("user.dir")); static { ourOpenChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); } private double myTotalCompressTime; private long myTotalUncompressedBytes; private long myTotalCompressedBytes; private IHuffProcessor myHuffer; private static String SUFFIX = ".hf"; private static boolean FAST_READER = true; public void compress(File f) throws IOException{ if (f.getName().endsWith(SUFFIX)) return; // don't read .hf files! if (f.isDirectory()) return; // don't read directories double start = System.currentTimeMillis(); myHuffer.preprocessCompress(getFastByteReader(f)); File outFile = new File(getCompressedName(f)); FileOutputStream out = new FileOutputStream(outFile); System.out.println("compressing to: "+outFile.getCanonicalPath()); myHuffer.compress(getFastByteReader(f), out,true); double end = System.currentTimeMillis(); double time = (end-start)/1000.0; myTotalUncompressedBytes += f.length(); myTotalCompressedBytes += outFile.length(); myTotalCompressTime += time; System.out.printf("%s fromt %d tot %d int %.3fn",f.getName(),f.length(),outFile.length(),time); } public void doMark() throws IOException{ if (myHuffer == null){ myHuffer = new SimpleHuffProcessor(); } int action = ourOpenChooser.showOpenDialog(null); if (action == JFileChooser.APPROVE_OPTION){ File dir = ourOpenChooser.getSelectedFile(); File[] list = dir.listFiles(); for(File f : list){
  • 10. compress(f); } System.out.println("--------"); System.out.printf("total bytes read: %dn",myTotalUncompressedBytes); System.out.printf("total compressed bytes %dn", myTotalCompressedBytes); System.out.printf("total percent compression %.3fn",100.0* (1.0 - 1.0*myTotalCompressedBytes/myTotalUncompressedBytes)); System.out.printf("compression time: %.3fn",myTotalCompressTime); } } public static void main(String[] args) throws IOException{ HuffMark hf = new HuffMark(); hf.doMark(); } private String getCompressedName(File f){ String name = f.getName(); String path = null; try { path = f.getCanonicalPath(); } catch (IOException e) { System.err.println("trouble with file canonicalizing "+f); return null; } int pos = path.lastIndexOf(name); String newName = path.substring(0, pos) + name + SUFFIX; return newName; } private InputStream getFastByteReader(File f) throws FileNotFoundException{ if (!FAST_READER){ return new FileInputStream(f); } ByteBuffer buffer = null; try { FileChannel channel = new FileInputStream(f).getChannel(); buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); byte[] barray = new byte[buffer.limit()]; if (barray.length != channel.size()){ System.err.println(String.format("Reading %s error: lengths differ %d %ldn",f.getName(),barray.length,channel.size())); } buffer.get(barray); return new ByteArrayInputStream(barray); } catch (IOException e) { e.printStackTrace(); } return null; } }
  • 11. HuffViewer.java import javax.swing.*; import java.awt.BorderLayout; import java.awt.event.*; import java.util.*; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * The GUI/View for Huffman coding assignment. Clients communicate * with this view by attaching a model and then using the menu choices/options that * are part of the GUI. Thus client code that fails to call <code>setModel</code> will * almost certainly not work and generate null pointer problems because the view/GUI will * not have an associated model. * <P> * @author Owen Astrachan * */ public class HuffViewer extends JFrame { private static String HUFF_SUFFIX = ".hf"; private static String UNHUFF_SUFFIX = ".unhf"; private static boolean FAST_READER = true; protected JTextArea myOutput; protected IHuffProcessor myModel; protected String myTitle; protected JTextField myMessage; protected File myFile; private boolean myForce; protected static JFileChooser ourChooser = new JFileChooser(System.getProperties().getProperty("user.dir")); public HuffViewer(String title) { setDefaultCloseOperation(EXIT_ON_CLOSE); JPanel panel = (JPanel) getContentPane(); panel.setLayout(new BorderLayout()); setTitle(title); myTitle = title; myForce = false; panel.add(makeOutput(), BorderLayout.CENTER); panel.add(makeMessage(), BorderLayout.SOUTH); makeMenus(); pack(); setSize(400, 400); setVisible(true); }
  • 12. /** * Associates this view with the given model. The GUI/View will * attach itself to the model so that communication between the view * and the model as well as <em>vice versa</em> is supported. * @param model is the model for this view */ public void setModel(IHuffProcessor model) { myModel = model; myModel.setViewer(this); } protected JPanel makeMessage() { JPanel p = new JPanel(new BorderLayout()); myMessage = new JTextField(30); p.setBorder(BorderFactory.createTitledBorder("message")); p.add(myMessage, BorderLayout.CENTER); return p; } protected JPanel makeOutput() { JPanel p = new JPanel(new BorderLayout()); myOutput = new JTextArea(10,40); p.setBorder(BorderFactory.createTitledBorder("output")); p.add(new JScrollPane(myOutput), BorderLayout.CENTER); return p; } protected File doRead() { int retval = ourChooser.showOpenDialog(null); if (retval != JFileChooser.APPROVE_OPTION) { return null; } showMessage("reading/initializing"); myFile = ourChooser.getSelectedFile(); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(myFile),"counting/reading bits ..."); } else { temp = getMonitorableStream(myFile,"counting/reading bits ..."); } final ProgressMonitorInputStream pmis = temp; final ProgressMonitor progress = pmis.getProgressMonitor(); try { Thread fileReaderThread = new Thread() { public void run() { try { int saved = myModel.preprocessCompress(pmis); HuffViewer.this.showMessage("saved: "+saved+" bits"); } catch (IOException e) {
  • 13. HuffViewer.this.showError("reading exception"); e.printStackTrace(); } if (progress.isCanceled()) { HuffViewer.this.showError("reading cancelled"); } } }; fileReaderThread.start(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } File ret = myFile; myFile = null; return ret; } protected JMenu makeOptionsMenu() { JMenu menu = new JMenu("Options"); JCheckBoxMenuItem force = new JCheckBoxMenuItem(new AbstractAction( "Force Compression") { public void actionPerformed(ActionEvent ev) { myForce = !myForce; } }); menu.add(force); return menu; } protected JMenu makeFileMenu() { JMenu fileMenu = new JMenu("File"); fileMenu.add(new AbstractAction("Open/Count") { public void actionPerformed(ActionEvent ev) { doRead(); } }); fileMenu.add(new AbstractAction("Compress") { public void actionPerformed(ActionEvent ev) { doSave(); } }); fileMenu.add(new AbstractAction("Uncompress") { public void actionPerformed(ActionEvent ev) { doDecode(); } }); fileMenu.add(new AbstractAction("Quit") { public void actionPerformed(ActionEvent ev) { System.exit(0); }
  • 14. }); return fileMenu; } protected void makeMenus() { JMenuBar bar = new JMenuBar(); bar.add(makeFileMenu()); bar.add(makeOptionsMenu()); setJMenuBar(bar); } private void doDecode() { File file = null; showMessage("uncompressing"); try { int retval = ourChooser.showOpenDialog(null); if (retval != JFileChooser.APPROVE_OPTION) { return; } file = ourChooser.getSelectedFile(); String name = file.getName(); String uname = name; if (name.endsWith(HUFF_SUFFIX)) { uname = name.substring(0,name.length() - HUFF_SUFFIX.length()) + UNHUFF_SUFFIX; } else { uname = name + UNHUFF_SUFFIX; } String newName = JOptionPane.showInputDialog(this, "Name of uncompressed file", uname); if (newName == null) { return; } String path = file.getCanonicalPath(); int pos = path.lastIndexOf(name); newName = path.substring(0, pos) + newName; final File newFile = new File(newName); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(file),"uncompressing bits ..."); } else { temp = getMonitorableStream(file, "uncompressing bits..."); } final ProgressMonitorInputStream stream = temp; final ProgressMonitor progress = stream.getProgressMonitor(); final OutputStream out = new FileOutputStream(newFile); Thread fileReaderThread = new Thread() { public void run() { try { myModel.uncompress(stream, out); } catch (IOException e) { HuffViewer.this.showError("reading exception"); e.printStackTrace(); }
  • 15. if (progress.isCanceled()) { HuffViewer.this.showError("reading cancelled"); } } }; fileReaderThread.start(); } catch (FileNotFoundException e) { showError("could not open " + file.getName()); e.printStackTrace(); } catch (IOException e) { showError("IOException, uncompression halted from viewer"); e.printStackTrace(); } } private void doSave() { myFile = doRead(); if (myFile == null){ return; } String name = myFile.getName(); showMessage("compressing "+name); String newName = JOptionPane.showInputDialog(this, "Name of compressed file", name + HUFF_SUFFIX); if (newName == null) { return; } String path = null; try { path = myFile.getCanonicalPath(); } catch (IOException e) { showError("trouble with file canonicalizing"); return; } int pos = path.lastIndexOf(name); newName = path.substring(0, pos) + newName; final File file = new File(newName); try { final FileOutputStream out = new FileOutputStream(file); ProgressMonitorInputStream temp = null; if (FAST_READER){ temp = getMonitorableStream(getFastByteReader(myFile),"compressing bits..."); } else { temp = getMonitorableStream(myFile,"compressing bits ..."); } final ProgressMonitorInputStream pmis = temp; final ProgressMonitor progress = pmis.getProgressMonitor(); Thread fileWriterThread = new Thread() { public void run() { try { myModel.compress(pmis, out,myForce); } catch (IOException e) { HuffViewer.this.showError("compression exception"); e.printStackTrace(); } if (progress.isCanceled()) {
  • 16. HuffViewer.this.showError("compression cancelled"); cleanUp(file); } } }; fileWriterThread.start(); } catch (FileNotFoundException e) { showError("could not open " + file.getName()); e.printStackTrace(); } myFile = null; } private void cleanUp(File f) { if (!f.delete()) { showError("trouble deleting " + f.getName()); } else { // do something here? } } private ProgressMonitorInputStream getMonitorableStream(InputStream stream, String message) { final ProgressMonitorInputStream pmis = new ProgressMonitorInputStream( this, message, stream); ProgressMonitor progress = pmis.getProgressMonitor(); progress.setMillisToDecideToPopup(1); progress.setMillisToPopup(1); return pmis; } private ProgressMonitorInputStream getMonitorableStream(File file, String message) { try { FileInputStream stream = new FileInputStream(file); if (stream == null){ System.out.println("null on "+file.getCanonicalPath()); } final ProgressMonitorInputStream pmis = new ProgressMonitorInputStream( this, message, stream); ProgressMonitor progress = pmis.getProgressMonitor(); progress.setMillisToDecideToPopup(1); progress.setMillisToPopup(1); return pmis; } catch (IOException e) { showError("could not open " + file.getName()); e.printStackTrace(); return null; } }
  • 17. /** * To be called by model/client code to display strings in the GUI. Each object * in parameter elements will be displayed as a string in this view. * @param elements is source of objects that will be displayed, each object's * <code>toString</code> method wil be called to display. */ public void update(Collection elements) { showMessage(""); myOutput.setText(""); for(Object o : elements){ myOutput.append(o+"n"); } } /** * Display a text message in the view (e.g., in the small text area * at the bottom of the GUI), thus a modeless message the user can ignore. * @param s is the message displayed */ public void showMessage(String s) { myMessage.setText(s); } /** * Show a modal-dialog indicating an error; the user must dismiss the * displayed dialog. * @param s is the error-message displayed */ public void showError(String s) { JOptionPane.showMessageDialog(this, s, "Huff info", JOptionPane.INFORMATION_MESSAGE); } private ByteArrayInputStream getFastByteReader(File f){ ByteBuffer buffer = null; try { FileChannel channel = new FileInputStream(f).getChannel(); buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); byte[] barray = new byte[buffer.limit()]; if (barray.length != channel.size()){ showError(String.format("Reading %s error: lengths differ %d %ldn",f.getName(),barray.length,channel.size())); } buffer.get(barray); return new ByteArrayInputStream(barray); } catch (IOException e) { e.printStackTrace(); } return null; } }
  • 18. IHuffConstants.java /** * Global constants used in Huff/Unhuff. Clients needing these * values should implement this interface or access the * values directly, e.g., as <code>IHuffConstants.BITS_PER_WORD</code>. However, * implementing the interface is preferred in which case * the values can be accessed simply as <code>BITS_PER_WORD</code>, for example. * <P> * @author Owen Astrachan */ public interface IHuffConstants { /** * The standard number of bits per chunk/word when huffing. */ public static final int BITS_PER_WORD = 8; /** * The size of the alphabet given the number of bits per chunk, this * should be 2^BITS_PER_WORD. */ public static final int ALPH_SIZE = (1 << BITS_PER_WORD); /** * The standard number of bits needed to represent/store * an int, this is 32 in Java and nearly all other languages. */ public static final int BITS_PER_INT = 32; /** * The value of the PSEUDO_EOF character. This is one-more * than the maximum value of a legal BITS_PER_WORD-bit character. */ public static final int PSEUDO_EOF = ALPH_SIZE; /** * Isolate the magic number in one place. */ public static final int MAGIC_NUMBER = 1234567873; }
  • 19. IHuffProcessor.java /** * The interface for the model that can be attached * to a HuffViewer. Most of the work done in huffing * (and unhuffing) will be via a class that implements * this interface. The interface may need to be extended * depending on the design of the huffman program. * <P> * @author Owen Astrachan * */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.File; public interface IHuffProcessor extends IHuffConstants { /** * Make sure this model communicates with some view. * @param viewer is the view for communicating. */ public void setViewer(HuffViewer viewer); /** * Preprocess data so that compression is possible --- * count characters/create tree/store state so that * a subsequent call to compress will work. The InputStream * is <em>not</em> a BitInputStream, so wrap it int one as needed. * @param in is the stream which could be subsequently compressed * @return number of bits saved by compression or some other measure */ public int preprocessCompress(InputStream in) throws IOException; /** * Compresses input to output, where the same InputStream has * previously been pre-processed via <code>preprocessCompress</code> * storing state used by this call. * @param in is the stream being compressed (not a BitInputStream) * @param out is bound to a file/stream to which bits are written * for the compressed file (not a BitOutputStream) * @return the number of bits written */ public int compress(InputStream in, OutputStream out, boolean force) throws IOException;
  • 20. /** * Uncompress a previously compressed stream in, writing the * uncompressed bits/data to out. * @param in is the previously compressed data (not a BitInputStream) * @param out is the uncompressed file/stream * @return the number of bits written to the uncompressed file/stream */ public int uncompress(InputStream in, OutputStream out) throws IOException; } SimpleHuffProcessor...> import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; public class SimpleHuffProcessor implements IHuffProcessor { private HuffViewer myViewer; public int compress(InputStream in, OutputStream out, boolean force) throws IOException { myViewer.showError("compress is not implemented"); return 0; } public int preprocessCompress(InputStream in) throws IOException { myViewer.showError("preprocessCompress is not implemented"); return 0; } public void setViewer(HuffViewer viewer) { myViewer = viewer; } public int uncompress(InputStream in, OutputStream out) throws IOException { myViewer.showError("uncompress is not implemented"); return 0; } private void showMessage(ArrayList<String> list){ myViewer.update(list); } }
  • 21. TreeNode.java /** * Utility binary-tree (Huffman tree) node for Huffman coding. * This is a simple, standard binary-tree node implementing * the comparable interface based on weight. * * @author Owen Astrachan * @version 1.0 July 2000 * @version 2.0 Jan 2006 */ public class TreeNode implements Comparable<TreeNode> { public int myValue; public int myWeight; public TreeNode myLeft; public TreeNode myRight; /** * construct leaf node (null children) * * @param value * is the value stored in the node (e.g., character) * @param weight * is used for comparison (e.g., count of # occurrences) */ public TreeNode(int value, int weight) { myValue = value; myWeight = weight; } /** * construct internal node (with children) * * @param value * is stored as value of node * @param weight * is weight of node * @param ltree * is left subtree * @param rtree * is right subtree */
  • 22. public TreeNode(int value, int weight, TreeNode ltree, TreeNode rtree) { this(value, weight); myLeft = ltree; myRight = rtree; } /** * Return value based on comparing this TreeNode to another. * @return -1 if this < o, +1 if this > o, and 0 if this == 0 */ public int compareTo(TreeNode rhs) { return myWeight - rhs.myWeight; } }