package com.limegroup.gnutella.gui.library; import java.awt.Color; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.Icon; import com.limegroup.gnutella.FileDesc; import com.limegroup.gnutella.FileManager; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.downloader.IncompleteFileManager; import com.limegroup.gnutella.gui.GUIMediator; import com.limegroup.gnutella.gui.IconManager; import com.limegroup.gnutella.gui.tables.AbstractDataLine; import com.limegroup.gnutella.gui.tables.ColoredCell; import com.limegroup.gnutella.gui.tables.ColoredCellImpl; import com.limegroup.gnutella.gui.tables.FileTransfer; import com.limegroup.gnutella.gui.tables.LimeTableColumn; import com.limegroup.gnutella.gui.tables.SizeHolder; import com.limegroup.gnutella.gui.tables.UploadCountHolder; import com.limegroup.gnutella.gui.themes.ThemeFileHandler; import com.limegroup.gnutella.gui.themes.ThemeMediator; import com.limegroup.gnutella.gui.themes.ThemeObserver; import com.limegroup.gnutella.gui.xml.XMLUtils; import com.limegroup.gnutella.licenses.License; import com.limegroup.gnutella.xml.LimeXMLDocument; import com.limegroup.gnutella.xml.LimeXMLSchemaRepository; import com.limegroup.gnutella.xml.MetaFileManager; import com.limegroup.gnutella.util.NameValue; /** * This class acts as a single line containing all * the necessary Library info. * @author Sam Berlin */ //2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678| public final class LibraryTableDataLine extends AbstractDataLine implements ThemeObserver, FileTransfer { /** * 0 final constant to preserve memory & allocations */ private static final Integer ZERO_INTEGER = new Integer(0); /** * 0 / 0 final constant UploadCountHolder to preserve memory & allocations */ private static final UploadCountHolder ZERO_UPLOAD_COUNT_HOLDER = new UploadCountHolder(0, 0); /** * Whether or not tooltips will display XML info. */ private static boolean _allowXML; /** * The schemas available */ private static String[] _schemas; /** * The meta file manager */ private static MetaFileManager _mfm; /** * Constant for the column with the icon of the file. */ static final int ICON_IDX = 0; private static final LimeTableColumn ICON_COLUMN = new LimeTableColumn(ICON_IDX, "LIBRARY_TABLE_ICON", GUIMediator.getThemeImage("question_mark"), 18, true, Icon.class); /** * Constant for the column with the name of the file. */ static final int NAME_IDX = 1; private static final LimeTableColumn NAME_COLUMN = new LimeTableColumn(NAME_IDX, "LIBRARY_TABLE_NAME", 239, true, ColoredCell.class); /** * Constant for the column storing the size of the file. */ static final int SIZE_IDX = 2; private static final LimeTableColumn SIZE_COLUMN = new LimeTableColumn(SIZE_IDX, "LIBRARY_TABLE_SIZE", 62, true, ColoredCell.class); /** * Constant for the column storing the file type (extension or more * more general type) of the file. */ static final int TYPE_IDX = 3; private static final LimeTableColumn TYPE_COLUMN = new LimeTableColumn(TYPE_IDX, "LIBRARY_TABLE_TYPE", 48, true, ColoredCell.class); /** * Constant for the column storing the file's path */ static final int PATH_IDX = 4; private static final LimeTableColumn PATH_COLUMN = new LimeTableColumn(PATH_IDX, "LIBRARY_TABLE_PATH", 108, true, ColoredCell.class); /** * Constant for the column storing the number of upload count info * */ static final int UPLOADS_IDX = 5; private static final LimeTableColumn UPLOADS_COLUMN = new LimeTableColumn(UPLOADS_IDX, "LIBRARY_TABLE_UPLOAD_COUNT", 62, true, UploadCountHolder.class); /** * Constant for the column storing the numbe of hits * of the file. */ static final int HITS_IDX = 6; private static final LimeTableColumn HITS_COLUMN = new LimeTableColumn(HITS_IDX, "LIBRARY_TABLE_HITCOUNT", 39, true, Integer.class); /** * Constant for the column storing the number of alt locations * */ static final int ALT_LOC_IDX = 7; private static final LimeTableColumn ALT_LOC_COLUMN = new LimeTableColumn(ALT_LOC_IDX, "LIBRARY_TABLE_NUMALTLOC", 72, true, Integer.class); /** * Constant for the license index. */ static final int LICENSE_IDX = 8; private static final LimeTableColumn LICENSE_COLUMN = new LimeTableColumn(LICENSE_IDX, "LIBRARY_TABLE_LICENSE", 20, true, License.class); /** * Number of columns */ static final int NUMBER_OF_COLUMNS = 9; /** Variable for the file */ private File _file; /** Variable for the name */ private String _name; /** Variable for the type */ private String _type; /** Variable for the size */ private int _size; /** Variable to hold the file descriptor */ private FileDesc _fileDesc; /** Variable for the path */ private String _path; /** * The colors for cells. */ private Color _sharedCellColor; private Color _unsharedCellColor; /** * The model this is being displayed on */ private final LibraryTableModel _model; /** * Whether or not the icon has been loaded. */ private boolean _iconLoaded = false; /** * Whether or not the icon has been scheduled to load. */ private boolean _iconScheduledForLoad = false; public LibraryTableDataLine(LibraryTableModel ltm) { super(); _model = ltm; updateTheme(); ThemeMediator.addThemeObserver(this); } /** * This must be removed from the theme observer list in * order to be garbage-collected. */ public void cleanup() { ThemeMediator.removeThemeObserver(this); } // inherit doc comment public void updateTheme() { _sharedCellColor = ThemeFileHandler.WINDOW8_COLOR.getValue(); _unsharedCellColor = ThemeFileHandler.NOT_SHARING_LABEL_COLOR.getValue(); } public FileDesc getFileDesc() { return _fileDesc; } public int getColumnCount() { return NUMBER_OF_COLUMNS; } /** * Initialize the object. * It will fail if not given a FileDesc or a File * (File is retained for compatability with the Incomplete folder) */ public void initialize(Object o) { File file; if (o instanceof FileDesc) { file = ((FileDesc)o).getFile(); _fileDesc = (FileDesc)o; } else { file = (File)o; _fileDesc = RouterService.getFileManager().getFileDescForFile(file); } super.initialize(file); String fullPath = file.getPath(); try { fullPath = file.getCanonicalPath(); } catch(IOException ioe) {} _file = file; _name = _file.getName(); _type = ""; if (!file.isDirectory()) { int index = _name.lastIndexOf("."); int index2 = fullPath.lastIndexOf(File.separator); _path = fullPath.substring(0,index2); if (index != -1 && index != 0) { _type = _name.substring(index+1); _name = _name.substring(0, index); } } _size = (int)file.length(); } /** * Returns the file of this data line. */ public File getFile() { return _file; } /** * Returns the object stored in the specified cell in the table. * * @param idx The column of the cell to access * * @return The <code>Object</code> stored at the specified "cell" in * the list */ public Object getValueAt(int idx) { switch (idx) { case ICON_IDX: if(!_iconScheduledForLoad) { _iconScheduledForLoad = true; GUIMediator.instance().schedule(new Runnable() { public void run() { GUIMediator.safeInvokeAndWait(new Runnable() { public void run() { IconManager.instance().getIconForFile(_file); _iconLoaded = true; _model.refresh(); } }); } }); return null; } else if(_iconLoaded) { return IconManager.instance().getIconForFile(_file); } else { return null; } case NAME_IDX: String nm = _name; // note: this fits better in the data line because // sorting and whatnot will work correctly. if (LibraryMediator.incompleteDirectoryIsSelected()) { try { //Ideally we'd eliminate the dependency on IFM, but this seems //better than adding yet another method to RouterService. nm = IncompleteFileManager.getCompletedName(_file); } catch (IllegalArgumentException e) { //Not an incomplete file? Just return untranslated value. } } return new ColoredCellImpl(nm, getColor()); case SIZE_IDX: return new ColoredCellImpl(new SizeHolder(_size), getColor()); case TYPE_IDX: return new ColoredCellImpl(_type, getColor()); case HITS_IDX: if ( _fileDesc == null ) return null; int hits = _fileDesc.getHitCount(); // don't allocate if we don't have to return hits == 0 ? ZERO_INTEGER : new Integer(hits); //note: we use Integer here because its compareTo is // smarter than String's, and it has a toString anyway. case ALT_LOC_IDX: if ( _fileDesc == null ) return null; int locs = RouterService.getAltlocManager().getNumLocs(_fileDesc.getSHA1Urn()) - 1; return locs <= 0 ? ZERO_INTEGER : new Integer(locs); case UPLOADS_IDX: if ( _fileDesc == null ) return null; int a = _fileDesc.getAttemptedUploads(); int c = _fileDesc.getCompletedUploads(); return a == 0 && c == 0 ? ZERO_UPLOAD_COUNT_HOLDER : new UploadCountHolder(a, c); case PATH_IDX: return new ColoredCellImpl(_path, getColor()); case LICENSE_IDX: License lc = getLicense(); if(lc != null) { if(lc.isValid(_fileDesc.getSHA1Urn())) return new NameValue(lc.getLicenseName(), new Integer(License.VERIFIED)); else return new NameValue(lc.getLicenseName(), new Integer(License.UNVERIFIED)); } else { return null; } } return null; } public LimeTableColumn getColumn(int idx) { switch(idx) { case ICON_IDX: return ICON_COLUMN; case NAME_IDX: return NAME_COLUMN; case SIZE_IDX: return SIZE_COLUMN; case TYPE_IDX: return TYPE_COLUMN; case PATH_IDX: return PATH_COLUMN; case HITS_IDX: return HITS_COLUMN; case ALT_LOC_IDX: return ALT_LOC_COLUMN; case UPLOADS_IDX: return UPLOADS_COLUMN; case LICENSE_IDX: return LICENSE_COLUMN; } return null; } public boolean isClippable(int idx) { switch(idx) { case ICON_IDX: return false; default: return true; } } public int getTypeAheadColumn() { return NAME_IDX; } public boolean isDynamic(int idx) { switch(idx) { case HITS_IDX: case ALT_LOC_IDX: case UPLOADS_IDX: return true; } return false; } /** * Initialize things we only need to do once */ static void setXMLEnabled(boolean en) { _allowXML = en; if ( _allowXML ) { _schemas = LimeXMLSchemaRepository.instance().getAvailableSchemaURIs(); FileManager fm = RouterService.getFileManager(); if ( fm instanceof MetaFileManager ) _mfm = (MetaFileManager)fm; } else { _schemas = null; _mfm = null; } } /** * Determines if this FileDesc has a license. */ boolean isLicensed() { return _fileDesc != null && _fileDesc.isLicensed(); } /** * Gets the license string for this FileDesc. */ License getLicense() { return _fileDesc != null ? _fileDesc.getLicense() : null; } /** Gets the first XML doc associated with the FileDesc, if one exists. */ LimeXMLDocument getXMLDocument() { if(_fileDesc != null) { List l = _fileDesc.getLimeXMLDocuments(); if(!l.isEmpty()) return (LimeXMLDocument)l.get(0); } return null; } public String[] getToolTipArray(int col) { // if XML isn't finished loading, no schemas exist, // we don't have a meta file manager, or we don't // have a FileDesc, get out of here. if ( !_allowXML || _schemas == null || _schemas.length == 0 || _mfm == null || _fileDesc == null ) return null; // Dynamically add the information. List allData = new LinkedList(); List docs = _fileDesc.getLimeXMLDocuments(); for(Iterator i = docs.iterator(); i.hasNext(); ) { LimeXMLDocument doc = (LimeXMLDocument)i.next(); allData.addAll(XMLUtils.getDisplayList(doc)); } if ( !allData.isEmpty() ) { // if it had meta-data, display the filename in the tooltip also. allData.add(0, _name); return (String[])allData.toArray(new String[allData.size()]); } else { return null; //return new String[] { "No meta-data exists.", "Click 'annotate' to add some." }; } } private Color getColor() { if (_fileDesc != null) return _sharedCellColor; if (RouterService.getFileManager().isCompletelySharedDirectory(_file)) return _sharedCellColor; return _unsharedCellColor; } }