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

Last change on this file since 24636 was 24636, checked in by ak19, 13 years ago

FileQueue.copyDirectoryContents is now overloaded to take an overwrite parameter. This is necessary for when CollectionManager.copyExtraBaseCollStuff() has to copy the macros folder across. At this point a new collection contains a macros folder already and it used to be unable to overwite it before. Now, with the new overwrite flag to FileQueue.copyDirContents(), setting overwrite to true when basing a new collection on an existing one allows all the necessary folders to be copied across.

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