source: main/trunk/gli/src/org/greenstone/gatherer/file/FileQueue.java@ 36272

Last change on this file since 36272 was 34289, checked in by ak19, 4 years ago

Some comments for the previous commit.

  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * Author: John Thompson, Greenstone Digital Library, University of Waikato
9 *
10 * Copyright (C) 1999 New Zealand Digital Library Project
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *########################################################################
26 */
27package org.greenstone.gatherer.file;
28
29import java.io.*;
30import java.util.*;
31import javax.swing.*;
32import javax.swing.event.*;
33import javax.swing.tree.*;
34import org.greenstone.gatherer.Configuration;
35import org.greenstone.gatherer.DebugStream;
36import org.greenstone.gatherer.Dictionary;
37import org.greenstone.gatherer.Gatherer;
38import org.greenstone.gatherer.collection.CollectionTreeNode;
39import org.greenstone.gatherer.gui.GProgressBar;
40import org.greenstone.gatherer.gui.tree.DragTree;
41import org.greenstone.gatherer.metadata.MetadataValue;
42import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
43import org.greenstone.gatherer.util.DragComponent;
44import org.greenstone.gatherer.util.StaticStrings;
45import org.greenstone.gatherer.util.SynchronizedTreeModelTools;
46import org.greenstone.gatherer.util.Utility;
47
48/** A threaded object which processes a queue of file actions such as copying and movement. It also handles updating the various trees involved so they are an accurate representation of the file system they are meant to match.
49 * @author John Thompson, Greenstone Digital Library, University of Waikato
50 * @version 2.3
51 */
52public class FileQueue
53 extends Thread
54{
55 /** The size of the io buffer, in bytes. */
56 static final private int BUFFER_SIZE = 1024;
57
58 /** When someone requests the movement queue to be dumped this cancel flag is set to true. */
59 private boolean cancel_action = false;
60 /** The button which controls the stopping of the file queue. */
61 private JButton stop_button = null;
62 /** true if the user has selected yes to all from a file 'clash' dialog. */
63 private boolean yes_to_all = false;
64 /** A label explaining the current moving files status. */
65 private JLabel file_status = null;
66 /** A list containing a queue of waiting movement jobs. */
67 private ArrayList queue = null;
68 /** A progress bar which shows how many bytes, out of the total size of bytes, has been moved. */
69 private GProgressBar progress = null;
70
71 /** The objects listening for FileCopiedSuccess events. */
72 private ArrayList file_copied_success_listeners = new ArrayList();
73
74 /** Constructor.
75 */
76 public FileQueue() {
77 DebugStream.println("FileQueue started.");
78 this.queue = new ArrayList();
79 file_status = new JLabel(Dictionary.get("FileActions.No_Activity"));
80 progress = new GProgressBar();
81 progress.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
82 progress.setForeground(Configuration.getColor("coloring.collection_tree_foreground", false));
83 progress.setString(Dictionary.get("FileActions.No_Activity"));
84 progress.setStringPainted(true);
85 }
86
87 /** There are tasks that depend on whether a file copy was succesful, so need to maintain listeners
88 * and need to fire events on successful copy. */
89 public void fireFileCopiedSuccessfully(File new_file) {
90 // Send the event off to all the matching Listeners
91 for (int i = 0; i < file_copied_success_listeners.size(); i++) {
92 ((FileCopiedSuccessListener) file_copied_success_listeners.get(i)).fileCopiedSuccessfully(new_file);
93 }
94 }
95 public void addFileCopiedSuccessListener(FileCopiedSuccessListener listener) {
96 file_copied_success_listeners.add(listener);
97 }
98 public void removeFileCopiedSuccessListener(FileCopiedSuccessListener listener) {
99 file_copied_success_listeners.remove(listener);
100 }
101
102 /** Add a new job to the queue, specifiying as many arguments as is necessary to complete this type of job (ie delete needs no target information).
103 * @param id A long id unique to all jobs created by a single action.
104 * @param source The DragComponent source of this file, most likely a DragTree.
105 * @param child The FileNode you wish to mode.
106 * @param target The DragComponent to move the file to, again most likely a DragTree.
107 * @param parent The files new FileNode parent within the target.
108 * @param type The type of this movement as an int, either COPY or DELETE.
109 */
110 public void addJob(long id, DragComponent source, FileNode[] children, DragComponent target, FileNode parent, byte type)
111 {
112 // Queue the sub-job(s) (this may fail if we are asked to delete a read only file)
113 for (int i = 0; i < children.length; i++) {
114 addJob(id, source, children[i], target, parent, type, -1);
115 }
116 }
117
118 synchronized private void addJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type, int position) {
119 FileJob job = new FileJob(id, source, child, target, parent, type);
120 DebugStream.println("Adding job: " + job);
121 if(position != -1 && position <= queue.size() + 1) {
122 queue.add(position, job);
123 }
124 else {
125 queue.add(job);
126 }
127 notify();
128 }
129
130 /** Calculates the total deep file size of the selected file nodes.
131 * @param files a FileNode[] of selected files
132 * @return true if a cancel was signalled, false otherwise
133 * @see org.greenstone.gatherer.file.FileManager.Task#run()
134 */
135 public boolean calculateSize(FileNode[] files)
136 {
137 file_status.setText(Dictionary.get("FileActions.Calculating_Size"));
138 progress.setString(Dictionary.get("FileActions.Calculating_Size"));
139
140 // Calculate the total file size of all the selected file nodes
141 Vector remaining = new Vector();
142 for (int i = 0; !cancel_action && i < files.length; i++) {
143 remaining.add(files[i]);
144 }
145 while (!cancel_action && remaining.size() > 0) {
146 FileNode node = (FileNode) remaining.remove(0);
147 if (node.isLeaf()) {
148 progress.addMaximum(node.getFile().length());
149 }
150 else {
151 for (int i = 0; !cancel_action && i < node.getChildCount(); i++) {
152 remaining.add(node.getChildAt(i));
153 }
154 }
155 }
156
157 // Now we return if calculation was cancelled so that the FileManagers Task can skip the addJob phase correctly.
158 if (cancel_action) {
159 cancel_action = false; // reset
160 return true;
161 }
162 else {
163 return false;
164 }
165 }
166
167 /** This method is called to cancel the job queue at the next available moment. */
168 public void cancelAction() {
169 cancel_action = true;
170 clearJobs();
171 }
172
173
174 /** Format the given filename path string so that it is no longer than the given width. If it is wider replace starting directories with ...
175 * @param key The key <strong>String</Strong> used to retrieve a phrase from the dictionary for this item.
176 * @param raw The raw filename path <strong>String</strong>.
177 * @param width The maximum width as an <i>int</i>.
178 * @return A path <strong>String</strong> no longer than width.
179 */
180 String formatPath(String key, String raw, int width) // package access
181 {
182 JLabel label = new JLabel(Dictionary.get(key, raw));
183 int position = -1;
184 while(label.getPreferredSize().width > width && (position = raw.indexOf(File.separator)) != -1) {
185 raw = "..." + raw.substring(position + 1);
186 label.setText(Dictionary.get(key, raw));
187 }
188 if(raw.indexOf(File.separator) == -1 && raw.startsWith("...")) {
189 raw = raw.substring(3);
190 }
191 return raw;
192 }
193
194
195 /** Access to the file state label. */
196 public JLabel getFileStatus() {
197 return file_status;
198 }
199
200 /** Access to the progress bar. */
201 public GProgressBar getProgressBar() {
202 return progress;
203 }
204
205
206 synchronized private void addFileJob(long id, DragComponent source, FileNode child, DragComponent target, FileNode parent, byte type)
207 {
208 queue.add(new FileJob(id, source, child, target, parent, type));
209 notify();
210 }
211
212
213 private void doEmptyDirectoryDelete(FileJob file_job)
214 {
215 FileNode source_node = file_job.getOrigin();
216 File source_directory = source_node.getFile();
217
218 // If the directory isn't empty then this will fail
219 if (source_directory.delete() == false) {
220 // The source directory couldn't be deleted, so give the user the option of continuing or cancelling
221 if (showErrorDialog(Dictionary.get("FileActions.Could_Not_Delete", source_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
222 clearJobs(); // Aborting action
223 }
224 return;
225 }
226
227 // Remove the node from the model
228 SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
229 }
230
231
232 private void doDirectoryDelete(FileJob file_job)
233 {
234 FileNode source_node = file_job.getOrigin();
235 File source_directory = source_node.getFile();
236
237 // The last thing we will do is delete this directory (which should be empty by then)
238 addFileJob(file_job.ID(), file_job.source, source_node, null, null, FileJob.DELETE_EMPTY_DIRECTORY);
239
240 // Add a new Delete job for each child of this directory (except metadata.xml files)
241 source_node.refresh();
242 for (int i = 0; i < source_node.size(); i++) {
243 FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
244 if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
245 addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
246 }
247 }
248
249 // Treat metadata.xml files specially: delete them first
250 for (int i = 0; i < source_node.size(); i++) {
251 FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
252 if (child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
253 addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
254 break;
255 }
256 }
257 }
258
259
260 private void doDirectoryCopy(FileJob file_job)
261 {
262 FileNode source_node = file_job.getOrigin();
263 FileNode target_node = file_job.getDestination();
264
265 File source_directory = source_node.getFile();
266 File target_directory = new File(target_node.getFile(), source_directory.getName());
267
268 // Check that the source directory doesn't contain the target directory (will create a cyclic loop)
269 if (target_directory.getAbsolutePath().startsWith(source_directory.getAbsolutePath())) {
270 if (showErrorDialog(Dictionary.get("FileActions.Cyclic_Path", source_directory.getName())) == JOptionPane.CANCEL_OPTION) {
271 clearJobs(); // Aborting action
272 }
273 return;
274 }
275
276 // The target directory shouldn't already exist
277 if (target_directory.exists()) {
278 if (showErrorDialog(Dictionary.get("FileActions.Folder_Already_Exists", target_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
279 clearJobs(); // Aborting action
280 }
281 return;
282 }
283 target_directory.mkdirs();
284
285 // Create a node for the new directory in the collection tree
286 FileSystemModel target_model = (FileSystemModel) file_job.target.getTreeModel();
287 CollectionTreeNode new_target_node = new CollectionTreeNode(target_directory);
288 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
289 new_target_node.setParent(target_node);
290
291 // Copy the non-folder level metadata assigned to the original directory to the new directory
292 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToExternalFile(source_directory);
293 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
294
295 // Add a new Copy job for each child of this directory (except metadata.xml files)
296 source_node.refresh();
297 for (int i = 0; i < source_node.size(); i++) {
298 FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
299 if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
300 addFileJob(file_job.ID(), file_job.source, child_file_node, file_job.target, new_target_node, FileJob.COPY);
301 }
302 }
303 }
304
305
306 private void doDirectoryMove(FileJob file_job)
307 {
308 FileNode source_node = file_job.getOrigin();
309 FileNode target_node = file_job.getDestination();
310
311 File source_directory = source_node.getFile();
312 File target_directory = new File(target_node.getFile(), source_directory.getName());
313 if (file_job.type == FileJob.RENAME) {
314 // This is the only difference between moves and renames
315 target_directory = target_node.getFile();
316 target_node = (FileNode) source_node.getParent();
317 }
318
319 // Check the target directory isn't the source directory
320 if (target_directory.equals(source_directory)) {
321 DebugStream.println("Target directory is the source directory!");
322 return;
323 }
324
325 // The target directory shouldn't already exist
326 if (target_directory.exists()) {
327 if (showErrorDialog(Dictionary.get("FileActions.Folder_Already_Exists", target_directory.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
328 clearJobs(); // Aborting action
329 }
330 return;
331 }
332 target_directory.mkdirs();
333
334 // Create a node for the new directory in the collection tree
335 FileSystemModel target_model = (FileSystemModel) file_job.target.getTreeModel();
336 CollectionTreeNode new_target_node = new CollectionTreeNode(target_directory);
337 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
338 new_target_node.setParent(target_node);
339
340 // Move the folder level metadata assigned to the original directory to the new directory
341 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_directory);
342 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
343 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
344
345 // The last thing we will do is delete this directory
346 addFileJob(file_job.ID(), file_job.source, source_node, null, null, FileJob.DELETE);
347
348 // Treat metadata.xml files specially: delete them last
349 source_node.refresh();
350 for (int i = 0; i < source_node.size(); i++) {
351 FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
352 if (child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
353 addFileJob(file_job.ID(), file_job.source, child_file_node, null, null, FileJob.DELETE);
354 break;
355 }
356 }
357
358 // Add a new Move job for each child of this directory (except metadata.xml files)
359 for (int i = 0; i < source_node.size(); i++) {
360 FileNode child_file_node = (FileNode) source_node.getChildAtUnfiltered(i);
361 if (!child_file_node.getFile().getName().equals(StaticStrings.METADATA_XML)) {
362 addFileJob(file_job.ID(), file_job.source, child_file_node, file_job.target, new_target_node, FileJob.MOVE);
363 }
364 }
365 }
366
367
368 private void doFileDelete(FileJob file_job)
369 {
370 FileNode source_node = file_job.getOrigin();
371 File source_file = source_node.getFile();
372
373 // Almost all files will be deleted from the collection tree (exception: files in "Downloaded Files")
374 if (source_node instanceof CollectionTreeNode) {
375 // If we're deleting a metadata.xml file we must unload it
376 boolean metadata_xml_file = source_file.getName().equals(StaticStrings.METADATA_XML);
377 if (metadata_xml_file) {
378 MetadataXMLFileManager.unloadMetadataXMLFile(source_file);
379 }
380 // Otherwise remove any metadata assigned directly to the file
381 else {
382 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
383 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
384 }
385 }
386
387 // Delete the source file
388 if (!Utility.delete(source_file)) {
389 // The source file couldn't be deleted, so give the user the option of continuing or cancelling
390 if (showErrorDialog(Dictionary.get("FileActions.File_Not_Deleted_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
391 clearJobs(); // Aborting action
392 }
393 return;
394 }
395
396 // Remove the node from the model
397 SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
398 }
399
400
401 private void doFileCopy(FileJob file_job)
402 {
403 FileNode source_node = file_job.getOrigin();
404 FileNode target_node = file_job.getDestination();
405
406 File source_file = source_node.getFile();
407 File target_file = new File(target_node.getFile(), source_file.getName());
408
409 // The target file shouldn't already exist -- if it does ask the user whether they want to overwrite
410 boolean overwrite_file = false;
411 if (target_file.exists()) {
412 int result = showOverwriteDialog(target_file.getName());
413 if (result == JOptionPane.NO_OPTION) {
414 // Don't overwrite
415 return;
416 }
417 if (result == JOptionPane.CANCEL_OPTION) {
418 clearJobs(); // Aborting action
419 return;
420 }
421
422 overwrite_file = true;
423 }
424
425 // Copy the file
426 try {
427 copyFile(source_file, target_file, true);
428 // let any listeners know that the copy wasn't cancelled and took place without error
429 fireFileCopiedSuccessfully(source_file);
430 }
431 catch (FileAlreadyExistsException exception) {
432 // This should not ever happen, since we've called copyFile with overwrite set
433 DebugStream.printStackTrace(exception);
434 return;
435 }
436 catch (FileNotFoundException exception) {
437 DebugStream.printStackTrace(exception);
438 if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
439 clearJobs(); // Aborting action
440 }
441 // Refresh the source tree model
442 FileSystemModel source_model = file_job.source.getTreeModel();
443 source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
444 return;
445 }
446 catch (InsufficientSpaceException exception) {
447 DebugStream.printStackTrace(exception);
448 if (showErrorDialog(Dictionary.get("FileActions.Insufficient_Space_Message", exception.getMessage())) == JOptionPane.CANCEL_OPTION) {
449 clearJobs(); // Aborting action
450 }
451 return;
452 }
453 catch (IOException exception) {
454 DebugStream.printStackTrace(exception);
455 if (showErrorDialog(exception.getMessage()) == JOptionPane.CANCEL_OPTION) {
456 clearJobs(); // Aborting action
457 }
458 return;
459 }
460 catch (ReadNotPermittedException exception) {
461 DebugStream.printStackTrace(exception);
462 if (showErrorDialog(Dictionary.get("FileActions.Read_Not_Permitted_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
463 clearJobs(); // Aborting action
464 }
465 return;
466 }
467 catch (UnknownFileErrorException exception) {
468 DebugStream.printStackTrace(exception);
469 if (showErrorDialog(Dictionary.get("FileActions.Unknown_File_Error_Message")) == JOptionPane.CANCEL_OPTION) {
470 clearJobs(); // Aborting action
471 }
472 return;
473 }
474 catch (WriteNotPermittedException exception) {
475 DebugStream.printStackTrace(exception);
476 if (showErrorDialog(Dictionary.get("FileActions.Write_Not_Permitted_Message", target_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
477 clearJobs(); // Aborting action
478 }
479 return;
480 }
481
482 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
483 if (overwrite_file == false) {
484 // Add the new node into the tree
485 FileSystemModel target_model = file_job.target.getTreeModel();
486 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
487 }
488 Gatherer.c_man.fireFileAddedToCollection(target_file);
489
490 // Copy the non-folder level metadata assigned to the original file to the new file
491 if (file_job.type == FileJob.COPY) {
492 // do metadata too
493 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToExternalFile(source_file);
494 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
495 }
496 }
497
498
499 private void doFileMove(FileJob file_job)
500 {
501 FileNode source_node = file_job.getOrigin();
502 FileNode target_node = file_job.getDestination();
503
504 File source_file = source_node.getFile();
505 File target_file = new File(target_node.getFile(), source_file.getName());
506 if (file_job.type == FileJob.RENAME) {
507 // This is the only difference between moves and renames
508 target_file = target_node.getFile();
509 target_node = (FileNode) source_node.getParent();
510 }
511
512 // Check the target file isn't the source file
513 if (target_file.equals(source_file)) {
514 DebugStream.println("Target file is the source file!");
515 return;
516 }
517
518 // The target file shouldn't already exist
519 if (target_file.exists()) {
520 int result = showOverwriteDialog(target_file.getName());
521 if (result == JOptionPane.NO_OPTION) {
522 // Don't overwrite
523 return;
524 }
525 if (result == JOptionPane.CANCEL_OPTION) {
526 clearJobs(); // Aborting action
527 return;
528 }
529 }
530
531 // Move the file by renaming it
532 if (!source_file.renameTo(target_file)) {
533 String args[] = { source_file.getName(), target_file.getAbsolutePath() };
534 if (showErrorDialog(Dictionary.get("FileActions.File_Move_Error_Message", args)) == JOptionPane.CANCEL_OPTION) {
535 clearJobs(); // Aborting action
536 }
537 return;
538 }
539
540 // Remove the node from the source model and add it to the target model
541 SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
542 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
543 FileSystemModel target_model = file_job.target.getTreeModel();
544 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
545
546 // Move the non-folder level metadata assigned to the original file to the new file
547 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
548 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
549 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
550 }
551
552
553 /** all this does is move the metadata, and delete the source */
554 private void doFileReplace(FileJob file_job)
555 {
556 FileNode source_node = file_job.getOrigin();
557 FileNode target_node = file_job.getDestination();
558
559 File source_file = source_node.getFile();
560 File target_file = target_node.getFile();
561
562 // Move the non-folder level metadata assigned to the original file to the new file
563 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
564 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
565 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
566 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
567
568 // now delete the original
569 doFileDelete(file_job);
570 }
571
572
573 private void processFileJob(FileJob file_job)
574 {
575 DebugStream.println("Processing file job " + file_job + "...");
576
577 // Ensure that the source file exists
578 File source_file = file_job.getOrigin().getFile();
579 if (!source_file.exists()) {
580 // The source file doesn't exist, so give the user the option of continuing or cancelling
581 if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
582 clearJobs(); // Aborting action
583 }
584 // Refresh the source tree model
585 FileSystemModel source_model = file_job.source.getTreeModel();
586 source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
587 return;
588 }
589
590 // Enable the "Stop" button
591 stop_button.setEnabled(true);
592
593 // Delete empty directory job
594 if (file_job.type == FileJob.DELETE_EMPTY_DIRECTORY) {
595 file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
596 doEmptyDirectoryDelete(file_job);
597 return;
598 }
599
600 // Delete job
601 if (file_job.type == FileJob.DELETE) {
602 file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
603 if (source_file.isFile()) {
604 long source_file_size = source_file.length();
605 doFileDelete(file_job);
606 progress.addValue(source_file_size); // Update progress bar
607 }
608 else {
609 doDirectoryDelete(file_job);
610 }
611 return;
612 }
613
614 // Copy job
615 if (file_job.type == FileJob.COPY || file_job.type == FileJob.COPY_FILE_ONLY) {
616 file_status.setText(Dictionary.get("FileActions.Copying", formatPath("FileActions.Copying", source_file.getAbsolutePath(), file_status.getSize().width)));
617 if (source_file.isFile()) {
618 long source_file_size = source_file.length();
619 doFileCopy(file_job);
620 progress.addValue(source_file_size); // Update progress bar
621 }
622 else {
623 doDirectoryCopy(file_job);
624 }
625 return;
626 }
627
628 // Move (or rename) job
629 if (file_job.type == FileJob.MOVE || file_job.type == FileJob.RENAME) {
630 file_status.setText(Dictionary.get("FileActions.Moving", formatPath("FileActions.Moving", source_file.getAbsolutePath(), file_status.getSize().width)));
631 if (source_file.isFile()) {
632 long source_file_size = source_file.length();
633 doFileMove(file_job);
634 progress.addValue(source_file_size); // Update progress bar
635 }
636 else {
637 doDirectoryMove(file_job);
638 }
639 return;
640 }
641
642 // Replace job
643 if (file_job.type == FileJob.REPLACE) {
644 file_status.setText(Dictionary.get("FileActions.Replacing", formatPath("FileActions.Replacing", source_file.getAbsolutePath(), file_status.getSize().width)));
645 doFileReplace(file_job);
646 return;
647 }
648 }
649
650
651 private int showErrorDialog(String error_message)
652 {
653 Object[] options = { Dictionary.get("General.OK"), Dictionary.get("General.Cancel") };
654 int result = JOptionPane.showOptionDialog(Gatherer.g_man, error_message, Dictionary.get("General.Error"), JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]);
655 if (result == 0) {
656 return JOptionPane.OK_OPTION;
657 }
658 else {
659 return JOptionPane.CANCEL_OPTION;
660 }
661 }
662
663
664 private int showOverwriteDialog(String target_file_name)
665 {
666 // Has "yes to all" been set?
667 if (yes_to_all) {
668 return JOptionPane.YES_OPTION;
669 }
670
671 Object[] options = { Dictionary.get("General.Yes"), Dictionary.get("FileActions.Yes_To_All"), Dictionary.get("General.No"), Dictionary.get("General.Cancel") };
672 int result = JOptionPane.showOptionDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Exists", target_file_name), Dictionary.get("General.Warning"), JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
673 if (result == 0) {
674 return JOptionPane.YES_OPTION;
675 }
676 else if (result == 1) {
677 yes_to_all = true;
678 return JOptionPane.YES_OPTION;
679 }
680 else if (result == 2) {
681 return JOptionPane.NO_OPTION;
682 }
683 else {
684 return JOptionPane.CANCEL_OPTION;
685 }
686 }
687
688
689 public void run()
690 {
691 super.setName("FileQueue");
692
693 while (!Gatherer.exit) {
694 // Retrieve the next job
695 int position = queue.size() - 1;
696 if (position >= 0) {
697 // We have a file job, so process it
698 processFileJob((FileJob) queue.remove(position));
699 }
700 else {
701 // No jobs, so reset and wait until we are notified of one
702 synchronized(this) {
703 // Force both workspace and collection trees to refresh
704 if (Gatherer.g_man != null && Gatherer.c_man.ready()) {
705 Gatherer.g_man.refreshWorkspaceTree(DragTree.COLLECTION_CONTENTS_CHANGED);
706 // Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED);
707 }
708
709 // Reset status area
710 file_status.setText(Dictionary.get("FileActions.No_Activity"));
711 progress.reset();
712 progress.setString(Dictionary.get("FileActions.No_Activity"));
713
714 // Reset "yes to all" and "cancel" flags
715 yes_to_all = false;
716 cancel_action = false;
717
718 // Wait for a new file job
719 try {
720 wait();
721 }
722 catch (InterruptedException exception) {}
723 }
724 }
725 }
726 }
727
728
729 /** Register the button that will be responsible for stopping executing file actions.
730 * @param stop_button a JButton
731 */
732 public void registerStopButton(JButton stop_button) {
733 this.stop_button = stop_button;
734 }
735
736
737 synchronized private void clearJobs() {
738 queue.clear();
739 }
740
741 /** Copy the contents from the source directory to the destination
742 * directory.
743 * @param source The source directory
744 * @param destination The destination directory
745 * @see org.greenstone.gatherer.Gatherer
746 */
747 public void copyDirectoryContents(File source, File destination, boolean overwrite)
748 throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
749 {
750 if (!source.isDirectory()) return;
751 // check that dest dirs exist
752 destination.mkdirs();
753
754 File [] src_files = source.listFiles();
755 if (src_files.length == 0) return; // nothing to copy
756 for (int i=0; i<src_files.length; i++) {
757 File f = src_files[i];
758 String f_name = f.getName();
759 File new_file = new File(destination, f_name);
760 if (f.isDirectory()) {
761 copyDirectoryContents(f, new_file, overwrite);
762 } else if (f.isFile()) {
763 copyFile(f, new_file, overwrite);
764 }
765 }
766 }
767
768 /** Preserving old default behaviour of copyDirContents method, where overwrite is false. */
769 public void copyDirectoryContents(File source, File destination) throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
770 {
771 copyDirectoryContents(source, destination, false);
772 }
773
774
775 /** Copy a file from the source location to the destination location.
776 * @param source The source File.
777 * @param destination The destination File.
778 * @see org.greenstone.gatherer.Gatherer
779 */
780 public void copyFile(File source, File destination, boolean overwrite)
781 throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
782 {
783 if (source.isDirectory()) {
784 destination.mkdirs();
785 return;
786 }
787
788 // Check if the origin file exists.
789 if (!source.exists()) {
790 DebugStream.println("Couldn't find the source file.");
791 throw new FileNotFoundException();
792 }
793
794 // Make sure the destination file does not exist.
795 if (destination.exists() && !overwrite) {
796 throw new FileAlreadyExistsException();
797 }
798
799 // Open an input stream to the source file
800 FileInputStream f_in = null;
801 try {
802 f_in = new FileInputStream(source);
803 }
804 catch (FileNotFoundException exception) {
805 // A FileNotFoundException translates into a ReadNotPermittedException in this case
806 throw new ReadNotPermittedException(exception.toString());
807 }
808
809 // Create an necessary directories for the target file
810 File dirs = destination.getParentFile();
811 dirs.mkdirs();
812
813 // Open an output stream to the target file
814 FileOutputStream f_out = null;
815 try {
816 f_out = new FileOutputStream(destination);
817 }
818 catch (FileNotFoundException exception) {
819 // A FileNotFoundException translates into a WriteNotPermittedException in this case
820 f_in.close();
821 throw new WriteNotPermittedException(exception.toString());
822 }
823
824 // Copy the file
825 byte data[] = new byte[BUFFER_SIZE];
826 int data_size = 0;
827 while ((data_size = f_in.read(data, 0, BUFFER_SIZE)) != -1 && !cancel_action) {
828 long destination_size = destination.length();
829 try {
830 f_out.write(data, 0, data_size);
831 }
832 // If an IO exception occurs, we can do some maths to determine if the number of bytes written to the file was less than expected. If so we assume a InsufficientSpace exception. If not we just throw the exception again.
833 catch (IOException io_exception) {
834 f_in.close();
835 f_out.close();
836
837 if (destination_size + (long) data_size > destination.length()) {
838 // Determine the difference (which I guess is in bytes).
839 long difference = (destination_size + (long) data_size) - destination.length();
840 // Transform that into a human readable string.
841 String message = Utility.formatFileLength(difference);
842 throw new InsufficientSpaceException(message);
843 }
844 else {
845 throw(io_exception);
846 }
847 }
848 }
849
850 // Flush and close the streams to ensure all bytes are written.
851 f_in.close();
852 f_out.close();
853
854 // We have now, in theory, produced an exact copy of the source file. Check this by comparing sizes.
855 if(!destination.exists() || (!cancel_action && source.length() != destination.length())) {
856 throw new UnknownFileErrorException();
857 }
858
859 // If we were cancelled, ensure that none of the destination file exists.
860 if (cancel_action) {
861 destination.delete();
862 }
863 }
864}
Note: See TracBrowser for help on using the repository browser.