1 /* 2 * File : NameItem.java 3 * Created : 24 nov. 2003 4 * By : Olivier 5 * 6 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details ( see the LICENSE file ). 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 package org.gudy.azureus2.ui.swt.views.tableitems.files; 24 25 import java.io.File; 26 import java.io.FileInputStream; 27 import java.security.MessageDigest; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.Map; 31 import java.util.Set; 32 import java.util.zip.CRC32; 33 34 import org.eclipse.swt.SWT; 35 36 import org.gudy.azureus2.core3.disk.DiskManagerFileInfo; 37 import org.gudy.azureus2.core3.download.DownloadManager; 38 import org.gudy.azureus2.core3.download.DownloadManagerState; 39 import org.gudy.azureus2.core3.internat.MessageText; 40 import org.gudy.azureus2.core3.util.AERunnable; 41 import org.gudy.azureus2.core3.util.AsyncDispatcher; 42 import org.gudy.azureus2.core3.util.BEncoder; 43 import org.gudy.azureus2.core3.util.ByteFormatter; 44 import org.gudy.azureus2.core3.util.Debug; 45 import org.gudy.azureus2.core3.util.DisplayFormatters; 46 import org.gudy.azureus2.plugins.ui.menus.MenuItem; 47 import org.gudy.azureus2.plugins.ui.menus.MenuItemListener; 48 import org.gudy.azureus2.plugins.ui.tables.*; 49 import org.gudy.azureus2.ui.swt.views.table.CoreTableColumnSWT; 50 51 import com.aelitis.azureus.ui.common.table.TableCellCore; 52 53 54 public class 55 FileHashItemBase 56 extends CoreTableColumnSWT 57 implements TableCellRefreshListener, TableCellMouseListener 58 { 59 protected static final String HT_CRC32 = "crc32"; 60 protected static final String HT_MD5 = "md5"; 61 protected static final String HT_SHA1 = "sha1"; 62 63 final String hash_type; 64 final TableContextMenuItem menuItem; 65 66 public FileHashItemBase( String _hash_type, int width )67 FileHashItemBase( 68 String _hash_type, 69 int width ) 70 { 71 super( _hash_type, ALIGN_LEAD, POSITION_INVISIBLE, width, TableManager.TABLE_TORRENT_FILES); 72 73 hash_type = _hash_type; 74 75 setType( TableColumn.TYPE_TEXT ); 76 77 setRefreshInterval( INTERVAL_LIVE ); 78 79 menuItem = addContextMenuItem( "FilesView." + hash_type + ".calculate" ); 80 81 menuItem.setStyle( MenuItem.STYLE_PUSH ); 82 83 menuItem.addMultiListener( 84 new MenuItemListener() 85 { 86 public void 87 selected( 88 MenuItem menu, Object target) 89 { 90 Object[] files = (Object[])target; 91 92 for ( Object _file: files ){ 93 94 if (_file instanceof TableRow) { 95 _file = ((TableRow)_file).getDataSource(); 96 } 97 DiskManagerFileInfo file = (DiskManagerFileInfo)_file; 98 99 updateHash( hash_type, file ); 100 } 101 } 102 }); 103 } 104 105 public void fillTableColumnInfo( TableColumnInfo info)106 fillTableColumnInfo( 107 TableColumnInfo info) 108 { 109 info.addCategories(new String[] { 110 CAT_CONTENT, 111 }); 112 info.setProficiency(TableColumnInfo.PROFICIENCY_ADVANCED ); 113 } 114 115 public void cellMouseTrigger( TableCellMouseEvent event)116 cellMouseTrigger( 117 TableCellMouseEvent event) 118 { 119 DiskManagerFileInfo file = (DiskManagerFileInfo) event.cell.getDataSource(); 120 121 if ( file == null ){ 122 123 return; 124 } 125 126 TableCellCore core_cell = (TableCellCore)event.cell; 127 128 if ( !event.cell.getText().startsWith( "<" )){ 129 130 core_cell.setCursorID( SWT.CURSOR_ARROW ); 131 core_cell.setToolTip( null ); 132 133 return; 134 } 135 136 if (event.eventType == TableRowMouseEvent.EVENT_MOUSEENTER){ 137 138 core_cell.setCursorID( SWT.CURSOR_HAND ); 139 core_cell.setToolTip( MessageText.getString( "FilesView.click.info" ) ); 140 141 }else if (event.eventType == TableRowMouseEvent.EVENT_MOUSEEXIT ){ 142 143 core_cell.setCursorID( SWT.CURSOR_ARROW ); 144 core_cell.setToolTip( null ); 145 } 146 147 if ( event.eventType != TableCellMouseEvent.EVENT_MOUSEUP ){ 148 149 return; 150 } 151 152 // Only activate on LMB. 153 154 if ( event.button != 1 ){ 155 156 return; 157 } 158 159 event.skipCoreFunctionality = true; 160 161 updateHash( hash_type, file ); 162 } 163 164 public void refresh( TableCell cell)165 refresh( 166 TableCell cell) 167 { 168 DiskManagerFileInfo file = (DiskManagerFileInfo)cell.getDataSource(); 169 170 if ( file == null ){ 171 172 return; 173 } 174 175 cell.setText( getHash( hash_type, file )); 176 } 177 178 179 private static AsyncDispatcher dispatcher = new AsyncDispatcher(); 180 181 private static Map<DiskManagerFileInfo,Set<String>> pending = new HashMap<DiskManagerFileInfo,Set<String>>(); 182 183 private static volatile DiskManagerFileInfo active; 184 private static volatile String active_hash; 185 private static volatile int active_percent; 186 187 private static boolean isFileReady( DiskManagerFileInfo file )188 isFileReady( 189 DiskManagerFileInfo file ) 190 { 191 if ( file == null || 192 file.getLength() != file.getDownloaded() || 193 file.getAccessMode() != DiskManagerFileInfo.READ ){ 194 195 return( false ); 196 } 197 198 File f = file.getFile( true ); 199 200 if ( f.length() != file.getLength() || !f.canRead()){ 201 202 return( false ); 203 } 204 205 return( true ); 206 } 207 208 private static void updateHash( final String hash_type, final DiskManagerFileInfo file )209 updateHash( 210 final String hash_type, 211 final DiskManagerFileInfo file ) 212 { 213 if ( !isFileReady( file )){ 214 215 return; 216 } 217 218 synchronized( pending ){ 219 220 Set<String> hashes = pending.get( file ); 221 222 if ( hashes != null && hashes.contains( hash_type )){ 223 224 return; 225 } 226 227 if ( hashes == null ){ 228 229 hashes = new HashSet<String>(); 230 231 pending.put( file, hashes ); 232 } 233 234 hashes.add( hash_type ); 235 } 236 237 dispatcher.dispatch( 238 new AERunnable() 239 { 240 public void 241 runSupport() 242 { 243 try{ 244 DownloadManager dm = file.getDownloadManager(); 245 246 if ( dm == null ){ 247 248 return; 249 } 250 251 if ( !isFileReady( file )){ 252 253 return; 254 } 255 256 active_percent = 0; 257 active_hash = hash_type; 258 active = file; 259 260 File f = file.getFile( true ); 261 262 CRC32 crc32 = null; 263 MessageDigest md = null; 264 265 if ( hash_type == HT_CRC32 ){ 266 267 crc32 = new CRC32(); 268 269 }else if ( hash_type == HT_MD5 ){ 270 271 md = MessageDigest.getInstance( "md5" ); 272 273 }else{ 274 275 md = MessageDigest.getInstance( "SHA1" ); 276 277 } 278 279 FileInputStream fis = new FileInputStream( f ); 280 281 long size = f.length(); 282 long done = 0; 283 284 if ( size == 0 ){ 285 286 size = 1; 287 } 288 289 try{ 290 byte[] buffer = new byte[512*1024]; 291 292 while( true ){ 293 294 int len = fis.read( buffer ); 295 296 if ( len <= 0 ){ 297 298 break; 299 } 300 301 if ( crc32 != null ){ 302 303 crc32.update( buffer, 0, len ); 304 } 305 306 if ( md != null ){ 307 308 md.update( buffer, 0, len ); 309 } 310 311 done += len; 312 313 active_percent = (int)(( 1000*done) / size ); 314 } 315 316 byte[] hash; 317 318 if ( crc32 != null ){ 319 320 long val = crc32.getValue(); 321 322 hash = ByteFormatter.intToByteArray( val ); 323 324 }else{ 325 326 hash = md.digest(); 327 } 328 329 Map other_hashes = dm.getDownloadState().getMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES ); 330 331 if ( other_hashes == null ){ 332 333 other_hashes = new HashMap(); 334 335 }else{ 336 337 other_hashes = BEncoder.cloneMap( other_hashes ); 338 } 339 340 Map file_hashes = (Map)other_hashes.get( String.valueOf( file.getIndex())); 341 342 if ( file_hashes == null ){ 343 344 file_hashes = new HashMap(); 345 346 other_hashes.put( String.valueOf( file.getIndex()), file_hashes ); 347 } 348 349 file_hashes.put( hash_type, hash ); 350 351 dm.getDownloadState().setMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES, other_hashes ); 352 353 }finally{ 354 355 fis.close(); 356 } 357 }catch( Throwable e ){ 358 359 Debug.out( e ); 360 361 }finally{ 362 363 synchronized( pending ){ 364 365 Set<String> hashes = pending.get( file ); 366 367 hashes.remove( hash_type ); 368 369 if ( hashes.size() == 0 ){ 370 371 pending.remove( file ); 372 } 373 374 active = null; 375 } 376 } 377 } 378 }); 379 } 380 381 private static String getHash( String hash_type, DiskManagerFileInfo file )382 getHash( 383 String hash_type, 384 DiskManagerFileInfo file ) 385 { 386 if ( file == null ){ 387 388 return( "" ); 389 } 390 391 DownloadManager dm = file.getDownloadManager(); 392 393 if ( dm == null ){ 394 395 return( "" ); 396 } 397 398 synchronized( pending ){ 399 400 Set<String> hashes = pending.get( file ); 401 402 if ( hashes != null && hashes.contains( hash_type )){ 403 404 if ( active == file && active_hash == hash_type ){ 405 406 return( DisplayFormatters.formatPercentFromThousands( active_percent )); 407 408 }else{ 409 410 return( "..." ); 411 } 412 } 413 } 414 415 Map other_hashes = dm.getDownloadState().getMapAttribute( DownloadManagerState.AT_FILE_OTHER_HASHES ); 416 417 if ( other_hashes != null ){ 418 419 Map file_hashes = (Map)other_hashes.get( String.valueOf( file.getIndex())); 420 421 if ( file_hashes != null ){ 422 423 byte[] hash = (byte[])file_hashes.get( hash_type ); 424 425 if ( hash != null ){ 426 427 return( ByteFormatter.encodeString( hash ).toLowerCase()); 428 } 429 } 430 } 431 432 if ( !isFileReady( file )){ 433 434 return( "" ); 435 } 436 437 return( "<" + MessageText.getString( "FilesView.click" ) + ">" ); 438 } 439 440 } 441