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

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

Further fix to client-GLI > Rightclick Doc > Replace: if the replacement file is identically named to the original one being replaced, then GLI warns the user of this and the user can cancel out. Prior to this fix, even if the user cancelled out the file would still get uploaded to the remote server, replacing the original one there even though the local one would not get replaced and leaving remote and local collection out of sync. And it's the remote version that gets built, moreover. Adding listeners and firing changed events on successful copy, in order to now only do the replacement file upload to remote GS3 server iff the local copy took place successfully (and wasn't cancelled or went wrong).

  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 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 public void fireFileCopiedSuccessfully(File new_file) {
88 // Send the event off to all the matching Listeners
89 for (int i = 0; i < file_copied_success_listeners.size(); i++) {
90 ((FileCopiedSuccessListener) file_copied_success_listeners.get(i)).fileCopiedSuccessfully(new_file);
91 }
92 }
93 public void addFileCopiedSuccessListener(FileCopiedSuccessListener listener) {
94 file_copied_success_listeners.add(listener);
95 }
96
97 public void removeFileCopiedSuccessListener(FileCopiedSuccessListener listener)
98 {
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 fireFileCopiedSuccessfully(source_file);
429 }
430 catch (FileAlreadyExistsException exception) {
431 // This should not ever happen, since we've called copyFile with overwrite set
432 DebugStream.printStackTrace(exception);
433 return;
434 }
435 catch (FileNotFoundException exception) {
436 DebugStream.printStackTrace(exception);
437 if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
438 clearJobs(); // Aborting action
439 }
440 // Refresh the source tree model
441 FileSystemModel source_model = file_job.source.getTreeModel();
442 source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
443 return;
444 }
445 catch (InsufficientSpaceException exception) {
446 DebugStream.printStackTrace(exception);
447 if (showErrorDialog(Dictionary.get("FileActions.Insufficient_Space_Message", exception.getMessage())) == JOptionPane.CANCEL_OPTION) {
448 clearJobs(); // Aborting action
449 }
450 return;
451 }
452 catch (IOException exception) {
453 DebugStream.printStackTrace(exception);
454 if (showErrorDialog(exception.getMessage()) == JOptionPane.CANCEL_OPTION) {
455 clearJobs(); // Aborting action
456 }
457 return;
458 }
459 catch (ReadNotPermittedException exception) {
460 DebugStream.printStackTrace(exception);
461 if (showErrorDialog(Dictionary.get("FileActions.Read_Not_Permitted_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
462 clearJobs(); // Aborting action
463 }
464 return;
465 }
466 catch (UnknownFileErrorException exception) {
467 DebugStream.printStackTrace(exception);
468 if (showErrorDialog(Dictionary.get("FileActions.Unknown_File_Error_Message")) == JOptionPane.CANCEL_OPTION) {
469 clearJobs(); // Aborting action
470 }
471 return;
472 }
473 catch (WriteNotPermittedException exception) {
474 DebugStream.printStackTrace(exception);
475 if (showErrorDialog(Dictionary.get("FileActions.Write_Not_Permitted_Message", target_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
476 clearJobs(); // Aborting action
477 }
478 return;
479 }
480
481 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
482 if (overwrite_file == false) {
483 // Add the new node into the tree
484 FileSystemModel target_model = file_job.target.getTreeModel();
485 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
486 }
487 Gatherer.c_man.fireFileAddedToCollection(target_file);
488
489 // Copy the non-folder level metadata assigned to the original file to the new file
490 if (file_job.type == FileJob.COPY) {
491 // do metadata too
492 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToExternalFile(source_file);
493 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
494 }
495 }
496
497
498 private void doFileMove(FileJob file_job)
499 {
500 FileNode source_node = file_job.getOrigin();
501 FileNode target_node = file_job.getDestination();
502
503 File source_file = source_node.getFile();
504 File target_file = new File(target_node.getFile(), source_file.getName());
505 if (file_job.type == FileJob.RENAME) {
506 // This is the only difference between moves and renames
507 target_file = target_node.getFile();
508 target_node = (FileNode) source_node.getParent();
509 }
510
511 // Check the target file isn't the source file
512 if (target_file.equals(source_file)) {
513 DebugStream.println("Target file is the source file!");
514 return;
515 }
516
517 // The target file shouldn't already exist
518 if (target_file.exists()) {
519 int result = showOverwriteDialog(target_file.getName());
520 if (result == JOptionPane.NO_OPTION) {
521 // Don't overwrite
522 return;
523 }
524 if (result == JOptionPane.CANCEL_OPTION) {
525 clearJobs(); // Aborting action
526 return;
527 }
528 }
529
530 // Move the file by renaming it
531 if (!source_file.renameTo(target_file)) {
532 String args[] = { source_file.getName(), target_file.getAbsolutePath() };
533 if (showErrorDialog(Dictionary.get("FileActions.File_Move_Error_Message", args)) == JOptionPane.CANCEL_OPTION) {
534 clearJobs(); // Aborting action
535 }
536 return;
537 }
538
539 // Remove the node from the source model and add it to the target model
540 SynchronizedTreeModelTools.removeNodeFromParent(file_job.source.getTreeModel(), source_node);
541 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
542 FileSystemModel target_model = file_job.target.getTreeModel();
543 SynchronizedTreeModelTools.insertNodeInto(target_model, target_node, new_target_node);
544
545 // Move the non-folder level metadata assigned to the original file to the new file
546 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
547 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
548 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
549 }
550
551
552 /** all this does is move the metadata, and delete the source */
553 private void doFileReplace(FileJob file_job)
554 {
555 FileNode source_node = file_job.getOrigin();
556 FileNode target_node = file_job.getDestination();
557
558 File source_file = source_node.getFile();
559 File target_file = target_node.getFile();
560
561 // Move the non-folder level metadata assigned to the original file to the new file
562 CollectionTreeNode new_target_node = new CollectionTreeNode(target_file);
563 ArrayList assigned_metadata = MetadataXMLFileManager.getMetadataAssignedDirectlyToFile(source_file);
564 MetadataXMLFileManager.removeMetadata((CollectionTreeNode) source_node, assigned_metadata);
565 MetadataXMLFileManager.addMetadata((CollectionTreeNode) new_target_node, assigned_metadata);
566
567 // now delete the original
568 doFileDelete(file_job);
569 }
570
571
572 private void processFileJob(FileJob file_job)
573 {
574 DebugStream.println("Processing file job " + file_job + "...");
575
576 // Ensure that the source file exists
577 File source_file = file_job.getOrigin().getFile();
578 if (!source_file.exists()) {
579 // The source file doesn't exist, so give the user the option of continuing or cancelling
580 if (showErrorDialog(Dictionary.get("FileActions.File_Not_Found_Message", source_file.getAbsolutePath())) == JOptionPane.CANCEL_OPTION) {
581 clearJobs(); // Aborting action
582 }
583 // Refresh the source tree model
584 FileSystemModel source_model = file_job.source.getTreeModel();
585 source_model.refresh(new TreePath(((FileNode) file_job.getOrigin().getParent()).getPath()));
586 return;
587 }
588
589 // Enable the "Stop" button
590 stop_button.setEnabled(true);
591
592 // Delete empty directory job
593 if (file_job.type == FileJob.DELETE_EMPTY_DIRECTORY) {
594 file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
595 doEmptyDirectoryDelete(file_job);
596 return;
597 }
598
599 // Delete job
600 if (file_job.type == FileJob.DELETE) {
601 file_status.setText(Dictionary.get("FileActions.Deleting", formatPath("FileActions.Deleting", source_file.getAbsolutePath(), file_status.getSize().width)));
602 if (source_file.isFile()) {
603 long source_file_size = source_file.length();
604 doFileDelete(file_job);
605 progress.addValue(source_file_size); // Update progress bar
606 }
607 else {
608 doDirectoryDelete(file_job);
609 }
610 return;
611 }
612
613 // Copy job
614 if (file_job.type == FileJob.COPY || file_job.type == FileJob.COPY_FILE_ONLY) {
615 file_status.setText(Dictionary.get("FileActions.Copying", formatPath("FileActions.Copying", source_file.getAbsolutePath(), file_status.getSize().width)));
616 if (source_file.isFile()) {
617 long source_file_size = source_file.length();
618 doFileCopy(file_job);
619 progress.addValue(source_file_size); // Update progress bar
620 }
621 else {
622 doDirectoryCopy(file_job);
623 }
624 return;
625 }
626
627 // Move (or rename) job
628 if (file_job.type == FileJob.MOVE || file_job.type == FileJob.RENAME) {
629 file_status.setText(Dictionary.get("FileActions.Moving", formatPath("FileActions.Moving", source_file.getAbsolutePath(), file_status.getSize().width)));
630 if (source_file.isFile()) {
631 long source_file_size = source_file.length();
632 doFileMove(file_job);
633 progress.addValue(source_file_size); // Update progress bar
634 }
635 else {
636 doDirectoryMove(file_job);
637 }
638 return;
639 }
640
641 // Replace job
642 if (file_job.type == FileJob.REPLACE) {
643 file_status.setText(Dictionary.get("FileActions.Replacing", formatPath("FileActions.Replacing", source_file.getAbsolutePath(), file_status.getSize().width)));
644 doFileReplace(file_job);
645 return;
646 }
647 }
648
649
650 private int showErrorDialog(String error_message)
651 {
652 Object[] options = { Dictionary.get("General.OK"), Dictionary.get("General.Cancel") };
653 int result = JOptionPane.showOptionDialog(Gatherer.g_man, error_message, Dictionary.get("General.Error"), JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[0]);
654 if (result == 0) {
655 return JOptionPane.OK_OPTION;
656 }
657 else {
658 return JOptionPane.CANCEL_OPTION;
659 }
660 }
661
662
663 private int showOverwriteDialog(String target_file_name)
664 {
665 // Has "yes to all" been set?
666 if (yes_to_all) {
667 return JOptionPane.YES_OPTION;
668 }
669
670 Object[] options = { Dictionary.get("General.Yes"), Dictionary.get("FileActions.Yes_To_All"), Dictionary.get("General.No"), Dictionary.get("General.Cancel") };
671 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]);
672 if (result == 0) {
673 return JOptionPane.YES_OPTION;
674 }
675 else if (result == 1) {
676 yes_to_all = true;
677 return JOptionPane.YES_OPTION;
678 }
679 else if (result == 2) {
680 return JOptionPane.NO_OPTION;
681 }
682 else {
683 return JOptionPane.CANCEL_OPTION;
684 }
685 }
686
687
688 public void run()
689 {
690 super.setName("FileQueue");
691
692 while (!Gatherer.exit) {
693 // Retrieve the next job
694 int position = queue.size() - 1;
695 if (position >= 0) {
696 // We have a file job, so process it
697 processFileJob((FileJob) queue.remove(position));
698 }
699 else {
700 // No jobs, so reset and wait until we are notified of one
701 synchronized(this) {
702 // Force both workspace and collection trees to refresh
703 if (Gatherer.g_man != null && Gatherer.c_man.ready()) {
704 Gatherer.g_man.refreshWorkspaceTree(DragTree.COLLECTION_CONTENTS_CHANGED);
705 // Gatherer.g_man.refreshCollectionTree(DragTree.COLLECTION_CONTENTS_CHANGED);
706 }
707
708 // Reset status area
709 file_status.setText(Dictionary.get("FileActions.No_Activity"));
710 progress.reset();
711 progress.setString(Dictionary.get("FileActions.No_Activity"));
712
713 // Reset "yes to all" and "cancel" flags
714 yes_to_all = false;
715 cancel_action = false;
716
717 // Wait for a new file job
718 try {
719 wait();
720 }
721 catch (InterruptedException exception) {}
722 }
723 }
724 }
725 }
726
727
728 /** Register the button that will be responsible for stopping executing file actions.
729 * @param stop_button a JButton
730 */
731 public void registerStopButton(JButton stop_button) {
732 this.stop_button = stop_button;
733 }
734
735
736 synchronized private void clearJobs() {
737 queue.clear();
738 }
739
740 /** Copy the contents from the source directory to the destination
741 * directory.
742 * @param source The source directory
743 * @param destination The destination directory
744 * @see org.greenstone.gatherer.Gatherer
745 */
746 public void copyDirectoryContents(File source, File destination, boolean overwrite)
747 throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
748 {
749 if (!source.isDirectory()) return;
750 // check that dest dirs exist
751 destination.mkdirs();
752
753 File [] src_files = source.listFiles();
754 if (src_files.length == 0) return; // nothing to copy
755 for (int i=0; i<src_files.length; i++) {
756 File f = src_files[i];
757 String f_name = f.getName();
758 File new_file = new File(destination, f_name);
759 if (f.isDirectory()) {
760 copyDirectoryContents(f, new_file, overwrite);
761 } else if (f.isFile()) {
762 copyFile(f, new_file, overwrite);
763 }
764 }
765 }
766
767 /** Preserving old default behaviour of copyDirContents method, where overwrite is false. */
768 public void copyDirectoryContents(File source, File destination) throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
769 {
770 copyDirectoryContents(source, destination, false);
771 }
772
773
774 /** Copy a file from the source location to the destination location.
775 * @param source The source File.
776 * @param destination The destination File.
777 * @see org.greenstone.gatherer.Gatherer
778 */
779 public void copyFile(File source, File destination, boolean overwrite)
780 throws FileAlreadyExistsException, FileNotFoundException, InsufficientSpaceException, IOException, ReadNotPermittedException, UnknownFileErrorException, WriteNotPermittedException
781 {
782 if (source.isDirectory()) {
783 destination.mkdirs();
784 return;
785 }
786
787 // Check if the origin file exists.
788 if (!source.exists()) {
789 DebugStream.println("Couldn't find the source file.");
790 throw new FileNotFoundException();
791 }
792
793 // Make sure the destination file does not exist.
794 if (destination.exists() && !overwrite) {
795 throw new FileAlreadyExistsException();
796 }
797
798 // Open an input stream to the source file
799 FileInputStream f_in = null;
800 try {
801 f_in = new FileInputStream(source);
802 }
803 catch (FileNotFoundException exception) {
804 // A FileNotFoundException translates into a ReadNotPermittedException in this case
805 throw new ReadNotPermittedException(exception.toString());
806 }
807
808 // Create an necessary directories for the target file
809 File dirs = destination.getParentFile();
810 dirs.mkdirs();
811
812 // Open an output stream to the target file
813 FileOutputStream f_out = null;
814 try {
815 f_out = new FileOutputStream(destination);
816 }
817 catch (FileNotFoundException exception) {
818 // A FileNotFoundException translates into a WriteNotPermittedException in this case
819 f_in.close();
820 throw new WriteNotPermittedException(exception.toString());
821 }
822
823 // Copy the file
824 byte data[] = new byte[BUFFER_SIZE];
825 int data_size = 0;
826 while ((data_size = f_in.read(data, 0, BUFFER_SIZE)) != -1 && !cancel_action) {
827 long destination_size = destination.length();
828 try {
829 f_out.write(data, 0, data_size);
830 }
831 // 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.
832 catch (IOException io_exception) {
833 f_in.close();
834 f_out.close();
835
836 if (destination_size + (long) data_size > destination.length()) {
837 // Determine the difference (which I guess is in bytes).
838 long difference = (destination_size + (long) data_size) - destination.length();
839 // Transform that into a human readable string.
840 String message = Utility.formatFileLength(difference);
841 throw new InsufficientSpaceException(message);
842 }
843 else {
844 throw(io_exception);
845 }
846 }
847 }
848
849 // Flush and close the streams to ensure all bytes are written.
850 f_in.close();
851 f_out.close();
852
853 // We have now, in theory, produced an exact copy of the source file. Check this by comparing sizes.
854 if(!destination.exists() || (!cancel_action && source.length() != destination.length())) {
855 throw new UnknownFileErrorException();
856 }
857
858 // If we were cancelled, ensure that none of the destination file exists.
859 if (cancel_action) {
860 destination.delete();
861 }
862 }
863}
Note: See TracBrowser for help on using the repository browser.