
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import java.math.*;
import java.text.*;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: </p>
 * @author unascribed
 * @version 1.0
 */

/*
 - rename directory
 - compress ratio column?
 drag-n-drop
 - add entries

*/

public class JWinZip extends JFrame
	implements DragGestureListener
{
    private JMenuBar myMenuBar = new JMenuBar();
    private JMenu myFileMenu = new JMenu();
    private JMenuItem myOpenMenuItem = new JMenuItem();
    private JScrollPane myScrollPane = new JScrollPane();
    private JTable myEntryTable = new JTable();
    private ArrayList myData=new ArrayList();
    private File myFile;
    private File myExtractDir;
    private JLabel myStatusBar = new JLabel();
    private JPopupMenu myPopupMenu = new JPopupMenu();
    private JMenuItem myExtractMenuItem = new JMenuItem();
    private JMenu myEditMenu = new JMenu();
    private JMenuItem mySelectAllMenuItem = new JMenuItem();
    private DropTarget myDropTarget;
    private DragSource myDragSource;
    private File myTempDir;
    private File myTempFile;
    private int[] mySelections=new int[0];
    private boolean myIsSelecting=false;
    private JMenuItem mySelectAllMenuItem2 = new JMenuItem();
    private JMenuItem myAddMenuItem = new JMenuItem();
    private JMenuItem myAddMenuItem2 = new JMenuItem();
    private JMenuItem myDeleteMenuItem2 = new JMenuItem();
    private JMenuItem myDeleteMenuItem = new JMenuItem();
    private JMenuItem myRenameMenuItem2 = new JMenuItem();
    private JMenuItem myNewMenuItem = new JMenuItem();
    private JMenuItem myExitMenuItem = new JMenuItem();
    private JMenuItem myExtractMenuItem2 = new JMenuItem();
    private JMenuItem myRenameMenuItem = new JMenuItem();
    private JMenuItem myChangeDirMenuItem = new JMenuItem();
    private JMenuItem myChangeDirMenuItem2 = new JMenuItem();

    public JWinZip() {
	// get starting directory
	myFile=new File("/");
	setTitle("JWinZip");

	myDragSource=DragSource.getDefaultDragSource();
	myDragSource.createDefaultDragGestureRecognizer(myEntryTable,
		DnDConstants.ACTION_COPY_OR_MOVE,this);

	DropTargetListener dropTargetListener=new WinZipDropTarget();
	myDropTarget=new DropTarget(myEntryTable,
				    DnDConstants.ACTION_COPY_OR_MOVE,dropTargetListener,true);

	try {
	    myTempDir=File.createTempFile("jwinzip",".dir");
	    myTempDir.delete();

	    jbInit();
	}
	catch(Exception e) {
	    e.printStackTrace();
	}

	DefaultTableModel model = new DefaultTableModel() {
	    public boolean isCellEditable(int row, int col)
		    { return false; }
	};

	myEntryTable.setModel(model);
	model.addColumn("File Name");
	model.addColumn("Date");
	model.addColumn("Size");
	model.addColumn("Compressed Size");
	model.addColumn("Directory");

// listen for selection
//myEntryTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	ListSelectionModel rowModel = myEntryTable.getSelectionModel();

	rowModel.addListSelectionListener(new ListSelectionListener() {
	    public void valueChanged(ListSelectionEvent inEvent) {
		myEntryTable_valueChanged(inEvent);
	    }
	});

	TableColumnModel columnModel=myEntryTable.getColumnModel();
	columnModel.addColumnModelListener(new TableColumnModelListener() {
	    public void columnSelectionChanged(ListSelectionEvent inEvent) {}
	    public void columnMarginChanged(ChangeEvent inEvent) {}
	    public void columnRemoved(TableColumnModelEvent inEvent) {}
	    public void columnAdded(TableColumnModelEvent inEvent) {}

	    public void columnMoved(TableColumnModelEvent inEvent) {
		myEntryTable_columnSelected(inEvent);
	    }
	});
    }

    public void myEntryTable_columnSelected(TableColumnModelEvent inEvent) {
	int col=inEvent.getToIndex();
	TreeMap map=new TreeMap();
	int length=myData.size();

	// sort by column
	for(int i=0;i<length;i++) {
	    map.put(myEntryTable.getValueAt(i,col),myData.get(i));
	}

	myData=new ArrayList(map.values());
	loadTable();
    }

    public void dragGestureRecognized(DragGestureEvent inEvent) {
	DragSourceListener listener=new WinZipDragSource();
	inEvent.startDrag(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR),
			  new WinZipTransfer(),listener);

	// ensure that drag does not change selection
	mySelections=myEntryTable.getSelectedRows();

	System.out.println("drag gesture: "+inEvent);
    }

    void myEntryTable_valueChanged(ListSelectionEvent inEvent) {
	if (!myIsSelecting && inEvent.getValueIsAdjusting()) {
	    int[] oldSelections=mySelections;
	    mySelections=myEntryTable.getSelectedRows();

//	    if( myEntryTable.getSelectionModel().isSelectedIndex(
//		    myEntryTable.getSelectedRow()) )
//	    {
//		System.out.println("  changing selection: "+oldSelections.length);
		selectRows(oldSelections);
//	    }
	}
    }

//	public boolean postEvent(Event evt) {
//		/**@todo Implement this java.awt.MenuContainer abstract method*/
//		throw new java.lang.UnsupportedOperationException("Method postEvent() not yet implemented.");
//	}

    public Font getFont() {
	return new Font("Dialog",Font.PLAIN,24);
    }

    public static void main(String[] args) {
	JWinZip winZip = new JWinZip();
	winZip.setSize(600,400);
	winZip.show();
    }

    private void jbInit() throws Exception {
	this.setJMenuBar(myMenuBar);
	this.addWindowListener(new java.awt.event.WindowAdapter() {
	    public void windowClosed(WindowEvent e) {
		this_windowClosed(e);
	    }
            public void windowClosing(WindowEvent e) {
                this_windowClosing(e);
            }
	});
	myFileMenu.setText("File");
	myOpenMenuItem.setText("Open");
	myOpenMenuItem.addActionListener(new java.awt.event.ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		myOpenMenuItem_actionPerformed(e);
	    }
	});
	myEntryTable.addMouseListener(new java.awt.event.MouseAdapter() {
	    public void mouseClicked(MouseEvent e) {
		myEntryTable_mouseClicked(e);
	    }
	    public void mouseReleased(MouseEvent e) {
		myEntryTable_mouseReleased(e);
	    }
	});
	myExtractMenuItem.setText("Extract");
	myExtractMenuItem.addActionListener(new java.awt.event.ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		myExtractMenuItem_actionPerformed(e);
	    }
	});
	myEditMenu.setText("Edit");
	mySelectAllMenuItem.setText("Select All");
        mySelectAllMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                mySelectAllMenuItem_actionPerformed(e);
            }
        });
	mySelectAllMenuItem2.setText("Select All");
        mySelectAllMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                mySelectAllMenuItem2_actionPerformed(e);
            }
        });
        myAddMenuItem.setText("Add");
        myAddMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myAddMenuItem_actionPerformed(e);
            }
        });
        myAddMenuItem2.setText("Add");
        myAddMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myAddMenuItem2_actionPerformed(e);
            }
        });
        myDeleteMenuItem2.setText("Delete");
        myDeleteMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myDeleteMenuItem2_actionPerformed(e);
            }
        });
        myDeleteMenuItem.setText("Delete");
        myDeleteMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myDeleteMenuItem_actionPerformed(e);
            }
        });
        myRenameMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myRenameMenuItem2_actionPerformed(e);
            }
        });
        myRenameMenuItem2.setText("Rename");
        myNewMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myNewMenuItem_actionPerformed(e);
            }
        });
        myNewMenuItem.setText("New");
        myExitMenuItem.setText("Exit");
        myExitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myExitMenuItem_actionPerformed(e);
            }
        });
        myExtractMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myExtractMenuItem2_actionPerformed(e);
            }
        });
        myExtractMenuItem2.setText("Extract");
        myRenameMenuItem.setText("Rename");
        myRenameMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myRenameMenuItem_actionPerformed(e);
            }
        });
        myChangeDirMenuItem.setText("Change Dir");
        myChangeDirMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myChangeDirMenuItem_actionPerformed(e);
            }
        });
        myChangeDirMenuItem2.setText("Change Dir");
        myChangeDirMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                myChangeDirMenuItem2_actionPerformed(e);
            }
        });
        myMenuBar.add(myFileMenu);
	myMenuBar.add(myEditMenu);
	myFileMenu.add(myExitMenuItem);
        myFileMenu.add(myNewMenuItem);
        myFileMenu.add(myOpenMenuItem);
	this.getContentPane().add(myScrollPane, BorderLayout.CENTER);
	this.getContentPane().add(myStatusBar,  BorderLayout.SOUTH);
	myScrollPane.getViewport().add(myEntryTable, null);
        myPopupMenu.add(myChangeDirMenuItem);
        myPopupMenu.add(myAddMenuItem);
	myPopupMenu.add(myDeleteMenuItem);
        myPopupMenu.add(myExtractMenuItem);
        myPopupMenu.add(myRenameMenuItem);
        myPopupMenu.add(mySelectAllMenuItem);
        myEditMenu.add(myAddMenuItem2);
        myEditMenu.add(myChangeDirMenuItem2);
        myEditMenu.add(myExtractMenuItem2);
        myEditMenu.add(myDeleteMenuItem2);
        myEditMenu.add(myRenameMenuItem2);
        myEditMenu.add(mySelectAllMenuItem2);
    }

    void this_windowClosed(WindowEvent inEvent) {
        try {
            deleteDir(myTempDir);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    void myOpenMenuItem_actionPerformed(ActionEvent inEvent) {
	JFileChooser chooser=new JFileChooser();
	File parent=myFile.getParentFile();

	if(parent==null)
	    parent=new File("/");

	chooser.setCurrentDirectory(parent);
//	chooser.setSelectedFile(myFile);
	int returnVal = chooser.showOpenDialog(this);

	if(returnVal == JFileChooser.APPROVE_OPTION) {
	    try {
		loadFile(chooser.getSelectedFile());
	    }
	    catch(Exception e) {
		e.printStackTrace();
	    }
	}
    }

    void loadFile(File inFile) throws Exception {
	myFile=inFile;
	setTitle(myFile.getPath());
	ZipFile zipFile=null;

	try {
	    zipFile=new ZipFile(myFile);
	    Enumeration enum = zipFile.entries();
	    ZipEntry entry;
	    myData.clear();
	    long size=0;
	    int count=0;

	    for(;enum.hasMoreElements();count++) {
		entry = (ZipEntry) enum.nextElement();
		size+=entry.getSize();

		if(!entry.isDirectory()) {
		    myData.add(entry);
		}
	    }

	    long length=myFile.length();

	    myStatusBar.setText("File Size: "
		+new UniqueKey(length,UniqueKey.FILE_SIZE)
		+"    Uncompressed: "+new UniqueKey(size,UniqueKey.FILE_SIZE)
		+"    Date: "
		+new UniqueKey(myFile.lastModified(),UniqueKey.DATE)
		+"    Files: "+count
		+"    Compressed Percent: "
		+new UniqueKey(100*length/size,UniqueKey.LONG));
	    loadTable();
	}
	finally {
	    try {
		zipFile.close();
	    }
	    catch(Exception e) {
		e.printStackTrace();
	    }
	}
    }

    void loadTable() {
	DefaultTableModel model=(DefaultTableModel) myEntryTable.getModel();
	Iterator iterator=myData.iterator();
	model.setNumRows(0);
	Object[] objects;
	ZipEntry entry;
	File file;

	while(iterator.hasNext()) {
	    entry = (ZipEntry) iterator.next();
	    file=new File(entry.getName());
	    objects = new Object[5];
	    objects[0] = new UniqueKey(file.getName());
	    objects[1] = new UniqueKey(entry.getTime(),UniqueKey.DATE);
	    objects[2] = new UniqueKey(entry.getSize(),UniqueKey.FILE_SIZE);
	    objects[3] = new UniqueKey(entry.getCompressedSize(),UniqueKey.FILE_SIZE);
	    objects[4] = new UniqueKey(file.getParent());
	    model.addRow(objects);
	}
    }

    // given a file/directory returns a list of files
    void getFiles(File inFile, File inEntryHome, ArrayList inEntryList, ArrayList inFileList) {
	// maintain Entry path
	if(inEntryHome==null) {
	    inEntryHome=new File(inFile.getName());
	}
	else {
	    inEntryHome=new File(inEntryHome,inFile.getName());
	}

	if(inFile.isFile()) {
	    inFileList.add(inFile);
	    inEntryList.add(inEntryHome);
	    return;
	}

	// loop through files in directory
	File[] files=inFile.listFiles();

	for(int i=0;i<files.length;i++) {
	    if(files[i].isDirectory()) {
		getFiles(files[i],inEntryHome,inEntryList,inFileList);
	    }
	    else {
		inFileList.add(files[i]);
		inEntryList.add(new File(inEntryHome,files[i].getName()));
	    }
	}
    }

    void myEntryTable_mouseClicked(MouseEvent inEvent) {
	if((inEvent.getModifiers()&MouseEvent.BUTTON3_MASK)>0) {
	    // if right click on a new row, then make new selection
	    Point point=inEvent.getPoint();
	    int row=myEntryTable.rowAtPoint(point);

	    if(!myEntryTable.isRowSelected(row)) {
		myEntryTable.clearSelection();
		myEntryTable.addRowSelectionInterval(row,row);
	    }

	    myPopupMenu.show(inEvent.getComponent(),point.x,point.y);
	}

    }

    void myEntryTable_mouseReleased(MouseEvent inEvent) {
	selectRows(mySelections);
    }

    void myExtractMenuItem_actionPerformed(ActionEvent inEvent) {
	// get extract directory
	JFileChooser chooser=new JFileChooser();
	chooser.setFileSelectionMode(chooser.DIRECTORIES_ONLY);
	chooser.setDialogType(JFileChooser.SAVE_DIALOG);
	chooser.setSelectedFile(myExtractDir);
	int returnVal = chooser.showOpenDialog(this);

	if(returnVal != JFileChooser.APPROVE_OPTION)
	    return;

	myExtractDir=chooser.getSelectedFile();

	// begin unzipping
	try {
	    extract(myExtractDir);
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
    }

/*
    *   @param inFile the directory to extract into
    *   @exception Exception
*/
    private void extract(File inFile) throws Exception {
	ZipFile zipFile=null;
	BufferedInputStream zipIn=null;
	FileOutputStream fileOut=null;
	byte[] buffer=new byte[2048];
	int length;
	ZipEntry zipEntry;
	File file, parent;
	int[] rows=myEntryTable.getSelectedRows();

	// begin unzipping
	try {
	    zipFile=new ZipFile(myFile);

	    for(int i=0;i<rows.length;i++) {
		zipEntry=(ZipEntry)myData.get(rows[i]);
		file=new File(inFile,zipEntry.getName());
		System.out.println("unzipping "+file);
		parent=file.getParentFile();

		if(!parent.exists() && !parent.mkdirs()) {
		    JOptionPane.showMessageDialog(this,
			"Could not create directory: "+parent,"Error",
		        JOptionPane.ERROR_MESSAGE);
		}

		zipIn=new BufferedInputStream(
			zipFile.getInputStream(zipEntry));
		fileOut=new FileOutputStream(file);

		while((length=zipIn.read(buffer))>0) {
		    fileOut.write(buffer,0,length);
		}

		zipIn.close();
		zipIn=null;
		fileOut.close();
		fileOut=null;
		file.setLastModified(zipEntry.getTime());
	    }
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	finally {
	    try {
		if(zipIn!=null)
		    zipIn.close();

	        if(fileOut!=null)
		    fileOut.close();

	        zipFile.close();
	    }
	    catch(Exception e) {
		e.printStackTrace();
	    }
	}
    }

    /*
    *   @param inRows the rows to select
    *   @exception Exception
    */
    private void selectRows(int[] inRows) {
	int count=myEntryTable.getRowCount();
	myIsSelecting=true;
	myEntryTable.clearSelection();

	for(int i=0;i<inRows.length;i++) {
	    if(inRows[i]<count)
	        myEntryTable.addRowSelectionInterval(inRows[i],inRows[i]);
	}

	myIsSelecting=false;
    }

    /*
    *   @param inFile the directory to extract into
    *   @exception Exception
    */
    private void deleteDir(File inDir) throws Exception {
	if(!inDir.exists())
	    return;

	 if(inDir.isFile()) {
	     inDir.delete();
	     return;
	 }

	 File[] files=inDir.listFiles();

	 for(int i=0;i<files.length;i++) {
	     deleteDir(files[i]);
	 }

	 inDir.delete();
    }

    public class WinZipTransfer implements Transferable, ClipboardOwner {
	private static final int FILE = 0;
	private final DataFlavor[] myFlavors = {DataFlavor.javaFileListFlavor};

	public WinZipTransfer() {
	}

	public DataFlavor[] getTransferDataFlavors() {
	    return (DataFlavor[])myFlavors.clone();
	}

	public boolean isDataFlavorSupported(DataFlavor flavor) {
	    int length=myFlavors.length;

	    for(int i=0;i<length;i++) {
		if(flavor.equals(myFlavors[i]))
		    return true;
	    }

	    return false;
	}

	public Object getTransferData(DataFlavor flavor)
		throws UnsupportedFlavorException, IOException
	{
	    if (!flavor.equals(myFlavors[FILE])) {
		throw new UnsupportedFlavorException(flavor);
	    }

	    try {
		deleteDir(myTempDir);
		extract(myTempDir);
		System.out.println("extracted into "+myTempDir);
		Vector data=new Vector();
		File[] files=myTempDir.listFiles();

		if(files!=null) {
		    for(int i=0;i<files.length;i++) {
			data.add(files[i]);
		    }
		}

		return data;
	    }
	    catch(Exception e) {
		if(e instanceof IOException) {
		    throw (IOException)e;
		}
		else {
		    e.printStackTrace();
		    throw new IOException(e.toString());
		}
	    }
	}

	public void lostOwnership(Clipboard clipboard, Transferable contents) {
	}
    }

    class WinZipDropTarget implements DropTargetListener {
	// ---------------------- DropTargetListener --------------------------
	public void dragEnter(DropTargetDragEvent inEvent) {
//	    System.out.println("dragEnter: "+inEvent);
	}

	public void dragOver(DropTargetDragEvent inEvent) {
//	    System.out.println("dragOver: "+inEvent);
	}

	public void dropActionChanged(DropTargetDragEvent inEvent) {
//	    System.out.println("dropActionChanged: "+inEvent);
	}

	public void dragExit(DropTargetEvent inEvent) {
//	    System.out.println("dragExit: "+inEvent);
	}

	public void drop(DropTargetDropEvent inEvent) {
//	    System.out.println("drop: "+inEvent+":"+inEvent.isLocalTransfer());

	    if(inEvent.isLocalTransfer()) {
		inEvent.rejectDrop();
		return;
	    }

	    DataFlavor f=DataFlavor.javaFileListFlavor;

	    try {
		if(inEvent.isDataFlavorSupported(f)) {
		    inEvent.acceptDrop(DnDConstants.ACTION_COPY);
		    Transferable t=inEvent.getTransferable();
		    Object o=t.getTransferData(f);

		    if(o instanceof java.util.List) {
			// prepare file list
			ArrayList files=new ArrayList();
			ArrayList entries=new ArrayList();
			java.util.List sourceFile=(java.util.List)o;
			Iterator iterator=sourceFile.iterator();

			while(iterator.hasNext()) {
			    o=iterator.next();

			    if(o instanceof File) {
				getFiles((File)o, null, entries, files);
			    }
			    else {
				System.out.println("Data not processed: "+o+":"+o.getClass());
			    }
			}

			// create archive
			addFiles(files,entries);
		    }
		    else {
			System.out.println("List not processed: "+o+":"
			    +o.getClass());
		    }

		    inEvent.dropComplete(true);
		}
		else {
		    inEvent.rejectDrop();
		    System.out.println("Data flavor is not supported");
		}
	    }
	    catch(Exception e) {
		e.printStackTrace();
		inEvent.dropComplete(false);
	    }
	}
    }

    class WinZipDragSource implements DragSourceListener {
	// ---------------------- DragSourceListener --------------------------
	public void dragEnter(DragSourceDragEvent inEvent) {
//	    System.out.println("dragEnter: "+inEvent);
	}

	public void dragOver(DragSourceDragEvent inEvent) {
//	    System.out.println("dragOver: "+inEvent);
	}

	public void dropActionChanged(DragSourceDragEvent inEvent) {
//	    System.out.println("dropActionChanged: "+inEvent);
	}

	public void dragExit(DragSourceEvent inEvent) {
//	    System.out.println("dragExit: "+inEvent);
	}

	public void dragDropEnd(DragSourceDropEvent inEvent) {
//	    System.out.println("dragDropEnd: "+inEvent);
	}
    }

    void this_windowClosing(WindowEvent e) {
	dispose();
    }

    void mySelectAllMenuItem_actionPerformed(ActionEvent e) {
	myEntryTable.setRowSelectionInterval(0,myEntryTable.getRowCount()-1);
	repaint();
    }

    void mySelectAllMenuItem2_actionPerformed(ActionEvent e) {
	mySelectAllMenuItem_actionPerformed(null);
    }

    void myAddMenuItem2_actionPerformed(ActionEvent inEvent) {
	myAddMenuItem_actionPerformed(null);
    }

    void myDeleteMenuItem_actionPerformed(ActionEvent inEvent) {
	int[] rows=myEntryTable.getSelectedRows();
	ZipOutputStream zipOut=null;
	InputStream fileIn=null;
	OutputStream fileOut=null;
	ZipFile zipFile=null;

	// begin unzipping
	try {
	    HashMap deleteData=new HashMap();
	    ZipEntry entry;

	    // build delete list
	    for(int i=0;i<rows.length;i++) {
		entry=(ZipEntry)myData.get(rows[i]);
		deleteData.put(entry,null);
	    }

	    // begin rebuilding archive
	    deleteDir(myTempDir);
	    zipOut=new ZipOutputStream(new BufferedOutputStream(
		new FileOutputStream(myTempDir)));
        zipOut.setLevel(9);
	    zipFile=new ZipFile(myFile);
	    writeEntries(myData.iterator(), zipFile, zipOut, deleteData);

	    // close zip files
	    zipOut.close();
	    zipOut=null;
	    zipFile.close();
	    zipFile=null;

	    copyfile(myTempDir,myFile);
	    loadFile(myFile);
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	finally {
	    if(zipOut!=null)
		try {
		    zipOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileOut!=null)
		try {
		    fileOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(zipFile!=null)
		try {
		    zipFile.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}
	}
    }


    void writeEntries(Iterator inIterator, ZipFile inZipFile,
	ZipOutputStream inZipOut, HashMap inMap) throws Exception
    {
	InputStream fileIn=null;

	try {
	    // begin rebuilding archive
	    ZipEntry entry;
	    ZipEntry oldEntry;
	    int length;
	    byte[] buffer=new byte[2048];

	    while(inIterator.hasNext()) {
		oldEntry=(ZipEntry)inIterator.next();

		// skip files that should be deleted
		if(inMap.containsKey(oldEntry))
		    continue;

		// write out file
		entry=new ZipEntry(oldEntry.getName());
		entry.setTime(oldEntry.getTime());

		fileIn=new BufferedInputStream(inZipFile.getInputStream(oldEntry));
		inZipOut.putNextEntry(entry);

		while((length=fileIn.read(buffer))>0) {
		    inZipOut.write(buffer,0,length);
		}

		inZipOut.closeEntry();
		fileIn.close();
		fileIn=null;
	    }
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	finally {
	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	}
    }


    void copyfile(File inSource, File inTarget) throws Exception
    {
	InputStream fileIn=null;
	OutputStream fileOut=null;

	try {
	    int length;
	    byte[] buffer=new byte[2048];

	    // copy file to destination
	    if(!inSource.renameTo(inTarget)) {
System.out.println("Must copy archive from temp");
		fileIn=new BufferedInputStream(new FileInputStream(inSource));
		fileOut=new BufferedOutputStream(new FileOutputStream(inTarget));

		while((length=fileIn.read(buffer))>0) {
		    fileOut.write(buffer,0,length);
		}

		fileIn.close();
		fileIn=null;
		fileOut.close();
		fileOut=null;

		deleteDir(inSource);
	    }
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	finally {
	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileOut!=null)
		try {
		    fileOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}
	}
    }


    void myAddMenuItem_actionPerformed(ActionEvent inEvent) {
	// get file to add
	JFileChooser chooser=new JFileChooser();
	chooser.setFileSelectionMode(chooser.FILES_AND_DIRECTORIES);
    //	chooser.setDialogType(JFileChooser.OPEN_DIALOG);
	chooser.setSelectedFile(myExtractDir);
	int returnVal = chooser.showOpenDialog(this);

	if(returnVal != JFileChooser.APPROVE_OPTION)
	    return;

	myExtractDir=chooser.getSelectedFile();

	try {
	    // prepare file list
	    ArrayList files=new ArrayList();
	    ArrayList entries=new ArrayList();
	    getFiles(myExtractDir,null,entries,files);

	    addFiles(files,entries);
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
    }

    // inEntryList and inFileList are coordinated lists that should be added
    void addFiles(ArrayList inFileList, ArrayList inEntryList) throws Exception
    {
	// begin rebuilding archive
	ZipOutputStream zipOut=null;
	InputStream fileIn=null;
	ZipFile zipFile=null;

	try {
	    // write new files to archive
	    HashMap newData=new HashMap();
	    deleteDir(myTempDir);
	    zipOut=new ZipOutputStream(new BufferedOutputStream(
		new FileOutputStream(myTempDir)));
        zipOut.setLevel(9);
	    ZipEntry entry;
	    File file;
	    File fileEntry;
	    int len=inFileList.size();
	    int i;
	    int length;
	    byte[] buffer=new byte[2048];

	    for(i=0;i<len;i++) {
		file=(File)inFileList.get(i);
		fileEntry=(File)inEntryList.get(i);

		// create zip entry
		entry=new ZipEntry(fileEntry.getPath());
		entry.setTime(file.lastModified());

		// write out file
		zipOut.putNextEntry(entry);
		fileIn=new BufferedInputStream(new FileInputStream(file));

		while((length=fileIn.read(buffer))>0) {
		    zipOut.write(buffer,0,length);
		}

		newData.put(entry.getName(),null);
		zipOut.closeEntry();
		fileIn.close();
		fileIn=null;
	    }

	    // write files from old archive
	    Iterator iterator=myData.iterator();

	    if(iterator.hasNext())
		zipFile=new ZipFile(myFile);

	    writeEntries(iterator, zipFile, zipOut, newData);

	    // close zip files
	    zipOut.close();
	    zipOut=null;

	    if(zipFile!=null) {
		zipFile.close();
		zipFile=null;
	    }

	    copyfile(myTempDir,myFile);
	    loadFile(myFile);
	}
	finally {
	    if(zipOut!=null)
		try {
		    zipOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(zipFile!=null)
		try {
		    zipFile.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}
	}
    }

    void myDeleteMenuItem2_actionPerformed(ActionEvent e) {
	myDeleteMenuItem_actionPerformed(null);
    }

    void myRenameMenuItem_actionPerformed(ActionEvent inEvent) {
	JOptionPane pane = new JOptionPane("Change to:",
	    JOptionPane.PLAIN_MESSAGE,JOptionPane.OK_CANCEL_OPTION);
	pane.setWantsInput(true);

//	String result=JOptionPane.showInputDialog(this,
//	    "Change to:","Change",
//	    JOptionPane.PLAIN_MESSAGE);

//	JOptionPane.showMessageDialog(this,
//	    "Change to:"+result,"Result",
//	    JOptionPane.PLAIN_MESSAGE);

	ZipOutputStream zipOut=null;
	InputStream fileIn=null;
	ZipFile zipFile=null;

	// get new name from user
	HashMap data=new HashMap();
	HashMap newData=new HashMap();
	int[] rows=myEntryTable.getSelectedRows();
	boolean ok=true;
	String name;
	String newName;
	ZipEntry entry;

	// build rename list
	for(int i=0;i<rows.length;i++) {
	    entry=(ZipEntry)myData.get(rows[i]);
	    name=entry.getName();

	    pane.setMessage("Change "+name+" to:");
	    pane.setInitialSelectionValue(name);
	    pane.createDialog(this, "Rename").show();

	    if(!pane.getValue().equals(new Integer(0)))
		// cancel was hit
		break;

	    newName=(String)pane.getInputValue();

	    if(newData.containsKey(newName)) {
		JOptionPane.showMessageDialog(this,
		    "File "+newName+" already Exists.",
		    "Error",JOptionPane.ERROR_MESSAGE);
		i--;
	    }
	    else {
		newData.put(newName,null);
		data.put(entry,newName);
	    }
	}

	try {
	    renameEntries(data, newData);
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
	finally {
	    if(zipOut!=null)
		try {
		    zipOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(zipFile!=null)
		try {
		    zipFile.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}
	}
    }

    void myNewMenuItem_actionPerformed(ActionEvent e) {
	File parent=myFile.getParentFile();

	if(parent==null)
	    parent=new File("/");

	JFileChooser chooser=new JFileChooser(parent);
	//chooser.setFileSelectionMode(JFileChooser.f);
	int returnVal = chooser.showOpenDialog(this);

	if(returnVal != JFileChooser.APPROVE_OPTION)
	    return;

	myFile=chooser.getSelectedFile();
	setTitle(myFile.getPath());

	myData.clear();
	DefaultTableModel model=(DefaultTableModel) myEntryTable.getModel();
	model.setNumRows(0);
	model.addRow(new Object[0]);

	long size=0;
	myStatusBar.setText("File Size: "
	    +new UniqueKey(myFile.length(),UniqueKey.FILE_SIZE)
	    +"    Uncompressed: "+new UniqueKey(size,UniqueKey.FILE_SIZE)
	    +"    Date: "
	    +new UniqueKey(myFile.lastModified(),UniqueKey.DATE)
	    +"    Files: 0");
    }

    void myExitMenuItem_actionPerformed(ActionEvent e) {
	dispose();
    }

    void myExtractMenuItem2_actionPerformed(ActionEvent e) {
	myExtractMenuItem_actionPerformed(null);
    }

    void myRenameMenuItem2_actionPerformed(ActionEvent e) {
	myRenameMenuItem_actionPerformed(null);
    }

    void myChangeDirMenuItem_actionPerformed(ActionEvent inEvent) {
	int row=myEntryTable.getSelectedRow();

	if(row<0)
	    return;

	ZipEntry entry=(ZipEntry)myData.get(row);
	File file=new File(entry.getName());
	String dirName=file.getParent();

	// get source directory to change
	JOptionPane pane = new JOptionPane("Enter Directory to Change:",
	    JOptionPane.PLAIN_MESSAGE,JOptionPane.OK_CANCEL_OPTION);
	pane.setWantsInput(true);
	pane.setInitialSelectionValue(dirName);
	pane.createDialog(this, "Change Dir").show();

	if(!pane.getValue().equals(new Integer(0)))
	    // cancel was hit
	    return;

	dirName=(String)pane.getInputValue();

	// find matching entries
	ArrayList changes=new ArrayList();
	Iterator iterator=myData.iterator();

	while(iterator.hasNext()) {
	    entry=(ZipEntry)iterator.next();

	    if(entry.getName().indexOf(dirName)==0)
		changes.add(entry);
	}

	if(changes.size()==0) {
	    JOptionPane.showMessageDialog(this,
		"There are no entries for "+dirName,"Alert",
		JOptionPane.PLAIN_MESSAGE);
	    return;
	}

	// get destination directory
	pane.setMessage("Change "+dirName+" to:");
	pane.setInitialSelectionValue(dirName);
	pane.createDialog(this, "Change Dir").show();

	if(!pane.getValue().equals(new Integer(0)))
	    // cancel was hit
	    return;

	String newDir=(String)pane.getInputValue();
	int pos=dirName.length();
	HashMap data=new HashMap();
	HashMap newData=new HashMap();
	int[] rows=myEntryTable.getSelectedRows();
	boolean ok=true;
	String name;
	String newName;
	iterator=changes.iterator();

	// build rename list
	while(iterator.hasNext()) {
	    entry=(ZipEntry)iterator.next();
	    name=entry.getName();
	    newName=newDir+name.substring(pos);

	    newData.put(newName,null);
	    data.put(entry,newName);
	}

        try {
	    renameEntries(data, newData);
	}
	catch(Exception e) {
	    e.printStackTrace();
	}
    }

    void renameEntries(HashMap inData, HashMap inNewData) throws Exception
    {
	ZipOutputStream zipOut=null;
	InputStream fileIn=null;
	ZipFile zipFile=null;

	try {
	    // begin  archive from rename list
	    deleteDir(myTempDir);
	    zipOut=new ZipOutputStream(new BufferedOutputStream(
		new FileOutputStream(myTempDir)));
        zipOut.setLevel(9);
	    String newName;
	    ZipEntry entry;
	    zipFile=new ZipFile(myFile);
	    ZipEntry oldEntry;
	    int length;
	    byte[] buffer=new byte[2048];
	    Iterator iterator=inData.keySet().iterator();

	    while(iterator.hasNext()) {
		oldEntry=(ZipEntry)iterator.next();
		newName=(String)inData.get(oldEntry);

		// write out file
		entry=new ZipEntry(newName);
		entry.setTime(oldEntry.getTime());

		fileIn=new BufferedInputStream(zipFile.getInputStream(oldEntry));
		zipOut.putNextEntry(entry);

		while((length=fileIn.read(buffer))>0) {
		    zipOut.write(buffer,0,length);
		}

		zipOut.closeEntry();
		fileIn.close();
		fileIn=null;

	    }

	    inData.putAll(inNewData);
	    writeEntries(myData.iterator(), zipFile, zipOut, inData);

	    // close zip files
	    zipOut.close();
	    zipOut=null;
	    zipFile.close();
	    zipFile=null;

	    copyfile(myTempDir,myFile);
	    loadFile(myFile);
	}
	finally {
	    if(zipOut!=null)
		try {
		    zipOut.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(fileIn!=null)
		try {
		    fileIn.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}

	    if(zipFile!=null)
		try {
		    zipFile.close();
		}
		catch(Exception e) {
		    e.printStackTrace();
		}
	}
    }

    void myChangeDirMenuItem2_actionPerformed(ActionEvent e) {
	myChangeDirMenuItem_actionPerformed(null);
    }
}


class UniqueKey implements Comparable {
	public UniqueKey(long inSize, int inFormat) {
		myId=myNextId++;

		if(inFormat==FILE_SIZE) {
			myObject=new Long(inSize);
			myString=toFileSize(inSize);
		}
		else if(inFormat==DATE) {
			myObject=new Date(inSize);
			myString=ourFormat.format((Date)myObject);
		}
		else {
			myObject=new Long(inSize);
			myString=myObject.toString();
		}
	}

	public UniqueKey(Comparable inObject) {
		myObject=inObject;

		if(myObject!=null)
			myString=myObject.toString();
		else
			myString="";
	}

	public String toString() {
		return myString;
	}

	public int compareTo(Object o) {
		long id;

		if(o!=null && o instanceof UniqueKey) {
			Object itsObject=((UniqueKey)o).myObject;
			int result=-1;

			if(myObject!=null && itsObject!=null) {
				result=myObject.compareTo(itsObject);

				if(result!=0)
					return result;
			}

			id=((UniqueKey)o).myId;

			if(myId<id)
				return -1;
			else
				return 1;
		}
		else
			return -1;
	}

	private String toFileSize(long inSize) {
		if(inSize>1000000) {
			return new BigDecimal(inSize).
				divide(BYTE_FACTOR,BigDecimal.ROUND_HALF_UP).
				divide(BYTE_FACTOR,BigDecimal.ROUND_HALF_UP).
				divide(THOUSAND,BigDecimal.ROUND_HALF_UP).
				divide(THOUSAND,2,BigDecimal.ROUND_HALF_UP).toString()+"M";
		}
		else if(inSize>1000) {
			return new BigDecimal(inSize).
				divide(BYTE_FACTOR,BigDecimal.ROUND_HALF_UP).
				divide(THOUSAND,2,BigDecimal.ROUND_HALF_UP).toString()+"K";
		}
		else
			return Long.toString(inSize);
	}
	private String myString;
	private Comparable myObject;
	private long myId;
	private static long myNextId=0;
	public final static int DATE=1;
	public final static int FILE_SIZE=2;
	public final static int LONG=3;
	private final static BigDecimal BYTE_FACTOR=new BigDecimal("1.024");
	private final static BigDecimal THOUSAND=new BigDecimal(1000);
	private final static SimpleDateFormat ourFormat=
		new SimpleDateFormat("MM/dd/yy HH:mm:ss");
}
