package com.bytezone.diskbrowser.gui; import java.awt.image.BufferedImage; import java.io.File; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.zip.CRC32; import java.util.zip.Checksum; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import com.bytezone.diskbrowser.disk.AppleDisk; import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.DiskFactory; import com.bytezone.diskbrowser.disk.FormattedDisk; import com.bytezone.diskbrowser.utilities.FileFormatException; public class TreeBuilder { private static SimpleDateFormat sdf = new SimpleDateFormat ("dd MMM yyyy"); private static final boolean FULL_TREE = false; private static final List suffixes = Arrays.asList ("po", "dsk", "do", "hdv", "2mg", "d13", "sdk", "gz"); FileComparator fc = new FileComparator (); JTree tree; int totalDisks; int totalFolders; Map totalFiles = new TreeMap (); Map> duplicateDisks = new TreeMap> (); Map diskNames = new HashMap (); Map> dosMap = new TreeMap> (); public TreeBuilder (File folder) { assert(folder.exists ()); assert(folder.isDirectory ()); long start = System.currentTimeMillis (); FileNode fn = new FileNode (folder); DefaultMutableTreeNode root = new DefaultMutableTreeNode (fn); fn.setTreeNode (root); addFiles (root, folder); DefaultTreeModel treeModel = new DefaultTreeModel (root); tree = new JTree (treeModel); long duration = System.currentTimeMillis () - start; System.out.printf ( "Tree building took %,d milliseconds for %,d disk%s and %,d folder%s%n", duration, totalDisks, (totalDisks == 1 ? "" : "s"), totalFolders, (totalFolders == 1 ? "" : "s")); treeModel.setAsksAllowsChildren (true);// allows empty nodes to appear as folders setDiskIcon ("/com/bytezone/diskbrowser/icons/disk.png"); ((FileNode) root.getUserObject ()).disks = totalDisks; if (FULL_TREE) { System.out.printf ("%nFolders ..... %,5d%n", totalFolders); System.out.printf ("Disks ....... %,5d%n%n", totalDisks); int tf = 0; for (String key : totalFiles.keySet ()) { int t = totalFiles.get (key); tf += t; System.out.printf ("%13.13s %,5d%n", key + " ...........", t); } System.out.printf ("%nTotal ...... %,6d%n%n", tf); } } private void addFiles (DefaultMutableTreeNode node, File directory) { File[] files = directory.listFiles (); if (files == null || files.length == 0) { System.out.println ("Empty folder : " + directory.getAbsolutePath ()); return; } FileNode parentNode = (FileNode) node.getUserObject (); Arrays.sort (files, fc); for (File file : files) { if (file.isDirectory ()) { FileNode fn = new FileNode (file); DefaultMutableTreeNode newNode = new DefaultMutableTreeNode (fn); fn.setTreeNode (newNode); newNode.setAllowsChildren (true); node.add (newNode); totalFolders++; if (FULL_TREE) addFiles (newNode, file);// recursion! continue; } if (FULL_TREE) { int pos = file.getName ().lastIndexOf ('.'); if (pos > 0) { String type = file.getName ().substring (pos + 1).toLowerCase (); if (totalFiles.containsKey (type)) { int t = totalFiles.get (type); totalFiles.put (type, ++t); } else totalFiles.put (type, 1); } } if (file.length () != 143360 && file.length () != 116480 && file.length () != 819264 && file.length () < 200000) { String name = file.getName ().toLowerCase (); if (!name.endsWith (".sdk") && !name.endsWith (".dsk.gz")) continue; } parentNode.disks++; String filename = file.getAbsolutePath (); if (validFileType (filename)) { FileNode fn = new FileNode (file); DefaultMutableTreeNode newNode = new DefaultMutableTreeNode (fn); fn.setTreeNode (newNode); newNode.setAllowsChildren (false); node.add (newNode); if (false) checkDuplicates (file); totalDisks++; if (false) checksumDos (file); } } } private void checksumDos (File file) { if (file.length () != 143360 || file.getAbsolutePath ().contains ("/ZDisks/")) return; Disk disk = new AppleDisk (file, 35, 16); byte[] buffer = disk.readSector (0, 0); Checksum checksum = new CRC32 (); checksum.update (buffer, 0, buffer.length); long cs = checksum.getValue (); List files = dosMap.get (cs); if (files == null) { files = new ArrayList (); dosMap.put (cs, files); } files.add (file); } private void checkDuplicates (File file) { if (diskNames.containsKey (file.getName ())) { List diskList = duplicateDisks.get (file.getName ()); if (diskList == null) { diskList = new ArrayList (); duplicateDisks.put (file.getName (), diskList); diskList.add (new DiskDetails (diskNames.get (file.getName ())));// add the original } diskList.add (new DiskDetails (file));// add the duplicate } else diskNames.put (file.getName (), file); } private boolean validFileType (String filename) { int dotPos = filename.lastIndexOf ('.'); if (dotPos < 0) return false; String suffix = filename.substring (dotPos + 1).toLowerCase (); return suffixes.contains (suffix); } private void setDiskIcon (String iconName) { URL url = this.getClass ().getResource (iconName); if (url != null) { ImageIcon icon = new ImageIcon (url); DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) tree.getCellRenderer (); renderer.setLeafIcon (icon); tree.setCellRenderer (renderer); tree.setRowHeight (18); } else System.out.println ("Failed to set the disk icon : " + iconName); } /* * Class used to control the text displayed by the JTree. */ public class FileNode implements DataSource { DefaultMutableTreeNode parentNode; public final File file; private static final int MAX_NAME_LENGTH = 36; private static final int SUFFIX_LENGTH = 12; private static final int PREFIX_LENGTH = MAX_NAME_LENGTH - SUFFIX_LENGTH - 3; private FormattedDisk formattedDisk; int disks; boolean showDisks; public FileNode (File file) { this.file = file; } public void setTreeNode (DefaultMutableTreeNode node) { this.parentNode = node; } public void readFiles () { addFiles (parentNode, file); } public FormattedDisk getFormattedDisk () { if (formattedDisk == null) try { formattedDisk = DiskFactory.createDisk (file); } catch (FileFormatException e) { System.out.println ("Swallowing a FileFormatException in TreeBuilder"); System.out.println (e.getMessage ()); return null; } return formattedDisk; } public boolean replaceDisk (FormattedDisk disk) { String path = disk.getDisk ().getFile ().getAbsolutePath (); if (formattedDisk != null && path.equals (file.getAbsolutePath ())) { formattedDisk = disk; return true; } return false; } @Override public String toString () { String name = file.getName (); if (name.length () > MAX_NAME_LENGTH) name = name.substring (0, PREFIX_LENGTH) + "..." + name.substring (name.length () - SUFFIX_LENGTH); if (showDisks && disks > 0) return String.format ("%s (%,d)", name, disks); return name; } @Override public String getText () { StringBuilder text = new StringBuilder (); text.append ("Directory : " + file.getAbsolutePath () + "\n\n"); text.append ("D File names " + " Date Size Type\n"); text.append ("- ----------------------------------------" + " ----------- -------------- ---------\n"); for (File f : file.listFiles ()) { String name = f.getName (); if (name.startsWith (".")) continue; Date d = new Date (f.lastModified ()); int pos = name.lastIndexOf ('.'); String type = pos > 0 && !f.isDirectory () ? name.substring (pos) : ""; String size = f.isDirectory () ? "" : String.format ("%,14d", f.length ()); text.append (String.format ("%s %-40.40s %s %-14s %s%n", f.isDirectory () ? "D" : " ", name, sdf.format (d), size, type)); } if (text.length () > 0) text.deleteCharAt (text.length () - 1); return text.toString (); } @Override public String getAssembler () { return null; } @Override public String getHexDump () { return null; } @Override public BufferedImage getImage () { return null; } @Override public JComponent getComponent () { return null; } } private class FileComparator implements Comparator { @Override public int compare (File filea, File fileb) { boolean fileaIsDirectory = filea.isDirectory (); boolean filebIsDirectory = fileb.isDirectory (); if (fileaIsDirectory && !filebIsDirectory) return -1; if (!fileaIsDirectory && filebIsDirectory) return 1; return filea.getName ().compareToIgnoreCase (fileb.getName ()); } } }