source: main/trunk/gli/src/org/greenstone/gatherer/file/FileManager.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: 21.8 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 * <BR><BR>
9 *
10 * Author: John Thompson, NZDL Project, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 2005 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.file;
38
39import java.io.File;
40import javax.swing.*;
41import org.greenstone.gatherer.Dictionary;
42import org.greenstone.gatherer.Gatherer;
43import org.greenstone.gatherer.collection.CollectionManager;
44import org.greenstone.gatherer.collection.CollectionTree;
45import org.greenstone.gatherer.collection.CollectionTreeNode;
46import org.greenstone.gatherer.gui.ExplodeMetadataDatabasePrompt;
47import org.greenstone.gatherer.gui.ReplaceSrcDocWithHtmlPrompt;
48import org.greenstone.gatherer.gui.GProgressBar;
49import org.greenstone.gatherer.gui.NewFolderOrFilePrompt;
50import org.greenstone.gatherer.gui.RenamePrompt;
51import org.greenstone.gatherer.gui.tree.DragTree;
52import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
53import org.greenstone.gatherer.util.DragComponent;
54import org.greenstone.gatherer.util.Utility;
55import org.greenstone.gatherer.DebugStream;
56
57/** Manages the moving of files within a separate thread.
58 * @author John Thompson, NZDL Project, University of Waikato
59 */
60public class FileManager
61{
62 /** Not only the queue of files to be moved, but also the object that moves them. */
63 static private FileQueue file_queue = null;
64
65 public static final int COPY = 0;
66 public static final int MOVE = 1;
67
68 public static final int FILE_TYPE = 0;
69 public static final int FOLDER_TYPE = 1;
70 protected static File startup_directory = null;
71
72
73 /** Constructor.
74 * @see org.greenstone.gatherer.file.FileQueue
75 */
76 public FileManager()
77 {
78 file_queue = new FileQueue();
79 file_queue.start();
80 }
81
82
83 /** Determine what action should be carried out by the file queue, and add all of the necessary file jobs. */
84 public void action(DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node)
85 {
86 // Check there is something to do
87 if (source_nodes == null || source_nodes.length == 0) {
88 return;
89 }
90
91 // We need a unique ID for each file task
92 long id = System.currentTimeMillis();
93
94 // If source and target are the same we're moving
95 if (source == target) {
96 // Start a new move FileTask and we're done
97 (new FileTask(id, source, source_nodes, target, target_node, FileJob.MOVE)).start();
98 return;
99 }
100
101 // If target isn't the RecycleBin, we're copying
102 if (!(target instanceof RecycleBin)) {
103 // Start a new copy FileTask and we're done
104 (new FileTask(id, source, source_nodes, target, target_node, FileJob.COPY)).start();
105 return;
106 }
107
108 // We're deleting... but first make sure source isn't read-only
109 boolean read_only_source = false;
110
111 // The workspace tree is read-only...
112 if (source.toString().equals("Workspace")) {
113 read_only_source = true;
114
115 // ...except for files from the "Downloaded Files" folder
116 String downloaded_files_folder_path = Gatherer.getGLIUserCacheDirectoryPath();
117 for (int i = 0; i < source_nodes.length; i++) {
118 // Is this the "Downloaded Files" folder?
119 if (source_nodes[i].getFile().getAbsolutePath().startsWith(downloaded_files_folder_path)) {
120 read_only_source = false;
121 }
122 }
123 }
124
125 // The source is read-only, so tell the user and abort
126 if (read_only_source) {
127 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Read_Only"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
128 return;
129 }
130
131 // Start a new delete FileTask and we're done
132 (new FileTask(id, source, source_nodes, target, target_node, FileJob.DELETE)).start();
133 }
134
135 /** For moving and copying of folders. */
136 public void action(File sourceFolder, File targetFolder, int operation) {
137 (new SimpleFileTask(sourceFolder, targetFolder, operation)).start();
138 }
139
140
141 /** Retrieves the file queue object. */
142 public FileQueue getQueue()
143 {
144 return file_queue;
145 }
146
147 /** Performs the simple file task of moving or copying folders. */
148 private class SimpleFileTask
149 extends Thread
150 {
151 private File sourceFolder;
152 private File targetFolder;
153 int operation; // MOVE or COPY
154
155 public SimpleFileTask(File sourceFolder, File targetFolder, int operation)
156 {
157 this.sourceFolder = sourceFolder;
158 this.targetFolder = targetFolder;
159 this.operation = operation;
160 }
161
162
163 public void run()
164 {
165 // check if we're moving or overwriting the current collection
166 String currentColPath = Gatherer.getCollectDirectoryPath()+CollectionManager.getLoadedCollectionName();
167 if(currentColPath.equals(sourceFolder.getAbsolutePath())
168 || currentColPath.equals(targetFolder.getAbsolutePath())) {
169 Gatherer.g_man.saveThenCloseCurrentCollection();
170 }
171
172 // if moving, try a simple move operation (if it works, it
173 // shouldn't take long at all and doesn't need a progress bar)
174 if(operation == MOVE && sourceFolder.renameTo(targetFolder)) {
175 //System.err.println("**** A simple renameTo() worked.");
176 WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
177 return;
178 }
179
180 // Reset the progress bar and set it to indeterminate while calculating its size
181 GProgressBar progress_bar = file_queue.getProgressBar();
182 progress_bar.reset();
183 progress_bar.setIndeterminate(true);
184
185 String status = "FileActions.Moving";
186 if(operation == COPY) {
187 status = "FileActions.Copying";
188 }
189 progress_bar.setString(Dictionary.get(status));
190 file_queue.getFileStatus().setText(Dictionary.get(status,
191 file_queue.formatPath(status,
192 sourceFolder.getAbsolutePath(),
193 file_queue.getFileStatus().getSize().width)));
194
195 // do the move or copy operation
196 try {
197 //System.err.println("**** Copying " + sourceFolder + " to: " + targetFolder);
198 file_queue.copyDirectoryContents(sourceFolder, targetFolder);
199 } catch(Exception e) {
200 JOptionPane.showMessageDialog(Gatherer.g_man, e.getMessage(),
201 "Can't perform file operation", JOptionPane.ERROR_MESSAGE);
202
203 progress_bar.setIndeterminate(false);
204 progress_bar.clear();
205 return;
206 }
207
208 // if moving, delete the original source folder and
209 // update the docs in GS collections node in the workspace tree
210
211 if(operation == MOVE) {
212 Utility.delete(sourceFolder);
213 WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
214 }
215
216
217 progress_bar.setIndeterminate(false);
218 progress_bar.clear();
219 file_queue.getFileStatus().setText(Dictionary.get("FileActions.No_Activity"));
220 progress_bar.setString(Dictionary.get("FileActions.No_Activity"));
221 }
222 }
223
224 private class FileTask
225 extends Thread
226 {
227 private long id;
228 private DragComponent source;
229 private FileNode[] source_nodes;
230 private DragComponent target;
231 private FileNode target_node;
232 private byte type;
233
234
235 public FileTask(long id, DragComponent source, FileNode[] source_nodes, DragComponent target, FileNode target_node, byte type)
236 {
237 this.id = id;
238 this.source = source;
239 this.source_nodes = source_nodes;
240 this.target = target;
241 this.target_node = target_node;
242 this.type = type;
243 }
244
245
246 public void run()
247 {
248 // Reset the progress bar and set it to indeterminate while calculating its size
249 GProgressBar progress_bar = file_queue.getProgressBar();
250 progress_bar.reset();
251 progress_bar.setIndeterminate(true);
252
253 // Calculate the progress bar size
254 boolean cancelled = file_queue.calculateSize(source_nodes);
255 if (!cancelled) {
256 file_queue.addJob(id, source, source_nodes, target, target_node, type);
257 if (Gatherer.isGsdlRemote) {
258 String collection_name = CollectionManager.getLoadedCollectionName();
259
260 // Perform the appropriate action based on the job type (RemoteGreenstoneServer will queue)
261 if (type == FileJob.COPY) {
262 // Copies: upload all the files at once in one zip file
263 File[] source_files = new File[source_nodes.length];
264 for (int i = 0; i < source_nodes.length; i++) {
265 source_files[i] = source_nodes[i].getFile();
266 }
267 Gatherer.remoteGreenstoneServer.uploadFilesIntoCollection(collection_name, source_files, target_node.getFile());
268 }
269 else if (type == FileJob.DELETE) {
270 // Deletes: delete each top-level file/directory one at a time
271 for (int i = 0; i < source_nodes.length; i++) {
272 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection_name, source_nodes[i].getFile());
273 }
274 }
275 else if (type == FileJob.MOVE) {
276 // Moves: move each top-level file/directory one at a time
277 for (int i = 0; i < source_nodes.length; i++) {
278 Gatherer.remoteGreenstoneServer.moveCollectionFile(
279 collection_name, source_nodes[i].getFile(), target_node.getFile());
280 }
281 }
282 }
283 }
284
285 progress_bar.setIndeterminate(false);
286 progress_bar.clear();
287 }
288 }
289
290 public void explodeMetadataDatabase(File file)
291 {
292 // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
293 new ExplodeMetadataDatabasePromptTask(file).start();
294 }
295
296 // Works with replace_srcdoc_with_html.pl
297 public void replaceSrcDocWithHtml(File[] files)
298 {
299 // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
300 new ReplaceSrcDocWithHtmlPromptTask(files).start();
301 }
302
303 private class ExplodeMetadataDatabasePromptTask
304 extends Thread
305 {
306 private File metadata_database_file = null;
307
308 public ExplodeMetadataDatabasePromptTask(File metadata_database_file)
309 {
310 this.metadata_database_file = metadata_database_file;
311 }
312
313 public void run()
314 {
315 ExplodeMetadataDatabasePrompt emp = new ExplodeMetadataDatabasePrompt(metadata_database_file);
316 }
317 }
318
319 // Works with replace_srcdoc_with_html.pl
320 private class ReplaceSrcDocWithHtmlPromptTask
321 extends Thread
322 {
323 private File[] replace_these_srcdoc_files = null;
324
325 public ReplaceSrcDocWithHtmlPromptTask(File[] replace_these_srcdoc_files)
326 {
327 this.replace_these_srcdoc_files = replace_these_srcdoc_files;
328 }
329
330 public void run()
331 {
332 ReplaceSrcDocWithHtmlPrompt prompt = new ReplaceSrcDocWithHtmlPrompt(replace_these_srcdoc_files);
333 }
334 }
335
336
337 public void openFileInExternalApplication(File file)
338 {
339 // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
340 new OpenFileInExternalApplicationTask(file).start();
341 }
342
343
344 private class OpenFileInExternalApplicationTask
345 extends Thread
346 {
347 private File file = null;
348
349 public OpenFileInExternalApplicationTask(File file)
350 {
351 this.file = file;
352 }
353
354 public void run()
355 {
356 // If we're using a remote Greenstone server, we need to download the file before viewing it...
357 if (Gatherer.isGsdlRemote) {
358 // ... but only if it is inside the collection and we haven't already downloaded it
359 if (file.getAbsolutePath().startsWith(Gatherer.getCollectDirectoryPath()) && file.length() == 0) {
360 if (Gatherer.remoteGreenstoneServer.downloadCollectionFile(
361 CollectionManager.getLoadedCollectionName(), file).equals("")) {
362 // Something has gone wrong downloading the file
363 return;
364 }
365 }
366 }
367
368 // View the file in an external application
369 Gatherer.spawnApplication(file);
370 }
371 }
372
373
374 public void newDummyDoc(DragTree tree, CollectionTreeNode parent_node){
375 newFolderOrDummyDoc(tree, parent_node, FILE_TYPE);
376 }
377
378
379 public void newFolder(DragTree tree, CollectionTreeNode parent_node) {
380 newFolderOrDummyDoc(tree, parent_node, FOLDER_TYPE);
381 }
382
383
384 protected void newFolderOrDummyDoc(DragTree tree, CollectionTreeNode parent_node, int type) {
385 (new NewFolderOrDummyDocumentTask(tree, parent_node, type)).start();
386 }
387
388
389 private class NewFolderOrDummyDocumentTask
390 extends Thread
391 {
392 private DragTree tree = null;
393 private CollectionTreeNode parent_node = null;
394 private int type;
395
396 public NewFolderOrDummyDocumentTask(DragTree tree, CollectionTreeNode parent_node, int type)
397 {
398 this.tree = tree;
399 this.parent_node = parent_node;
400 this.type = type;
401 }
402
403 public void run()
404 {
405 // Ask the user for the directories name.
406 String extension = "";
407 if (type == FILE_TYPE) {
408 extension = ".nul";
409 }
410
411 NewFolderOrFilePrompt new_folder_prompt = new NewFolderOrFilePrompt(parent_node, type, extension);
412 String name = new_folder_prompt.display();
413 new_folder_prompt.dispose();
414 new_folder_prompt = null;
415
416 // And if the name is non-null...
417 if (name != null) {
418 FileSystemModel model = (FileSystemModel) tree.getModel();
419 File folder_file = new File(parent_node.getFile(), name);
420
421 //... check if it already exists.
422 if (folder_file.exists()) {
423 if (type == FILE_TYPE) {
424 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Already_Exists_No_Create", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
425 }
426 else {
427 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Folder_Already_Exists", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
428 }
429 }
430 // Otherwise create it.
431 else {
432 try {
433 if (type == FILE_TYPE) {
434 folder_file.createNewFile();
435 if (Gatherer.isGsdlRemote) {
436 Gatherer.remoteGreenstoneServer.uploadCollectionFile(
437 CollectionManager.getLoadedCollectionName(), folder_file);
438 }
439 }
440 else {
441 folder_file.mkdirs();
442 if (Gatherer.isGsdlRemote) {
443 Gatherer.remoteGreenstoneServer.newCollectionDirectory(
444 CollectionManager.getLoadedCollectionName(), folder_file);
445 }
446 }
447
448 // Update the parent node to show the new folder
449 parent_node.refresh();
450
451 // Refresh workspace tree (collection tree is done automatically)
452 Gatherer.g_man.refreshWorkspaceTree(DragTree.COLLECTION_CONTENTS_CHANGED);
453 }
454 catch (Exception exception) {
455 if (type == FILE_TYPE) {
456 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.File_Create_Error", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
457 }
458 else {
459 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("FileActions.Folder_Create_Error", name), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
460 }
461 }
462 }
463
464 folder_file = null;
465 model = null;
466 }
467 name = null;
468 }
469 }
470
471
472 public void renameCollectionFile(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
473 {
474 // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
475 new RenameTask(collection_tree, collection_tree_node).start();
476 }
477
478
479 private class RenameTask
480 extends Thread
481 {
482 private CollectionTree collection_tree = null;
483 private CollectionTreeNode collection_tree_node = null;
484
485 public RenameTask(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
486 {
487 this.collection_tree = collection_tree;
488 this.collection_tree_node = collection_tree_node;
489 }
490
491 public void run()
492 {
493 RenamePrompt rename_prompt = new RenamePrompt(collection_tree_node);
494 String new_collection_file_name = rename_prompt.display();
495 rename_prompt.dispose();
496 rename_prompt = null;
497
498 if (new_collection_file_name != null) {
499 File collection_file = collection_tree_node.getFile();
500 File new_collection_file = new File(collection_file.getParentFile(), new_collection_file_name);
501 CollectionTreeNode new_collection_tree_node = new CollectionTreeNode(new_collection_file);
502 file_queue.addJob(System.currentTimeMillis(), collection_tree, new FileNode[] { collection_tree_node }, collection_tree, new_collection_tree_node, FileJob.RENAME);
503 if (Gatherer.isGsdlRemote) {
504 Gatherer.remoteGreenstoneServer.moveCollectionFile(
505 CollectionManager.getLoadedCollectionName(), collection_file, new_collection_file);
506 }
507 }
508 }
509 }
510
511 public void replaceCollectionFile(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
512 {
513 // This must go in a separate thread because we need the progress bar to work (remote Greenstone server)
514 new ReplaceTask(collection_tree, collection_tree_node).start();
515 }
516
517
518 private class ReplaceTask
519 extends Thread implements FileCopiedSuccessListener
520 {
521 private CollectionTree collection_tree = null;
522 private CollectionTreeNode collection_tree_node = null;
523
524 public ReplaceTask(CollectionTree collection_tree, CollectionTreeNode collection_tree_node)
525 {
526 this.collection_tree = collection_tree;
527 this.collection_tree_node = collection_tree_node;
528 }
529
530 public void run()
531 {
532 JFileChooser file_chooser = new JFileChooser(startup_directory);
533 file_chooser.setDialogTitle(Dictionary.get("ReplacePrompt.Title"));
534 File new_file = null;
535 int return_val = file_chooser.showOpenDialog(null);
536 if(return_val == JFileChooser.APPROVE_OPTION) {
537 new_file = file_chooser.getSelectedFile();
538 }
539
540 if (new_file == null) {
541 return;
542 }
543
544 // save the search path for next time
545 startup_directory = new_file.getParentFile();
546 // make up a node for the file to bring in
547 WorkspaceTreeNode source_node = new WorkspaceTreeNode(new_file);
548
549 //DebugStream.setDebugging(true, "FileManager.ReplaceTask");
550
551 // Some different handling if the old and new tail file names are the same and target file goes into the same
552 // location in collection tree as source.
553 // This avoids past errors upon replacing with same filename (diff file contents) where the attached meta gets lost,
554 // or remote file gets updated but file gone missing in client-GLI view until collection reopened.
555 boolean isSameLeafName = false;
556 if(collection_tree_node.getFile().getName().equals(new_file.getName())) {
557 DebugStream.println(" @@@ File Replace: New file has the same name as existing.");
558 isSameLeafName = true;
559 }
560
561 File target_directory = collection_tree_node.getFile().getParentFile();
562 CollectionTreeNode new_collection_tree_node = new CollectionTreeNode(new File(target_directory, new_file.getName()));
563
564 FileNode parent = (FileNode)collection_tree_node.getParent(); // store the original source's parent, need it several times after changing source
565
566 if(isSameLeafName) {
567 // If the file name of the replacing file IS the same as the one being replaced
568 // perform a COPY operation, which will copy across metadata too, after confirming whether the user really wants to replace the source with identically named target
569
570 // (a) First, this instance of ReplaceTask and no other starts listening to whether the user
571 // DIDN'T CANCEL out of an identical filename copy operation and if this local file copy
572 // was a success. If so, on successful file copy event fired (only then), the source file
573 // from the workspace tree will also be uploaded to the remote GS3
574 file_queue.addFileCopiedSuccessListener(this);
575
576 // (b) Now can finally add the COPY job to the queue
577 file_queue.addJob(System.currentTimeMillis(), Gatherer.g_man.gather_pane.workspace_tree, new FileNode[] { source_node }, collection_tree, parent, FileJob.COPY);
578
579 } else {
580 // If the file name of the replacing file is NOT the same as the one being replaced
581 // (a) copy the new file in - but don't bring metadata
582 file_queue.addJob(System.currentTimeMillis(), Gatherer.g_man.gather_pane.workspace_tree, new FileNode[] { source_node }, collection_tree, parent, FileJob.COPY_FILE_ONLY);
583
584 // (b) final step to finish off: do a replace of old file with new file
585 file_queue.addJob(System.currentTimeMillis(), collection_tree, new FileNode[] { collection_tree_node }, collection_tree, new_collection_tree_node, FileJob.REPLACE);
586 }
587
588
589 //DebugStream.setDebugging(false, "FileManager.ReplaceTask");
590 }
591
592
593 /** In order to detect that the user cancelled out of replacing an identically named target file,
594 * we now listen to events fired that the file was successfully copied across. Only then do we
595 * bother transferring the source file (from the workspace) into the target location in the
596 * collection on the remote file system. We don't do this if the user cancelled.
597 */
598 public void fileCopiedSuccessfully(File new_file) {
599
600 //DebugStream.setDebugging(true, "FileManager.ReplaceTask.fileCopiedSuccessfully");
601
602 if (Gatherer.isGsdlRemote) {
603 File target_directory = this.collection_tree_node.getFile().getParentFile();
604 File collection_tree_node_file = this.collection_tree_node.getFile();
605
606 String collection_name = CollectionManager.getLoadedCollectionName();
607 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection_name, collection_tree_node_file);
608 Gatherer.remoteGreenstoneServer.uploadFilesIntoCollection(collection_name, new File[] { new_file }, target_directory);
609 }
610
611 // stop listening to further events fired now that we've handled this event successfully
612 file_queue.removeFileCopiedSuccessListener(this);
613 //DebugStream.setDebugging(false, "FileManager.ReplaceTask.fileCopiedSuccessfully");
614 }
615 }
616}
Note: See TracBrowser for help on using the repository browser.