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