/* * File : NameItem.java * Created : 24 nov. 2003 * By : Olivier * * Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * AELITIS, SAS au capital de 46,603.30 euros, * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ package org.gudy.azureus2.ui.swt.views.tableitems.files; import java.io.File; import java.io.FileInputStream; import java.security.MessageDigest; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.zip.CRC32; import org.eclipse.swt.SWT; import org.gudy.azureus2.core3.disk.DiskManagerFileInfo; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerState; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.core3.util.AsyncDispatcher; import org.gudy.azureus2.core3.util.BEncoder; import org.gudy.azureus2.core3.util.ByteFormatter; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.DisplayFormatters; import org.gudy.azureus2.plugins.ui.menus.MenuItem; import org.gudy.azureus2.plugins.ui.menus.MenuItemListener; import org.gudy.azureus2.plugins.ui.tables.*; import org.gudy.azureus2.ui.swt.views.table.CoreTableColumnSWT; import com.aelitis.azureus.ui.common.table.TableCellCore; public class FileHashItemBase extends CoreTableColumnSWT implements TableCellRefreshListener, TableCellMouseListener { protected static final String HT_CRC32 = "crc32"; protected static final String HT_MD5 = "md5"; protected static final String HT_SHA1 = "sha1"; final String hash_type; final TableContextMenuItem menuItem; public FileHashItemBase( String _hash_type, int width ) { super( _hash_type, ALIGN_LEAD, POSITION_INVISIBLE, width, TableManager.TABLE_TORRENT_FILES); hash_type = _hash_type; setType( TableColumn.TYPE_TEXT ); setRefreshInterval( INTERVAL_LIVE ); menuItem = addContextMenuItem( "FilesView." + hash_type + ".calculate" ); menuItem.setStyle( MenuItem.STYLE_PUSH ); menuItem.addMultiListener( new MenuItemListener() { public void selected( MenuItem menu, Object target) { Object[] files = (Object[])target; for ( Object _file: files ){ DiskManagerFileInfo file = (DiskManagerFileInfo)_file; updateHash( hash_type, file ); } } }); } public void fillTableColumnInfo( TableColumnInfo info) { info.addCategories(new String[] { CAT_CONTENT, }); info.setProficiency(TableColumnInfo.PROFICIENCY_ADVANCED ); } public void cellMouseTrigger( TableCellMouseEvent event) { DiskManagerFileInfo file = (DiskManagerFileInfo) event.cell.getDataSource(); if ( file == null ){ return; } TableCellCore core_cell = (TableCellCore)event.cell; if ( !event.cell.getText().startsWith( "<" )){ core_cell.setCursorID( SWT.CURSOR_ARROW ); core_cell.setToolTip( null ); return; } if (event.eventType == TableRowMouseEvent.EVENT_MOUSEENTER){ core_cell.setCursorID( SWT.CURSOR_HAND ); core_cell.setToolTip( MessageText.getString( "FilesView.click.info" ) ); }else if (event.eventType == TableRowMouseEvent.EVENT_MOUSEEXIT ){ core_cell.setCursorID( SWT.CURSOR_ARROW ); core_cell.setToolTip( null ); } if ( event.eventType != TableCellMouseEvent.EVENT_MOUSEUP ){ return; } // Only activate on LMB. if ( event.button != 1 ){ return; } event.skipCoreFunctionality = true; updateHash( hash_type, file ); } public void refresh( TableCell cell) { DiskManagerFileInfo file = (DiskManagerFileInfo)cell.getDataSource(); if ( file == null ){ return; } cell.setText( getHash( hash_type, file )); } private static AsyncDispatcher dispatcher = new AsyncDispatcher(); private static Map<DiskManagerFileInfo,Set<String>> pending = new HashMap<DiskManagerFileInfo,Set<String>>(); private static volatile DiskManagerFileInfo active; private static volatile String active_hash; private static volatile int active_percent; private static boolean isFileReady( DiskManagerFileInfo file ) { if ( file == null || file.getLength() != file.getDownloaded() || file.getAccessMode() != DiskManagerFileInfo.READ ){ return( false ); } File f = file.getFile( true ); if ( f.length() != file.getLength() || !f.canRead()){ return( false ); } return( true ); } private static void updateHash( final String hash_type, final DiskManagerFileInfo file ) { if ( !isFileReady( file )){ return; } synchronized( pending ){ Set<String> hashes = pending.get( file ); if ( hashes != null && hashes.contains( hash_type )){ return; } if ( hashes == null ){ hashes = new HashSet<String>(); pending.put( file, hashes ); } hashes.add( hash_type ); } dispatcher.dispatch( new AERunnable() { public void runSupport() { try{ DownloadManager dm = file.getDownloadManager(); if ( dm == null ){ return; } if ( !isFileReady( file )){ return; } active_percent = 0; active_hash = hash_type; active = file; File f = file.getFile( true ); CRC32 crc32 = null; MessageDigest md = null; if ( hash_type == HT_CRC32 ){ crc32 = new CRC32(); }else if ( hash_type == HT_MD5 ){ md = MessageDigest.getInstance( "md5" ); }else{ md = MessageDigest.getInstance( "SHA1" ); } FileInputStream fis = new FileInputStream( f ); long size = f.length(); long done = 0; if ( size == 0 ){ size = 1; } try{ byte[] buffer = new byte[512*1024]; while( true ){ int len = fis.read( buffer ); if ( len <= 0 ){ break; } if ( crc32 != null ){ crc32.update( buffer, 0, len ); } if ( md != null ){ md.update( buffer, 0, len ); } done += len; active_percent = (int)(( 1000*done) / size ); } byte[] hash; if ( crc32 != null ){ long val = crc32.getValue(); hash = ByteFormatter.intToByteArray( val ); }else{ hash = md.digest(); } Map other_hashes = dm.getDownloadState().getMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES ); if ( other_hashes == null ){ other_hashes = new HashMap(); }else{ other_hashes = BEncoder.cloneMap( other_hashes ); } Map file_hashes = (Map)other_hashes.get( String.valueOf( file.getIndex())); if ( file_hashes == null ){ file_hashes = new HashMap(); other_hashes.put( String.valueOf( file.getIndex()), file_hashes ); } file_hashes.put( hash_type, hash ); dm.getDownloadState().setMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES, other_hashes ); }finally{ fis.close(); } }catch( Throwable e ){ Debug.out( e ); }finally{ synchronized( pending ){ Set<String> hashes = pending.get( file ); hashes.remove( hash_type ); if ( hashes.size() == 0 ){ pending.remove( file ); } active = null; } } } }); } private static String getHash( String hash_type, DiskManagerFileInfo file ) { if ( file == null ){ return( "" ); } DownloadManager dm = file.getDownloadManager(); if ( dm == null ){ return( "" ); } synchronized( pending ){ Set<String> hashes = pending.get( file ); if ( hashes != null && hashes.contains( hash_type )){ if ( active == file && active_hash == hash_type ){ return( DisplayFormatters.formatPercentFromThousands( active_percent )); }else{ return( "..." ); } } } Map other_hashes = dm.getDownloadState().getMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES ); if ( other_hashes != null ){ Map file_hashes = (Map)other_hashes.get( String.valueOf( file.getIndex())); if ( file_hashes != null ){ byte[] hash = (byte[])file_hashes.get( hash_type ); if ( hash != null ){ return( ByteFormatter.encodeString( hash ).toLowerCase()); } } } if ( !isFileReady( file )){ return( "" ); } return( "<" + MessageText.getString( "FilesView.click" ) + ">" ); } }