source: main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java@ 26573

Last change on this file since 26573 was 26573, checked in by ak19, 11 years ago

Related to commit revision 26567 which mandated the inclusion of the site flag (and site_name) when building scripts are run over Greenstone 3 collections. This is now passed in by GLI to those scripts.

  • Property svn:keywords set to Author Date Id Revision
File size: 93.6 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 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.collection;
38
39import java.io.*;
40import java.util.*;
41import javax.swing.*;
42import javax.swing.event.*;
43import javax.swing.filechooser.FileSystemView;
44import javax.swing.tree.*;
45import org.greenstone.gatherer.Configuration;
46import org.greenstone.gatherer.DebugStream;
47import org.greenstone.gatherer.Dictionary;
48import org.greenstone.gatherer.Gatherer;
49import org.greenstone.gatherer.cdm.CollectionDesignManager;
50import org.greenstone.gatherer.cdm.CollectionMeta;
51import org.greenstone.gatherer.cdm.CollectionMetaManager;
52import org.greenstone.gatherer.cdm.CommandTokenizer;
53import org.greenstone.gatherer.cdm.BuildTypeManager;
54import org.greenstone.gatherer.cdm.CollectionConfiguration;
55import org.greenstone.gatherer.greenstone.Classifiers;
56import org.greenstone.gatherer.greenstone.LocalGreenstone;
57import org.greenstone.gatherer.greenstone.LocalLibraryServer;
58import org.greenstone.gatherer.greenstone.Plugins;
59import org.greenstone.gatherer.greenstone3.ServletConfiguration;
60import org.greenstone.gatherer.gui.LockFileDialog;
61import org.greenstone.gatherer.gui.ModalProgressPopup;
62import org.greenstone.gatherer.gui.WarningDialog;
63import org.greenstone.gatherer.metadata.DocXMLFileManager;
64import org.greenstone.gatherer.metadata.FilenameEncoding;
65import org.greenstone.gatherer.metadata.MetadataChangedListener;
66import org.greenstone.gatherer.metadata.MetadataSet;
67import org.greenstone.gatherer.metadata.MetadataSetManager;
68import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
69import org.greenstone.gatherer.metadata.ProfileXMLFileManager;
70import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
71import org.greenstone.gatherer.shell.GShell;
72import org.greenstone.gatherer.shell.GShellEvent;
73import org.greenstone.gatherer.shell.GShellListener;
74import org.greenstone.gatherer.shell.GShellProgressMonitor;
75import org.greenstone.gatherer.util.Codec;
76import org.greenstone.gatherer.util.StaticStrings;
77import org.greenstone.gatherer.util.Utility;
78import org.greenstone.gatherer.util.XMLTools;
79import org.w3c.dom.*;
80
81/** This class manages many aspects of the collection, from its creation via scripts, data access via methods and its importing and building into the final collection. It is also responsible for firing appropriate event when significant changes have occured within the collection, and for creating a new metadata set manager as necessary.
82 * @author John Thompson
83 * @version 2.3
84 */
85public class CollectionManager
86 implements GShellListener, MetadataChangedListener
87{
88 /** Are we currently in the process of building? */
89 static private boolean building = false;
90 /** Are we currently in the process of importing? */
91 static private boolean importing = false;
92 /** Are we currently in the process of scheduling? */
93 static private boolean scheduling = false;
94 /** The objects listening for CollectionContentsChanged events. */
95 static private ArrayList collection_contents_changed_listeners = new ArrayList();
96 /** The collection this manager is managing! */
97 static private Collection collection = null;
98 /** The collection tree (used in both Gather and Enrich panes). */
99 static private CollectionTree collection_tree = null;
100 /** The collection tree model. */
101 static private CollectionTreeModel collection_tree_model = null;
102 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
103 static private FMTreeModelListener fm_tree_model_listener = null;
104 /** The monitor responsible for parsing the build process. */
105 static private GShellProgressMonitor build_monitor = null;
106 /** The monitor responsible for parsing the import process. */
107 static private GShellProgressMonitor import_monitor = null;
108 /** The monitor responsible for parsing the scheduler process. */
109 static private GShellProgressMonitor schedule_monitor = null;
110 ///** The monitor responsible for parsing the fedora collection delete process. */
111 //static private GShellProgressMonitor fedora_coldelete_monitor = null;
112
113 static private String delete_collection_name = null;
114
115 /** The name of the standard lock file. */
116 static final public String LOCK_FILE = "gli.lck";
117
118 /** Used to indicate the source of the message is the file collection methods. */
119 static final public int COLLECT = 3;
120 /** Used to indicate the source of the message is the building methods. */
121 static final public int BUILDING = 5;
122 /** Used to indicate the source of the message is in the scheduling methods...? */
123 static final public int SCHEDULING = 7;
124
125 /** To store the path to the perl scripts. In the case of local Greenstone servers,
126 * this will be the local bin/script folder. */
127 static private String scriptPath = "";
128
129 /** Constructor. */
130 public CollectionManager() {
131 // Initialisation.
132 this.building = false;
133 this.importing = false;
134 this.scheduling = false;
135 this.collection = null;
136
137 MetadataXMLFileManager.addMetadataChangedListener(this);
138
139 // If using a remote Greenstone server, delete the local collect directory because it will be out of date
140 if (Gatherer.isGsdlRemote) {
141 System.err.println("Deleting user's local collect directory...");
142 Utility.delete(new File(Gatherer.getCollectDirectoryPath()));
143 System.err.println("Done.");
144 new File(Gatherer.getCollectDirectoryPath()).mkdirs();
145
146 scriptPath = ""; // remote greenstone: scriptPath will be determined on remote server side
147 }
148 else { // local greenstone case: scripts are inside bin/script
149 scriptPath = LocalGreenstone.getBinScriptDirectoryPath();
150 }
151 }
152
153
154 static public void addCollectionContentsChangedListener(CollectionContentsChangedListener listener)
155 {
156 collection_contents_changed_listeners.add(listener);
157 }
158
159
160 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
161 * @see org.greenstone.gatherer.Configuration
162 * @see org.greenstone.gatherer.Gatherer
163 * @see org.greenstone.gatherer.collection.Collection
164 * @see org.greenstone.gatherer.gui.BuildOptions
165 * @see org.greenstone.gatherer.shell.GShell
166 * @see org.greenstone.gatherer.shell.GShellListener
167 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
168 * @see org.greenstone.gatherer.util.Utility
169 */
170 public void buildCollection()
171 {
172
173 DebugStream.println("In CollectionManager.buildCollection(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
174 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
175 building = true;
176
177 // Generate the buildcol.pl command
178 ArrayList command_parts_list = new ArrayList();
179 if (!Gatherer.isGsdlRemote) {
180 command_parts_list.add(Configuration.perl_path);
181 command_parts_list.add("-S");
182 }
183
184 if (Configuration.fedora_info.isActive()) {
185 command_parts_list.add(scriptPath + "g2f-buildcol.pl");
186
187 command_parts_list.add("-hostname");
188 command_parts_list.add(Configuration.fedora_info.getHostname());
189
190 command_parts_list.add("-port");
191 command_parts_list.add(Configuration.fedora_info.getPort());
192
193 command_parts_list.add("-username");
194 command_parts_list.add(Configuration.fedora_info.getUsername());
195
196 command_parts_list.add("-password");
197 command_parts_list.add(Configuration.fedora_info.getPassword());
198
199 command_parts_list.add("-protocol");
200 command_parts_list.add(Configuration.fedora_info.getProtocol());
201
202 }
203 else {
204
205 if ( !CollectionDesignManager.isCompleteBuild() && CollectionDesignManager.index_manager.isLucene() ) {
206 command_parts_list.add(scriptPath + "incremental-buildcol.pl");
207 CollectionDesignManager.setBuildcolWasFull(false);
208 } else {
209 command_parts_list.add(scriptPath + "full-buildcol.pl");
210 CollectionDesignManager.setBuildcolWasFull(true);
211 }
212 }
213
214 command_parts_list.add("-gli");
215 command_parts_list.add("-language");
216 command_parts_list.add(Configuration.getLanguage());
217
218 if(Gatherer.GS3) {
219 command_parts_list.add("-site");
220 command_parts_list.add(Configuration.site_name);
221 }
222
223 if(!Gatherer.isGsdlRemote) {
224 command_parts_list.add("-collectdir");
225 command_parts_list.add(getCollectDirectory()); // <../collect/>
226 }
227
228 String[] build_options = collection.build_options.getValues();
229 for (int i = 0; i < build_options.length; i++) {
230 command_parts_list.add(build_options[i]);
231 }
232
233 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
234
235 // Run the buildcol.pl and
236 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
237 GShell shell = new GShell(command_parts, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
238 shell.addGShellListener(Gatherer.g_man.create_pane);
239 shell.addGShellListener(Gatherer.g_man.format_pane);
240 shell.start();
241
242 }
243
244 /*probably repeating alot of work, but I want to keep this separate... wendy*/
245 public void scheduleBuild()
246 {
247 DebugStream.println("In CollectionManager.scheduleBuild(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
248 DebugStream.println("Is event dispatch threa: " + SwingUtilities.isEventDispatchThread());
249
250 ArrayList sched_list = new ArrayList();
251 if (!Gatherer.isGsdlRemote) {
252 sched_list.add(Configuration.perl_path);
253 sched_list.add("-S");
254 }
255 sched_list.add(scriptPath + "schedule.pl");
256 sched_list.add("-colname");
257 sched_list.add(collection.getName());
258 sched_list.add("-gli");
259
260 // First, generate the import.pl command, also converting to a string
261 // Generate the import.pl command
262 ArrayList import_list = new ArrayList();
263 if (!Gatherer.isGsdlRemote) {
264 import_list.add(Configuration.perl_path);
265 import_list.add("-S");
266 }
267
268 String cmdPrefix = CollectionDesignManager.isCompleteBuild() ? "full-" : "incremental-";
269 import_list.add(scriptPath + cmdPrefix + "import.pl");
270 import_list.add("-language");
271 import_list.add(Configuration.getLanguage());
272
273 if(Gatherer.GS3) {
274 import_list.add("-site");
275 import_list.add(Configuration.site_name);
276 }
277
278 if(!Gatherer.isGsdlRemote) {
279 import_list.add("-collectdir");
280 import_list.add(getCollectDirectory());
281 }
282
283 String[] import_options = collection.import_options.getValues();
284 int i = 0;
285 for (i = 0; i < import_options.length; i++) {
286 import_list.add(import_options[i]);
287 }
288
289 import_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
290
291 String[] import_parts = (String[]) import_list.toArray(new String[0]);
292 String command = "";
293 i = 0;
294 for (i = 0; i < import_parts.length-1; i++) {
295 command = command + import_parts[i] + " ";
296 }
297 command = command + import_parts[i];
298
299 sched_list.add("-import");
300 sched_list.add("\"" + command + "\"");
301
302 // Generate the buildcol.pl command, also converting to a string
303 ArrayList build_list = new ArrayList();
304
305 // i'm not doing this in schedule.pl right now - should i be?
306 if (!Gatherer.isGsdlRemote) {
307 build_list.add(Configuration.perl_path);
308 build_list.add("-S");
309 }
310
311 String buildType = (new CollectionMeta( CollectionDesignManager.collect_config.getBuildType() )).getValue(CollectionMeta.TEXT);
312 if ( !CollectionDesignManager.isCompleteBuild() && buildType.equals( BuildTypeManager.BUILD_TYPE_LUCENE ) ) {
313 build_list.add(scriptPath + "incremental-buildcol.pl");
314 } else {
315 build_list.add(scriptPath + "full-buildcol.pl");
316 }
317
318 build_list.add("-language");
319 build_list.add(Configuration.getLanguage());
320
321 if(Gatherer.GS3) {
322 build_list.add("-site");
323 build_list.add(Configuration.site_name);
324 }
325
326 if(!Gatherer.isGsdlRemote) {
327 build_list.add("-collectdir");
328 build_list.add(getCollectDirectory());
329 }
330
331 String[] build_options = collection.build_options.getValues();
332 for (i = 0; i < build_options.length; i++) {
333 build_list.add(build_options[i]);
334 }
335
336 build_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
337
338 //build actual string
339 String[] build_parts = (String[]) build_list.toArray(new String[0]);
340 String command2 = "";
341 for(i = 0; i < build_parts.length-1; i++) {
342 command2 = command2 + build_parts[i] + " ";
343 }
344 command2 = command2 + build_parts[i];
345
346 sched_list.add("-build");
347 sched_list.add("\"" + command2 + "\"");
348
349 //next, the scheduling frequency goes here
350 String[] schedule_options = collection.schedule_options.getValues();
351 for(i = 0; i < schedule_options.length; i++) {
352 sched_list.add(schedule_options[i]);
353 }
354
355 //now, hope it will run. ;)
356 String[] sched_parts = (String[]) sched_list.toArray(new String[0]);
357
358 GShell shell = new GShell(sched_parts, GShell.SCHEDULE, SCHEDULING, this, schedule_monitor, GShell.GSHELL_SCHEDULE);
359 shell.addGShellListener(Gatherer.g_man.create_pane);
360 shell.addGShellListener(Gatherer.g_man.format_pane);
361 shell.start();
362 }
363
364 /** Used to determine whether the currently active collection has been built.
365 * @return A boolean indicating the built status of the collection.
366 */
367 public boolean built() {
368 if(collection != null) {
369 // Determine if the collection has been built by looking for the build.cfg (gs2)
370 // buildConfig.xml (gs3) or export.inf (fedora) file
371 String file_name = "";
372
373 if (Configuration.fedora_info != null && Configuration.fedora_info.isActive()) { // FLI case
374 // Fedora build
375 //file_name = getLoadedCollectionArchivesDirectoryPath() + "import.inf";
376 //file_name = getLoadedCollectionExportDirectoryPath() + "export.inf"; // export.pl no longer generates this
377 file_name = getLoadedCollectionExportDirectoryPath() + "archiveinf-doc.gdb";
378 } else {
379 // GLI is running, check if it's greenstone 3 or greenstone 2
380 if (Gatherer.GS3) { // GS3 GLI
381 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CONFIG_XML;
382 }
383 else { // greenstone 2 GLI
384 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CFG;
385 }
386 }
387 File test_file = new File(file_name);
388 return test_file.exists();
389 }
390 return false;
391 }
392
393 /** Used to determine whether the currently active collection has been imported.
394 * @return A boolean indicating the imported status of the collection.
395 */
396 public boolean imported() {
397 if ( collection != null ) {
398 String file_name = getLoadedCollectionDirectoryPath() + "archives";
399 File test_file = new File(file_name);
400 return test_file.exists();
401 }
402 return false;
403 }
404
405 /** a test method to see if we can delete a directory/file - returns false is the file or any of the contents of a directory cannot be deleted */
406 static private boolean canDelete(File file)
407 {
408 if (!file.isDirectory()) {
409 return file.canWrite();
410 }
411 File [] file_list = file.listFiles();
412 for (int i=0; i<file_list.length; i++) {
413 if (!canDelete(file_list[i])) {
414 return false;
415 }
416 }
417 return true;
418 }
419
420
421 /** Called to close the current collection and remove its lock file.
422 * @see org.greenstone.gatherer.Gatherer
423 * @see org.greenstone.gatherer.collection.Collection
424 * @see org.greenstone.gatherer.util.Utility
425 */
426 public void closeCollection() {
427 if (collection == null) {
428 return;
429 }
430 DebugStream.println("Close collection: " + collection.getName());
431
432 // Remove the lock on this file, then remove the collection.
433 File lock_file = new File(getLoadedCollectionDirectoryPath() + LOCK_FILE);
434 lock_file.delete();
435 if (lock_file.exists()) {
436 System.err.println("Warning: Lockfile was not successfully deleted.");
437 }
438
439 // Remove the lock file on the server
440 if (Gatherer.isGsdlRemote) {
441 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), lock_file);
442 }
443
444 // release the current build_log file - else we're unable to delete the last closed collection
445 // until another collection is opened and its opening a new build_log closes this old one
446 Gatherer.g_man.create_pane.options_pane.closeCurrentLogDocument();
447
448 MetadataSetManager.clearMetadataSets();
449 MetadataXMLFileManager.clearMetadataXMLFiles();
450 DocXMLFileManager.clearDocXMLFiles();
451 ProfileXMLFileManager.clearProfileXMLFile();
452
453 collection.destroy();
454 collection = null;
455 collection_tree_model = null;
456 //Configuration.setCollectionConfiguration(null);
457 Gatherer.refresh(Gatherer.COLLECTION_CLOSED);
458 if (Gatherer.g_man != null) {
459 Gatherer.g_man.updateUI(); // !!! Necessary?
460 }
461 }
462
463//This method is no longer used in gs3 since the modification of CollectionConfiguration.java
464// public void convertToGS3Collection() {
465// // Generate the convert_coll_from_gs2.pl command
466// ArrayList command_parts_list = new ArrayList();
467// if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
468// command_parts_list.add(Configuration.perl_path);
469// command_parts_list.add("-S");
470// }
471// command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
472//
473// command_parts_list.add("-site");
474// command_parts_list.add(Configuration.site_name);
475//
476// command_parts_list.add("-collectdir");
477// command_parts_list.add(getCollectDirectory());
478// command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
479//
480// // Run the convert_coll_from_gs2.pl command
481// String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
482// GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
483// process.addGShellListener(this);
484// process.run(); // Don't bother threading this... yet
485//
486// }
487
488 /** When basing a new collection on an existing one, we need to copy
489 * over some extra directories: all except import, archives, building, index
490 * really we just want images, macros, perllib, but there may also be eg style, or other dirs.
491 */
492 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
493 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
494 return false;
495 }
496 DebugStream.println("Copying extra dirs from the base collection");
497
498
499 File subdirs[] = base_coll_dir.listFiles();
500 for (int i = 0; subdirs != null && i < subdirs.length; i++) {
501 File subdir = subdirs[i];
502 if (subdir.isDirectory()) {
503 String dir_name = subdir.getName();
504 // ignore those we don't need, (archives, buildng, index) and
505 // those we are handling in another place (import, etc, metadata)
506 if (dir_name.startsWith ("import") || dir_name.startsWith("archives") || dir_name.startsWith("building") || dir_name.startsWith("index") || dir_name.startsWith("etc") || dir_name.startsWith("metadata") || dir_name.startsWith("log") || dir_name.startsWith("tmp")) {
507 continue;
508 }
509 try {
510 // copy the directory
511 File new_coll_subdir = new File(new_coll_dir, dir_name);
512 new_coll_subdir.mkdirs();
513 // Copy with force overwrite, since it's a new collection that's currently
514 // being created and won't have anything in it but at most modelcol stuff
515 Gatherer.f_man.getQueue().copyDirectoryContents(subdir, new_coll_subdir, true);
516 }
517 catch (Exception e) {
518 DebugStream.println("Couldn't copy over the" + subdir+" dir from the base collection: "+e.toString());
519 }
520 }
521 }
522
523 return true;
524 }
525
526 /** Used to set the current collection to the given collection. Note that this call should -always- be proceeded by a ready call, and if the collection is ready and the saved flag is unset then the user should be prompted to save. Also note that this method creates yet another GShell to run buildcol.pl.
527 * @param description a description of the collection as a String
528 * @param email the email address of the author/maintainer as a String
529 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
530 * @param title the longer title of the collection as a String
531 * @param base_collection_directory if the user has chosen to base their new collection on an existing one, this is the directory where this base collection can be found, as a File, otherwise its null
532 * @param metadata_sets if the user has decided to select several metadata sets with which to initially populate the GLI then this is an ArrayList of metadata set file names, otherwise its null
533 */
534 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
535 {
536 // Display a modal progress popup to indicate that the collection is being loaded
537 ModalProgressPopup create_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Creating_Collection"), Dictionary.get("CollectionManager.Creating_Collection_Please_Wait"));
538 create_collection_progress_popup.display();
539
540 // Create the collection on a separate thread so the progress bar updates correctly
541 (new CreateCollectionTask(description, email, name, title, base_collection_directory, metadata_sets, create_collection_progress_popup)).start();
542 }
543
544
545 private class CreateCollectionTask
546 extends Thread
547 {
548 private String description = null;
549 private String email = null;
550 private String name = null;
551 private String title = null;
552 private File base_collection_directory = null;
553 private ArrayList metadata_sets = null;
554 private ModalProgressPopup create_collection_progress_popup = null;
555
556 public CreateCollectionTask(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets, ModalProgressPopup create_collection_progress_popup)
557 {
558 this.description = description;
559 this.email = email;
560 this.name = name;
561 this.title = title;
562 this.base_collection_directory = base_collection_directory;
563 this.metadata_sets = metadata_sets;
564 this.create_collection_progress_popup = create_collection_progress_popup;
565 }
566
567 public void run()
568 {
569 createCollectionInternal(description, email, name, title, base_collection_directory, metadata_sets);
570 create_collection_progress_popup.close();
571 }
572 }
573
574
575 private void createCollectionInternal(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
576 {
577 try {
578 // first make sure that the collect directory exists
579 File collect_dir = new File(getDefaultCollectDirectory());
580 if (!collect_dir.exists()) {
581 collect_dir.mkdirs();
582 }
583
584 // Create the new collection
585 makeCollection(name, email);
586
587 // Check that the collection has been created successfully
588 String collection_directory_path = getCollectionDirectoryPath(name);
589 if (!new File(collection_directory_path).exists()) {
590 // If there is no collection directory then the creation was unsuccessful, or cancelled
591
592 return;
593 }
594
595 // Check for the existence of the collection configuration file
596 String file_name = ((Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG);
597 File collect_cfg_file = new File(collection_directory_path + "etc" + File.separator + file_name);
598
599 if (!collect_cfg_file.exists()) {
600 System.err.println("Error: no " + file_name + " file has been created!");
601 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", Dictionary.get("CollectionManager.No_Config_File")), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
602 return;
603 }
604
605 // ACTIVE_DIR/log/
606 File log_dir = new File(collection_directory_path + "log");
607 log_dir.mkdirs();
608
609 // Make sure an import folder exists
610 File collection_import_directory = new File(collection_directory_path + "import");
611 if (!collection_import_directory.exists()) {
612 collection_import_directory.mkdirs();
613 if (Gatherer.isGsdlRemote) {
614 Gatherer.remoteGreenstoneServer.newCollectionDirectory(name, collection_import_directory);
615 }
616 }
617
618 // Now create the collection object around the directory.
619 collection = new Collection(new File(collection_directory_path, "gli.col"));
620
621 // for remote case, scheduling causes an Exception on creating a new collection that
622 // can't be recovered from. For GS3, it doesn't work since it it trying to access etc/main.cfg
623 if (canDoScheduling()) {
624 scheduling();
625 }
626
627 MetadataSetManager.clearMetadataSets();
628 MetadataXMLFileManager.clearMetadataXMLFiles();
629 DocXMLFileManager.clearDocXMLFiles();
630
631 // Import default metadata sets, if any
632 // for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
633 // importMetadataSet((MetadataSet) metadata_sets.get(i));
634 // }
635
636 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_directory_path + "metadata"));
637
638 // Before creating the CollectionDesignManager check if we are basing it upon some other collection
639 if (base_collection_directory != null) {
640 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
641
642 // If we're using a remote Greenstone server, download the collection shell to get the files needed
643 if (Gatherer.isGsdlRemote) {
644 String base_collection_name = base_collection_directory.getName();
645 Gatherer.remoteGreenstoneServer.downloadCollection(base_collection_name);
646 }
647
648 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
649 // copy over other needed directories
650 copyExtraBaseCollStuff(new File(collection_directory_path), base_collection_directory);
651
652 // Try to import any existing metadata sets for this collection
653 // Look in base_collection_directory/metadata and import any metadata sets found.
654 File base_metadata_directory = new File(base_collection_directory, "metadata");
655 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
656 if (base_metadata_sets != null) {
657 for (int i = 0; i < base_metadata_sets.size(); i++) {
658 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
659 }
660 }
661 else {
662 DebugStream.println("This base collection has no metadata directory.");
663 }
664
665 // Now we update our collect.cfg
666 DebugStream.println("Copy and update " + file_name + " from base collection.");
667
668 if (Gatherer.GS3 == true) {
669 updateCollectionConfigXML(new File(base_collection_directory, Utility.CONFIG_GS3_FILE),
670 new File(collection_directory_path, Utility.CONFIG_GS3_FILE));
671 } else {
672 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE),
673 new File(collection_directory_path, Utility.CONFIG_FILE),
674 description, email, title);
675 }
676 }
677 else {
678 // only load metadata sets here if we have not based the collection on any other.
679 // Load the default metadata sets
680 addDefaultMetadataSets();
681
682 // Make sure we always have the extracted metadata set
683 addRequiredMetadataSets();
684 }
685
686 collection.cdm = new CollectionDesignManager(new File(getLoadedCollectionCfgFilePath()));
687
688 // We always set title and description here rather than calling mkcol.pl with Unicode arguments
689
690 // First though, if we based this collection on another collection and thereyby inherited multilingual
691 // collection names and descriptions, clear those before setting description and name to new values.
692 ArrayList colname_metas = collection.cdm.collectionmeta_manager.getMetadata(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR); // retrieves all languages
693 ArrayList coldesc_metas = collection.cdm.collectionmeta_manager.getMetadata(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
694 for(int i = 0; i < colname_metas.size(); i++) {
695 CollectionMeta colname_meta = (CollectionMeta)colname_metas.get(i);
696 colname_meta.setValue("");
697 }
698 for(int i = 0; i < coldesc_metas.size(); i++) {
699 CollectionMeta coldesc_meta = (CollectionMeta)coldesc_metas.get(i);
700 coldesc_meta.setValue("");
701 }
702
703 // set the collection name and description (in the default language)
704 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR); // retrieves default language
705 collection_name_collectionmeta.setValue(title);
706 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
707 collection_extra_collectionmeta.setValue(description);
708
709 // Now that we have a CDM, update several settings, such as if we created this collection by basing it on another, set it as public automatically. This update is done to the internal xml structure which may be saved into collect.cfg or collectionConfig.xml accordingly.
710 if (base_collection_directory != null) {
711 // Update the creator and maintainer
712 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
713 creator_collectionmeta.setValue(email);
714 creator_collectionmeta = null;
715 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
716 maintainer_collectionmeta.setValue(email);
717 maintainer_collectionmeta = null;
718
719 // All collections based on others are automatically public
720 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
721 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
722 public_collectionmeta = null;
723
724 // Finally reset the icons
725 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
726 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
727 icon_collection_collectionmeta = null;
728 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
729 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
730 icon_collection_small_collectionmeta = null;
731 }
732
733 saveCollection();
734
735 // Create a lock file
736 createLockFile(new File(collection_directory_path, LOCK_FILE));
737
738 // We're done. Let everyone know.
739 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
740 }
741 catch (Exception error) {
742 DebugStream.printStackTrace(error);
743 }
744 }
745
746 private void scheduling()
747 throws Exception
748 {
749 //try to obtain email address of collection owner if it exists...
750 String stmp = Configuration.getEmail();
751 if(stmp != null) {
752 collection.schedule_options.setValue("toaddr", false, Configuration.getEmail());
753 }
754
755 //The next few items deal with updating the SMTP server, and the to: and from: addresses
756 //from main.cfg and the collection configuration. if no changes are made, or the
757 //values are result to NULL, any existing values are kept.
758
759 //try to obtain email address of Greenstone installation webmaster for - used to indicate "sender".
760 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
761 BufferedReader maincfg = new BufferedReader(new FileReader(mcfg));
762 stmp = "";
763 String fromaddr = "";
764 while((stmp = maincfg.readLine()) != null) {
765 if(stmp.startsWith("maintainer")) {
766 fromaddr = stmp.substring(10); //length of MailServer
767 fromaddr = fromaddr.trim();
768 break;
769 }
770 }
771 maincfg.close();
772 if(!fromaddr.equals("NULL") && !fromaddr.equals("null")) {
773 collection.schedule_options.setValue("fromaddr", false, fromaddr);
774 }
775
776 //try to obtain an smtp server address from main.cfg. If that fails,
777 //try mail.server if an email address exists. If that fails,
778 //maybe a message to set attribute in main.cfg?
779 //i'm pretty sure there exists functionality to do this, but
780 //i'll finish this faster if I just wrote it
781
782
783 maincfg = new BufferedReader(new FileReader(mcfg));
784 String smtptmp = "NULL";
785 while((stmp = maincfg.readLine()) != null) {
786 if(stmp.startsWith("MailServer")) {
787 smtptmp = stmp.substring(10); //length of MailServer
788 smtptmp = smtptmp.trim();
789 break;
790 }
791 }
792 maincfg.close();
793
794 //try if lookup fails
795 if(smtptmp.equals("NULL") || smtptmp.equals("null")) {
796 String email2=fromaddr;
797 if(!email2.equals("NULL") && !email2.equals("null")) {
798 int loc = email2.indexOf('@');
799 email2 = email2.substring(loc+1);
800 smtptmp = "mail."+email2;
801 }
802 }
803 if(!smtptmp.equals("NULL") && !smtptmp.equals("null")) {
804 collection.schedule_options.setValue("smtp", false, smtptmp);
805 }
806
807 }
808
809
810 private void createLockFile(File lock_file)
811 {
812 try {
813 Document default_lockfile = XMLTools.parseXMLFile("xml/" + LOCK_FILE, true);
814 String user_name = System.getProperty("user.name");
815 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
816 person_element.appendChild(default_lockfile.createTextNode(user_name));
817 person_element = null;
818 user_name = null;
819 String machine_name = Utility.getMachineName();
820 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
821 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
822 machine_element = null;
823 machine_name = null;
824 String date_time = Utility.getDateString();
825 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
826 date_element.appendChild(default_lockfile.createTextNode(date_time));
827 date_element = null;
828 date_time = null;
829 XMLTools.writeXMLFile(lock_file, default_lockfile);
830 }
831 catch (Exception exception) {
832 DebugStream.printStackTrace(exception);
833 }
834 }
835
836
837 public boolean deleteCollection(String collection_name)
838 {
839 // First we must release the collection from the local library, if it's running
840 if (LocalLibraryServer.isRunning() == true) {
841 LocalLibraryServer.releaseCollection(collection_name);
842 }
843
844 // if we're running FLI, it's a fedora collection, and we need to call a special
845 // delete script to delete the collection and all its documents from the fedora
846 // repository and from fedora-gsearch's index.
847 if (Configuration.fedora_info.isActive()) {
848
849 // Generate the buildcol.pl command
850 ArrayList command_parts_list = new ArrayList();
851 if (!Gatherer.isGsdlRemote) {
852 command_parts_list.add(Configuration.perl_path);
853 command_parts_list.add("-S");
854 }
855
856 command_parts_list.add(scriptPath + "g2f-deletecol.pl");
857
858 command_parts_list.add("-hostname");
859 command_parts_list.add(Configuration.fedora_info.getHostname());
860
861 command_parts_list.add("-port");
862 command_parts_list.add(Configuration.fedora_info.getPort());
863
864 command_parts_list.add("-username");
865 command_parts_list.add(Configuration.fedora_info.getUsername());
866
867 command_parts_list.add("-password");
868 command_parts_list.add(Configuration.fedora_info.getPassword());
869
870 command_parts_list.add("-protocol");
871 command_parts_list.add(Configuration.fedora_info.getProtocol());
872
873 command_parts_list.add("-gli");
874 command_parts_list.add("-language");
875 command_parts_list.add(Configuration.getLanguage());
876
877 if(Gatherer.GS3) {
878 command_parts_list.add("-site");
879 command_parts_list.add(Configuration.site_name);
880 }
881
882 if(!Gatherer.isGsdlRemote) {
883 command_parts_list.add("-collectdir");
884 command_parts_list.add(getCollectDirectory()); // <../collect/>
885 }
886
887 command_parts_list.add(collection_name);
888
889 // Run the g2f-deletecol.pl to remove the fedora collection
890 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
891 GShell shell = new GShell(command_parts, GShell.DELETE, COLLECT, this, null, GShell.GSHELL_FEDORA_COLDELETE); // fedora_coldelete_monitor set to null
892 shell.start();
893
894 delete_collection_name = collection_name;
895 return true; // we assume it succeeded for now, wait until the process returns
896 // and then proceed to delete the collection directory
897 }
898 else { // not a fedora collection
899
900 // Delete the collection on the server if we're using a remote Greenstone
901 if (Gatherer.isGsdlRemote) {
902 Gatherer.remoteGreenstoneServer.deleteCollection(collection_name);
903 }
904
905 // if Greenstone3, need to deactivate the collection on the server
906 if (Gatherer.GS3) {
907 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection_name);
908 }
909 // Now delete the collection directory
910 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
911 }
912 }
913
914
915 public void fireFileAddedToCollection(File file)
916 {
917 // Send the event off to all the CollectionContentsChangedListeners
918 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
919 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
920 }
921 }
922
923
924 /** Retrieve the current collection.
925 * @return The <strong>Collection</strong> itself.
926 */
927 public Collection getCollection() {
928 return collection;
929 }
930
931
932 /** Returns the absolute filename of the specified collection's directory.
933 */
934 static public String getCollectionDirectoryPath(String collection_name)
935 {
936 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
937 }
938
939
940 /** Returns the absolute filename of the loaded collection's archives directory.
941 */
942 static public String getLoadedCollectionArchivesDirectoryPath()
943 {
944 return getLoadedCollectionDirectoryPath() + "archives" + File.separator;
945 }
946
947 /** Returns the absolute filename of the loaded collection's export directory.
948 */
949 static public String getLoadedCollectionExportDirectoryPath()
950 {
951 return getLoadedCollectionDirectoryPath() + "export" + File.separator;
952 }
953
954
955
956 /** Returns the absolute filename of the loaded collection's building directory.
957 */
958 static public String getLoadedCollectionBuildingDirectoryPath()
959 {
960 return getLoadedCollectionDirectoryPath() + "building" + File.separator;
961 }
962
963
964 /** Returns the absolute filename of the loaded collection's collect.cfg file.
965 */
966 static public String getLoadedCollectionCfgFilePath()
967 {
968 String path = (Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG;
969 return getLoadedCollectionEtcDirectoryPath() + path;
970 }
971
972
973 /** Returns the absolute filename of the loaded collection's directory.
974 */
975 static public String getLoadedCollectionDirectoryPath()
976 {
977 return collection.getCollectionDirectory().getPath() + File.separator;
978 }
979
980
981 /** Returns the absolute filename of the loaded collection's etc directory.
982 */
983 static public String getLoadedCollectionEtcDirectoryPath()
984 {
985 return getLoadedCollectionDirectoryPath() + "etc" + File.separator;
986 }
987
988
989 /** Returns the absolute filename of the loaded collection's .col file.
990 */
991 static public String getLoadedCollectionColFilePath()
992 {
993 return getLoadedCollectionDirectoryPath() + "gli.col";
994 }
995
996
997 /** Returns the absolute filename of the loaded collection's images directory.
998 */
999 static public String getLoadedCollectionImagesDirectoryPath()
1000 {
1001 return getLoadedCollectionDirectoryPath() + "images" + File.separator;
1002 }
1003
1004
1005 /** Returns the absolute filename of the loaded collection's import directory.
1006 */
1007 static public String getLoadedCollectionImportDirectoryPath()
1008 {
1009 return getLoadedCollectionDirectoryPath() + "import" + File.separator;
1010 }
1011
1012
1013 /** Returns the absolute filename of the loaded collection's index directory.
1014 */
1015 static public String getLoadedCollectionIndexDirectoryPath()
1016 {
1017 return getLoadedCollectionDirectoryPath() + "index" + File.separator;
1018 }
1019
1020
1021 /** Returns the absolute filename of the loaded collection's log directory.
1022 */
1023 static public String getLoadedCollectionLogDirectoryPath()
1024 {
1025 return getLoadedCollectionDirectoryPath() + "log" + File.separator;
1026 }
1027
1028
1029 /** Returns the absolute filename of the loaded collection's macros directory.
1030 */
1031 static public String getLoadedCollectionMacrosDirectoryPath()
1032 {
1033 return getLoadedCollectionDirectoryPath() + "macros" + File.separator;
1034 }
1035
1036
1037 /** Returns the absolute filename of the loaded collection's metadata directory.
1038 */
1039 static public String getLoadedCollectionMetadataDirectoryPath()
1040 {
1041 return getLoadedCollectionDirectoryPath() + "metadata" + File.separator;
1042 }
1043
1044
1045 /** Returns the (group-qualified) name of the loaded collection with
1046 * OS-dependent file separator.
1047 */
1048 static public String getLoadedCollectionName()
1049 {
1050 return CollectionManager.getLoadedCollectionName(false);
1051 }
1052
1053 /** Returns the (group-qualified) name of the loaded collection with
1054 * OS-dependent space separator.
1055 * @url true if url-type forward slashes, false if OS-dependent filesystem slashes.
1056 */
1057 static public String getLoadedCollectionName(boolean url)
1058 {
1059 if (collection != null) {
1060 //return collection.getName();
1061 return collection.getGroupQualifiedName(url);
1062 }
1063
1064 return null;
1065 }
1066
1067 /** @return the subname of any collection (stripped of any collection-group). */
1068 static public String getLoadedCollectionTailName()
1069 {
1070 if (collection != null) {
1071 return collection.getCollectionTailName();
1072 }
1073
1074 return null;
1075 }
1076
1077 /** Returns the "collectionGroupName/collectionName" or just the collectionName
1078 * depending on whether the collection is part of a collection group or not.
1079 * If url = true, then returns the sub-path as a URL (containing / only),
1080 * and if url = false, then the sub-path is returned in filepath form
1081 * (\ or /, depending on the OS).
1082 */
1083 static public String getLoadedGroupQualifiedCollectionName(boolean url)
1084 {
1085 if (collection != null) {
1086 return collection.getGroupQualifiedName(url);
1087 }
1088
1089 return null;
1090 }
1091
1092 public CollectionTree getCollectionTree()
1093 {
1094 if (collection_tree == null) {
1095 collection_tree = new CollectionTree(collection_tree_model, true);
1096 }
1097
1098 return collection_tree;
1099 }
1100
1101
1102 /** Retrieve the tree model associated with the current collection. */
1103 public CollectionTreeModel getCollectionTreeModel()
1104 {
1105 if (collection_tree_model == null && collection != null) {
1106 // Use the import directory to generate a new CollectionTreeModel
1107 collection_tree_model = new CollectionTreeModel(new CollectionTreeNode(new File(getLoadedCollectionImportDirectoryPath())));
1108 // Ensure that the manager is a change listener for the tree.
1109 if (fm_tree_model_listener == null) {
1110 fm_tree_model_listener = new FMTreeModelListener();
1111 }
1112 collection_tree_model.addTreeModelListener(fm_tree_model_listener);
1113 }
1114 return collection_tree_model;
1115 }
1116
1117
1118 /** This method when called, creates a new GShell in order to run the import.pl script.
1119 * @see org.greenstone.gatherer.Configuration
1120 * @see org.greenstone.gatherer.Gatherer
1121 * @see org.greenstone.gatherer.gui.BuildOptions
1122 * @see org.greenstone.gatherer.shell.GShell
1123 * @see org.greenstone.gatherer.shell.GShellListener
1124 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
1125 * @see org.greenstone.gatherer.util.Utility
1126 */
1127 public void importCollection() {
1128 importing = true;
1129
1130 if (!saved()) {
1131 DebugStream.println("CollectionManager.importCollection().forcesave");
1132 import_monitor.saving();
1133 saveCollection();
1134 }
1135
1136 DebugStream.println("CollectionManager.importCollection()");
1137 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
1138 //check that we can remove the old index before starting import
1139 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1140 if (index_dir.exists()) {
1141 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
1142 if (!canDelete(index_dir)) {
1143 // tell the user
1144 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1145 // tell the gui manager
1146 // a message for the building log
1147 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
1148 Gatherer.g_man.create_pane.message(event);
1149 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
1150 Gatherer.g_man.create_pane.processComplete(event);
1151 importing = false;
1152 return;
1153 }
1154 }
1155
1156 // Generate the import.pl command
1157 ArrayList command_parts_list = new ArrayList();
1158 if (!Gatherer.isGsdlRemote) {
1159 command_parts_list.add(Configuration.perl_path);
1160 command_parts_list.add("-S");
1161 }
1162
1163 if (Configuration.fedora_info.isActive()) {
1164 command_parts_list.add(scriptPath + "g2f-import.pl");
1165
1166 command_parts_list.add("-hostname");
1167 command_parts_list.add(Configuration.fedora_info.getHostname());
1168
1169 command_parts_list.add("-port");
1170 command_parts_list.add(Configuration.fedora_info.getPort());
1171
1172 command_parts_list.add("-username");
1173 command_parts_list.add(Configuration.fedora_info.getUsername());
1174
1175 command_parts_list.add("-password");
1176 command_parts_list.add(Configuration.fedora_info.getPassword());
1177
1178 command_parts_list.add("-protocol");
1179 command_parts_list.add(Configuration.fedora_info.getProtocol());
1180 }
1181 else {
1182 String cmdPrefix = null;
1183 if ( CollectionDesignManager.isCompleteBuild() ) {
1184 cmdPrefix = "full-";
1185 CollectionDesignManager.setImportWasFull( true );
1186 } else {
1187 cmdPrefix = "incremental-";
1188 CollectionDesignManager.setImportWasFull( false );
1189 }
1190 command_parts_list.add(scriptPath + cmdPrefix + "import.pl"); // scriptPath already set according to local or remote case
1191 }
1192
1193 command_parts_list.add("-gli");
1194 command_parts_list.add("-language");
1195 command_parts_list.add(Configuration.getLanguage());
1196
1197 if(Gatherer.GS3) {
1198 command_parts_list.add("-site");
1199 command_parts_list.add(Configuration.site_name);
1200 }
1201
1202 if(!Gatherer.isGsdlRemote) {
1203 command_parts_list.add("-collectdir");
1204 command_parts_list.add(getCollectDirectory());
1205 }
1206
1207 String[] import_options = collection.import_options.getValues();
1208 for (int i = 0; i < import_options.length; i++) {
1209 System.err.println( "Tacking on option: " + import_options[i] );
1210 command_parts_list.add(import_options[i]);
1211 }
1212
1213 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
1214
1215 // Run the import.pl command
1216 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1217 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
1218 //shell.setEventProperty("is_incremental", Boolean.toString(is_incremental));
1219 shell.addGShellListener(Gatherer.g_man.create_pane);
1220 shell.addGShellListener(Gatherer.g_man.format_pane);
1221 shell.start();
1222 DebugStream.println("CollectionManager.importCollection().return");
1223
1224 importing = false;
1225 }
1226
1227
1228 public void importMetadataSet(MetadataSet external_metadata_set)
1229 {
1230 // Copy the .mds file into the collection's "metadata" folder...
1231 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
1232
1233 // ...but not if it is the redundant "hidden.mds" file
1234 if (external_metadata_set_file.getName().equals("hidden.mds")) {
1235 return;
1236 }
1237
1238 // ...and only if it doesn't already exist
1239 File metadata_set_file = new File(getLoadedCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
1240 if (!metadata_set_file.exists()) {
1241 try {
1242 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, false);
1243
1244 // If we're using a remote Greenstone server, upload the metadata file
1245 if (Gatherer.isGsdlRemote) {
1246 Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1247 }
1248 }
1249 catch (Exception exception) {
1250 DebugStream.printStackTrace(exception);
1251 }
1252
1253 // Load it into the MetadataSetManager
1254 MetadataSetManager.loadMetadataSet(metadata_set_file);
1255 }
1256 }
1257
1258
1259 /** Determine if we are currently in the middle of importing (and thus, in this case, we can't allow the log writer to exit). Boy was this a mission to track down. The cascade of crap rolls out something like this: Joe Schmo clicks 'Build Collection', which calls the importCollection() method above, which in turn saves the collection with a saveTask, which fires a collectionChanged message once its finished, which drives the list of logs shown on the create pane to update, which fires a itemChanged() event to the OptionsPane who dutifully tells the current log writer thread to finish up writing (all zero lines its been asked to write) and then die. Wereapon Joe Schmo gets a pretty log to look at, but it isn't actually being written to file so the next time he tries to view it faeces hits the air motion cooling device. Joy.
1260 * @return true if the gli is currently importing
1261 */
1262 public boolean isImporting() {
1263 return importing;
1264 }
1265
1266
1267 public void loadCollection(String collection_file_path)
1268 {
1269 // Display a modal progress popup to indicate that the collection is being loaded
1270 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
1271 load_collection_progress_popup.display();
1272
1273 // Load the collection on a separate thread so the progress bar updates correctly
1274 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
1275 }
1276
1277
1278 private class LoadCollectionTask
1279 extends Thread
1280 {
1281 private String collection_file_path = null;
1282 private ModalProgressPopup load_collection_progress_popup = null;
1283
1284 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
1285 {
1286 this.collection_file_path = collection_file_path;
1287 this.load_collection_progress_popup = load_collection_progress_popup;
1288 }
1289
1290 public void run()
1291 {
1292 loadCollectionInternal(collection_file_path);
1293 load_collection_progress_popup.close();
1294 Gatherer.setMenuBarEnabled(true);
1295 }
1296 }
1297
1298
1299 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
1300 * @param location The path to the collection as a <strong>String</strong>.
1301 * @see org.greenstone.gatherer.Configuration
1302 * @see org.greenstone.gatherer.Gatherer
1303 * @see org.greenstone.gatherer.collection.Collection
1304 * @see org.greenstone.gatherer.util.Utility
1305 */
1306 private void loadCollectionInternal(String location)
1307 {
1308 DebugStream.println("Loading collection " + location + "...");
1309
1310
1311 // Check we have actually been given a .col file.
1312 if (!location.endsWith(".col")) {
1313 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1314 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
1315 return;
1316 }
1317
1318 // Check that the collection configuration file is available
1319 File collection_file = new File(location);
1320
1321 //String collection_name = collection_directory.getName();
1322 String collection_name = "";
1323 File collection_directory = collection_file.getParentFile();
1324
1325 // To get colname = (colgroup/)coltailname, subtract Gatherer.getCollectDirectoryPath() from collection_directory:
1326 int index = collection_directory.getAbsolutePath().indexOf(Gatherer.getCollectDirectoryPath());
1327 if(index == -1) {
1328 System.err.println("*** ERROR: collection directory " + collection_directory + " is not located in collect folder: " + Gatherer.getCollectDirectoryPath());
1329 } else {
1330 index += Gatherer.getCollectDirectoryPath().length();
1331 collection_name = collection_directory.getAbsolutePath().substring(index);
1332 }
1333
1334 if (Gatherer.isGsdlRemote) {
1335 if (Gatherer.remoteGreenstoneServer.downloadCollection(collection_name).equals("")) {
1336 return;
1337 }
1338 }
1339
1340 // Ensure that the collection directory exists
1341 if (collection_directory == null || !collection_directory.exists()) {
1342 // We can't open this
1343 System.err.println("CollectionManager.loadCollection: No collection directory.");
1344 return;
1345 }
1346
1347 String file_str = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
1348 File collection_config_file = new File(collection_directory, file_str);
1349 if (!collection_config_file.exists()) {
1350 System.err.println("CollectionManager.loadCollection: No config file.");
1351 collection_directory = null;
1352 collection_config_file = null;
1353 return;
1354 }
1355
1356 // Ensure that an import directory exists for this collection
1357 File collection_import_directory = new File(collection_directory, "import");
1358 if (!collection_import_directory.exists()) {
1359 collection_import_directory.mkdir();
1360 }
1361
1362 // Special case of a user trying to open an old greenstone collection.
1363 boolean non_gli_collection = false;
1364 File collection_metadata_directory = new File(collection_directory, "metadata");
1365 if (!collection_metadata_directory.exists()) {
1366 DebugStream.println("Loading non-gatherer collection...");
1367 // Show a warning message in case user wants to quit now
1368 non_gli_collection = true;
1369 WarningDialog legacy_dialog = new WarningDialog("warning.LegacyCollection", Dictionary.get("LegacyCollection.Title"), Dictionary.get("LegacyCollection.Message"), null, true);
1370 if (legacy_dialog.display()==JOptionPane.CANCEL_OPTION) {
1371 legacy_dialog.dispose();
1372 collection_directory = null;
1373 collection_config_file = null;
1374 return;
1375 }
1376 legacy_dialog.dispose();
1377
1378 }
1379
1380 // Now determine if a lock already exists on this collection.
1381 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
1382 if (lock_file.exists()) {
1383 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, collection_name, lock_file);
1384 int choice = dialog.getChoice();
1385 dialog.dispose();
1386 dialog = null;
1387
1388 if (choice != LockFileDialog.YES_OPTION) {
1389 // user has cancelled
1390 lock_file = null;
1391 collection_directory = null;
1392 collection_config_file = null;
1393 return;
1394 }
1395
1396 lock_file.delete();
1397 }
1398
1399 // now we are using gli.col - old colls may have used the collection name
1400 if (!collection_file.exists()) {
1401 File old_coll_file = new File(collection_directory, collection_name+".col");
1402 if (old_coll_file.exists()) {
1403 try {
1404 old_coll_file.renameTo(collection_file);
1405 } catch (Exception e) {
1406 DebugStream.println("Couldn't rename "+old_coll_file.getName()+" to gli.col. Will just carry on with default gli.col");
1407 // but just carry on.
1408 }
1409 }
1410 }
1411
1412 try {
1413 // Create a lock file.
1414 createLockFile(lock_file);
1415 // This lock file may not have been created so check
1416 if(!lock_file.canWrite()) {
1417 // The lock file cannot be written to. Most likely cause incorrect file permissions.
1418 System.err.println("Cannot write lock file!");
1419 String args[] = new String[2];
1420 args[0] = location;
1421 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{lock_file.getAbsolutePath()});
1422 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS")!=-1){
1423 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1424 args[1] += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1425 //}
1426 }
1427 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1428 args = null;
1429 return;
1430 }
1431
1432 // need to fix this up as currently it craps out if the .col file is not there, which is may not always be.
1433 if (canDoScheduling() && collection_file.exists()) {
1434 //THIS LOOKS LIKE THE BEST PLACE TO TRY AND UPDATE .col FILES FOR EXISTING COLLECTIONS...Wendy
1435 // Don't need to update anything if collection_file doesn't exist yet.
1436 //First, see if "Schedule" exists in the XMl File...
1437 BufferedReader bir = new BufferedReader(new FileReader(collection_file));
1438 boolean flag = false;
1439 try {
1440 String stmp = new String();
1441
1442 while((stmp = bir.readLine()) != null) {
1443 stmp = stmp.trim();
1444 if(stmp.equals("<Schedule>") || stmp.equals("<Schedule/>")) {
1445 flag = true;
1446 break;
1447 }
1448 }
1449 bir.close();
1450
1451 } catch (IOException ioe) {
1452 DebugStream.printStackTrace(ioe);
1453 }
1454
1455 //modify if old .col (i.e. no Schedule exists in XML file)
1456 if(!flag) {
1457 File new_collection_file = new File(collection_directory.getAbsolutePath() + "/tmp.col");
1458
1459
1460 BufferedWriter bor = new BufferedWriter(new FileWriter(new_collection_file));
1461 bir = new BufferedReader(new FileReader(collection_file));
1462
1463 try {
1464 String stmp = new String();
1465 while((stmp = bir.readLine()) != null) {
1466 String stmp2 = stmp.trim();
1467 if(stmp2.startsWith("<!ELEMENT Argument")) {
1468 bor.write(" <!ELEMENT Schedule (Arguments*)>\n");
1469 }
1470 else if(stmp2.equals("</BuildConfig>")) {
1471 bor.write(" <Schedule/>\n");
1472 }
1473
1474 bor.write(stmp + "\n");
1475
1476 }
1477 bir.close();
1478 bor.close();
1479 } catch (IOException ioe) {
1480 DebugStream.printStackTrace(ioe);
1481 }
1482
1483 //copy over tmp.col to replace
1484 try {
1485 collection_file.delete();
1486 new_collection_file.renameTo(collection_file);
1487 } catch (Exception e) {
1488 DebugStream.printStackTrace(e);
1489 }
1490 }
1491 }
1492
1493 // Open the collection file
1494 this.collection = new Collection(collection_file);
1495 if (collection.error) {
1496 collection = null;
1497 // Remove lock file
1498 if (lock_file.exists()) {
1499 lock_file.delete();
1500 }
1501 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
1502 }
1503
1504 if (canDoScheduling()) {
1505 scheduling();
1506 }
1507
1508 // These may have been set in the past, but are no longer used
1509 // by GLI
1510 collection.import_options.removeValue("removeold");
1511 collection.import_options.removeValue("keepold");
1512
1513 MetadataSetManager.clearMetadataSets();
1514 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
1515
1516 // Make sure we always have the extracted metadata set
1517 addRequiredMetadataSets();
1518
1519 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
1520
1521 // If this is a non-GLI (legacy) collection, load the default metadata sets
1522 if (non_gli_collection) {
1523 addDefaultMetadataSets();
1524
1525 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
1526 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
1527 }
1528
1529 // Read through the metadata.xml files in the import directory, building up the metadata value trees
1530 MetadataXMLFileManager.clearMetadataXMLFiles();
1531 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory,collection.toSkimFile());
1532
1533
1534 // get rid of the previous scan through docxml files
1535 DocXMLFileManager.clearDocXMLFiles();
1536
1537 if (Configuration.fedora_info.isActive()) { // FLI case
1538 // Read through the docmets.xml files in the export directory
1539 File collection_export_directory = new File(getLoadedCollectionExportDirectoryPath());
1540 DocXMLFileManager.loadDocXMLFiles(collection_export_directory,"docmets.xml");
1541 }
1542 else {
1543 // Read through the doc.xml files in the archives directory
1544 File collection_archives_directory = new File(getLoadedCollectionArchivesDirectoryPath());
1545 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory,"doc.xml");
1546 }
1547
1548
1549 // Get a list of the collection specific classifiers and plugins
1550 Classifiers.loadClassifiersList(collection_name);
1551 Plugins.loadPluginsList(collection_name);
1552
1553 collection.cdm = new CollectionDesignManager(collection_config_file);
1554 if (non_gli_collection) {
1555 // Change the classifiers to use the namespaced element names
1556 LegacyCollectionImporter.updateClassifiers(collection.cdm);
1557 }
1558
1559 // We're done. Let everyone know.
1560 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", collection_name));
1561 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
1562 }
1563 catch (Exception error) {
1564 // There is obviously no existing collection present.
1565 DebugStream.printStackTrace(error);
1566 error.printStackTrace();
1567 if(error.getMessage() != null) {
1568 String[] args = new String[2];
1569 args[0] = location;
1570 args[1] = error.getMessage();
1571 //args[1] = "The Librarian Interface does not have permission to write to... Please check file permissions.";
1572 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1573 }
1574 else {
1575 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1576 }
1577 }
1578
1579 lock_file = null;
1580 collection_directory = null;
1581 collection_config_file = null;
1582 }
1583
1584 /** At present, scheduling only works for GS2, only when GS2 is local and only when GLI runs from
1585 * within a GS2 installation. This method can be adjusted as scheduling becomes available for more
1586 * more situations. */
1587 public static boolean canDoScheduling() {
1588 // Would be nice to support more of these, rather than returning false
1589 if(Gatherer.isGsdlRemote) {
1590 return false;
1591 }
1592 if(Gatherer.GS3) {
1593 return false;
1594 }
1595 if (Configuration.fedora_info.isActive()) {
1596 return false;
1597 }
1598
1599 // GS2's etc/main.cfg is necessary for scheduling, but scheduling looks for it locally:
1600 // it assumes GLI is inside a GS2 installation
1601 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
1602 if(!mcfg.exists()) {
1603 System.out.println("Cannot do scheduling, since there is no file: " + mcfg.getAbsolutePath()
1604 + ".\nScheduling presently depends on GLI running from inside a GS2.");
1605 return false;
1606 }
1607
1608 return true;
1609 }
1610
1611 private void makeCollection(String name, String email)
1612 {
1613 // Generate the mkcol.pl command
1614 ArrayList command_parts_list = new ArrayList();
1615 if (!Gatherer.isGsdlRemote) {
1616 command_parts_list.add(Configuration.perl_path);
1617 command_parts_list.add("-S");
1618 }
1619 command_parts_list.add(scriptPath + "mkcol.pl");
1620 if(Gatherer.GS3) {
1621 command_parts_list.add(Utility.GS3MODE_ARGUMENT); // add '-gs3mode'
1622 command_parts_list.add("-site");
1623 command_parts_list.add(Configuration.site_name);
1624 }
1625
1626 if(!Gatherer.isGsdlRemote) {
1627 command_parts_list.add("-collectdir");
1628 command_parts_list.add(getDefaultCollectDirectory());
1629 }
1630 command_parts_list.add("-win31compat");
1631 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
1632
1633 if (email != null && !email.equals("")) {
1634 command_parts_list.add("-creator");
1635 command_parts_list.add(email);
1636 }
1637
1638 command_parts_list.add(name);
1639
1640 // Run the mkcol.pl command
1641 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1642 //for(int i = 0; i < command_parts.length; i++) {
1643 ///ystem.err.println("\""+command_parts[i]+"\"");
1644 //}
1645
1646 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1647 process.run(); // Don't bother threading this... yet
1648 }
1649
1650
1651 /** Any implementation of GShellListener must include this method to allow the GShell to send messages to listeners. However in this case the CollectionManager is in no way interested in what the messages are, just the import events which have a specific type and are handled elsewhere. Thus we can safely ignore this event.
1652 * @param event A <strong>GShellEvent</strong> which contains a the message.
1653 */
1654 public synchronized void message(GShellEvent event) {
1655
1656 }
1657
1658
1659 public void metadataChanged(CollectionTreeNode[] file_nodes)
1660 {
1661 if (collection != null) {
1662 collection.setMetadataChanged(true);
1663
1664 // we're only going to refresh the tree's already visible nodes (and reselect them) IFF
1665 // gs.FilenameEncoding meta was set on any files/folders and the filenames of the
1666 // affected CollectionTreeNodes need to be recalculated
1667 if(FilenameEncoding.isRefreshRequired()) {
1668
1669 // refreshes the relevant part of the tree
1670 TreePath[] paths = collection_tree.getSelectionPaths();
1671 if(paths != null) {
1672 for(int i = 0; i < paths.length; i++) {
1673 collection_tree_model.refresh(paths[i]);
1674 collection_tree.setSelectionPath(paths[i]);
1675 }
1676 }
1677 // refreshed the tree, so turn off the requirement to refresh
1678 FilenameEncoding.setRefreshRequired(false);
1679 }
1680 }
1681 }
1682
1683
1684 public void openCollectionFromLastTime() {
1685 // If there was an open collection last session, reopen it.
1686 // (this method doesn't get called if there was no open collection or only a collect folder
1687 // instead of any previously-opened collection)
1688
1689 // Load the collection now
1690 loadCollection(Gatherer.open_collection_file_path);
1691 }
1692
1693
1694 /** This call is fired whenever a process within a GShell created by this class begins.
1695 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1696 * @see org.greenstone.gatherer.Gatherer
1697 * @see org.greenstone.gatherer.gui.GUIManager
1698 * @see org.greenstone.gatherer.shell.GShell
1699 */
1700 public synchronized void processBegun(GShellEvent event) {
1701 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1702 ///ystem.err.println("ProcessBegun " + event.getType());
1703 // If this is one of the types where we wish to lock user control
1704 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1705 }
1706 /** This call is fired whenever a process within a GShell created by this class ends.
1707 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1708 * @see org.greenstone.gatherer.Gatherer
1709 * @see org.greenstone.gatherer.gui.GUIManager
1710 * @see org.greenstone.gatherer.shell.GShell
1711 */
1712 public synchronized void processComplete(GShellEvent event) {
1713 //ystem.err.println("CollectionManager.processComplete(" + event.getType() + ")");
1714 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1715 ///ystem.err.println("Received process complete event - " + event);
1716 // If we were running an import, now run a build.
1717 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1718 // Finish import.
1719 collection.setImported(true);
1720 collection.setFilesChanged(false);
1721 collection.setMetadataChanged(false);
1722 buildCollection();
1723 }
1724 else if(event.getType() == GShell.SCHEDULE && event.getStatus() == GShell.OK ) {
1725
1726 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.ScheduleBuilt", Dictionary.get("ScheduleBuilt.Title"), Dictionary.get("ScheduleBuilt.Message"), null, false);
1727 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1728 collection_built_warning_dialog.display();
1729 collection_built_warning_dialog.dispose();
1730 collection_built_warning_dialog = null;
1731 }
1732 // If we were running a build, now is when we move files across.
1733 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1734
1735 if ( CollectionDesignManager.buildcolWasFull() ) {
1736 if(installCollection()) {
1737 // If we have a local library running then ask it to add our newly create collection
1738 if (LocalLibraryServer.isRunning() == true) {
1739 LocalLibraryServer.addCollection(collection.getName());
1740 }
1741 else if (Gatherer.GS3) {
1742 //xiao comment out this: convertToGS3Collection();
1743 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1744 }
1745
1746 // Fire a collection changed first to update the preview etc buttons
1747 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1748
1749 // Now display a message dialog saying its all built
1750 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", Dictionary.get("CollectionBuilt.Title"), Dictionary.get("CollectionBuilt.Message"), null, false);
1751 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1752 collection_built_warning_dialog.display();
1753 collection_built_warning_dialog.dispose();
1754 collection_built_warning_dialog = null;
1755
1756 //Set nothing as needing rebuilding, as a build has just finished :-)
1757 CollectionDesignManager.resetRebuildTypeRequired();
1758 }
1759 else {
1760 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1761 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1762 DebugStream.println("Status is ok but !installCollection()");
1763 }
1764 }
1765 }
1766
1767 else if(event.getType() == GShell.DELETE) {
1768
1769 // we can only get here if we tried to delete a fedora collection
1770
1771 if(event.getStatus() == GShell.ERROR) { // error purging the collection from fedora
1772
1773 JOptionPane.showMessageDialog(Gatherer.g_man,Dictionary.get("DeleteCollectionPrompt.Failed_Fedora_Delete", new String[]{delete_collection_name}),Dictionary.get("DeleteCollectionPrompt.Failed_Title"),JOptionPane.WARNING_MESSAGE);
1774 delete_collection_name = null; // re-zero
1775 }
1776
1777 else if(event.getStatus() == GShell.OK) { // fedora purge was successful
1778
1779 if(delete_collection_name != null) {
1780 if (Gatherer.isGsdlRemote) {
1781 Gatherer.remoteGreenstoneServer.deleteCollection(delete_collection_name);
1782 }
1783
1784 // if Greenstone3, need to deactivate the collection on the server
1785 if (Gatherer.GS3) {
1786 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + delete_collection_name);
1787 }
1788
1789 // Now at last, can delete the collection directory as for a normal collection
1790 boolean success = Utility.delete(new File(getCollectionDirectoryPath(delete_collection_name)));
1791
1792 if (!success) {
1793 JOptionPane.showMessageDialog(Gatherer.g_man,Dictionary.get("DeleteCollectionPrompt.Failed_Delete", new String[]{delete_collection_name}),Dictionary.get("DeleteCollectionPrompt.Failed_Title"),JOptionPane.WARNING_MESSAGE);
1794 }
1795 delete_collection_name = null; // re-zero
1796 }
1797
1798 }
1799 }
1800
1801 else if (event.getStatus() == GShell.CANCELLED) {
1802 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1803 Gatherer.g_man.repaint();
1804 }
1805 else if (event.getStatus() == GShell.ERROR) {
1806 if (event.getType() == GShell.NEW) {
1807 String name = event.getMessage();
1808 String collectDir = getCollectionDirectoryPath(name);
1809 String errMsg = "";
1810 if (!new File(getCollectionDirectoryPath(name)).exists() || !new File(getCollectionDirectoryPath(name)).canWrite()) {
1811 String reason = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{collectDir});
1812 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", new String[]{reason});
1813 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS") != -1){
1814 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1815 errMsg += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1816 //}
1817 }
1818 } else {
1819 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection");
1820 }
1821 JOptionPane.showMessageDialog(Gatherer.g_man, errMsg, Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1822 }
1823 else if(event.getType() == GShell.SCHEDULE) {
1824 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Schedule_Failed"), Dictionary.get("CollectionManager.Schedule_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1825 }
1826 else {
1827 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1828 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1829 }
1830
1831 Gatherer.g_man.repaint(); // It appears Java's own dialogs have the same not always painting correct area bug that I suffer from. Well I don't suffer from it personally, but my ModalDialog components do.
1832 }
1833 }
1834
1835
1836 /** Determine if the manager is ready for actions apon its collection.
1837 * @return A <i>boolean</i> which is <i>true</i> to indicate a collection has been loaded and thus the collection is ready for editing, <i>false</i> otherwise.
1838 */
1839 static public synchronized boolean ready() {
1840 if(collection != null) {
1841 return true;
1842 }
1843 else {
1844 return false;
1845 }
1846 }
1847
1848
1849 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1850 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1851 */
1852 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1853 build_monitor = monitor;
1854 }
1855 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1856 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1857 */
1858 public void registerImportMonitor(GShellProgressMonitor monitor) {
1859 import_monitor = monitor;
1860 }
1861
1862 public void registerScheduleMonitor(GShellProgressMonitor monitor) {
1863 schedule_monitor = monitor;
1864 }
1865
1866
1867 static public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1868 {
1869 collection_contents_changed_listeners.remove(listener);
1870 }
1871
1872
1873 public void removeMetadataSet(MetadataSet metadata_set)
1874 {
1875 DebugStream.println("Removing metadata set...");
1876
1877 // Delete the .mds file from the collection's "metadata" folder...
1878 File metadata_set_file = metadata_set.getMetadataSetFile();
1879
1880 // ...but not if it is the "ex.mds" file
1881 if (metadata_set_file.getName().equals("ex.mds")) {
1882 return;
1883 }
1884
1885 // ...and only if it exists
1886 if (metadata_set_file.exists()) {
1887 metadata_set_file.delete();
1888
1889 // Unload it from the MetadataSetManager
1890 MetadataSetManager.unloadMetadataSet(metadata_set);
1891
1892 // If we're using a remote Greenstone server, delete the metadata file on the server
1893 if (Gatherer.isGsdlRemote) {
1894 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1895 }
1896 }
1897 }
1898
1899
1900 /** Used to check whether all open collections have a 'saved' state.
1901 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1902 * @see org.greenstone.gatherer.collection.Collection
1903 */
1904 public boolean saved() {
1905 boolean result = true;
1906 if(collection != null) {
1907 result = collection.getSaved();
1908 }
1909 return result;
1910 }
1911
1912
1913 /** Saves the currently loaded collection. */
1914 public void saveCollection()
1915 {
1916
1917 if (collection == null) return;
1918
1919 DebugStream.println("Saving collection " + collection.getName() + "...");
1920
1921 // Change cursor to hourglass
1922 Gatherer.g_man.wait(true);
1923
1924 // Create a backup of the collection file, just in case anything goes wrong
1925 File collection_file = new File(getLoadedCollectionColFilePath());
1926 if (collection_file.exists()) {
1927 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1928 if (!collection_file.renameTo(collection_file_backup)) {
1929 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1930 }
1931 collection_file_backup.deleteOnExit();
1932 }
1933
1934 // Write out the collection file
1935 collection.save();
1936
1937 // Write out the collection configuration file
1938 collection.cdm.save();
1939
1940 // Change cursor back to normal
1941 Gatherer.g_man.wait(false);
1942 }
1943
1944
1945 /** I started giving the user the choice of using an existing meta set or creating a new one. The second option being so that they didn't have to add/merge/ignore each element, they could all be added automatically. However, I am not sure where the merge prompt gets called from, and it is not essential, so I am leaving it for now - it should be added back in and finished. [kjdon] */
1946 // now add in greenstone metadata set too.
1947 private void addDefaultMetadataSets()
1948 {
1949 // Add dublin core which is the default metadata set. The user
1950 // can change this later
1951 File dc_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"dublin.mds");
1952 if (dc_file.exists()) {
1953 importMetadataSet(new MetadataSet(dc_file));
1954 }
1955 File gs_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"greenstone.mds");
1956 if (gs_file.exists()) {
1957 importMetadataSet(new MetadataSet(gs_file));
1958 }
1959 }
1960
1961
1962 private void addRequiredMetadataSets()
1963 {
1964 // Always import the extracted metadata set
1965 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1966 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1967 }
1968
1969 private String getDefaultCollectDirectory() {
1970 String collect_dir = Gatherer.getCollectDirectoryPath();
1971 // Remove erroneous file windows file separator as it causes problems when running import.pl
1972 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1973 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1974 }
1975 return collect_dir;
1976 }
1977
1978 // used as arg in the perl scripts
1979 private String getCollectDirectory() {
1980 String collect_dir = Gatherer.getCollectDirectoryPath();
1981 return collect_dir.substring(0, collect_dir.length()-1); // remove trailing slash
1982
1983 // the following will stick any colgroup at the end of the collect directory, making it no longer
1984 // possible to get the real collect dir in a general manner if this were located outside greenstone
1985 //String collect_dir = collection.getCollectionDirectory().getParentFile().getPath();
1986 //return collect_dir;
1987 }
1988
1989
1990 /** Install collection by moving its files from building to index after a successful build.
1991 * @see org.greenstone.gatherer.Gatherer
1992 * @see org.greenstone.gatherer.util.Utility
1993 */
1994 private boolean installCollection()
1995 {
1996 if (Configuration.fedora_info.isActive()) {
1997 DebugStream.println("Fedora build complete. No need to move files.");
1998 return true;
1999 }
2000
2001
2002 DebugStream.println("Build complete. Moving files.");
2003
2004 try {
2005 // Ensure that the local library has released this collection so we can delete the index directory
2006 if (LocalLibraryServer.isRunning() == true) {
2007 LocalLibraryServer.releaseCollection(getLoadedCollectionName(true)); // URL style slash //collection.getName());
2008 }
2009 // deactivate it in tomcat so that windows will release the index files
2010 if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
2011 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection.getName());
2012 }
2013 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
2014 DebugStream.println("Index = " + index_dir.getAbsolutePath());
2015
2016 File building_dir = new File(getLoadedCollectionBuildingDirectoryPath());
2017 DebugStream.println("Building = " + building_dir.getAbsolutePath());
2018
2019 // Get the build mode from the build options
2020 String build_mode = collection.build_options.getValue("mode");
2021
2022 // Special case for build mode "all": replace index dir with building dir
2023 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
2024 // Remove the old index directory
2025 if (index_dir.exists()) {
2026 Utility.delete(index_dir);
2027
2028 // Wait for a couple of seconds, just for luck
2029 wait(2000);
2030
2031 // Check the delete worked
2032 if (index_dir.exists()) {
2033 throw new Exception(Dictionary.get("CollectionManager.Index_Not_Deleted"));
2034 }
2035 }
2036
2037 if (Gatherer.isGsdlRemote) {
2038 Gatherer.remoteGreenstoneServer.deleteCollectionFile(
2039 collection.getGroupQualifiedName(false), new File(getLoadedCollectionIndexDirectoryPath()));
2040 Gatherer.remoteGreenstoneServer.moveCollectionFile(collection.getGroupQualifiedName(false),
2041 new File(getLoadedCollectionBuildingDirectoryPath()), new File(getLoadedCollectionIndexDirectoryPath()));
2042 }
2043
2044 // Move the building directory to become the new index directory
2045 if (building_dir.renameTo(index_dir) == false) {
2046 throw new Exception(Dictionary.get("CollectionManager.Build_Not_Moved"));
2047 }
2048 }
2049
2050 // Otherwise copy everything in the building dir into the index dir
2051 else {
2052 moveContentsInto(building_dir, index_dir);
2053 }
2054 }
2055 catch (Exception exception) {
2056 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Install_Exception", exception.getMessage()), "Error", JOptionPane.ERROR_MESSAGE);
2057 return false;
2058 }
2059 return true;
2060 }
2061
2062
2063 /** Moves all the files in one directory into another, overwriting existing files */
2064 private void moveContentsInto(File source_directory, File target_directory)
2065 {
2066 File[] source_files = source_directory.listFiles();
2067 for (int i = 0; i < source_files.length; i++) {
2068 File source_file = source_files[i];
2069 File target_file = new File(target_directory, source_file.getName());
2070
2071 if (source_file.isDirectory()) {
2072 moveContentsInto(source_file, target_file);
2073 source_file.delete();
2074 }
2075 else {
2076 if (target_file.exists()) {
2077 target_file.delete();
2078 }
2079
2080 source_file.renameTo(target_file);
2081 }
2082 }
2083 }
2084
2085 private void updateCollectionConfigXML(File base_cfg, File new_cfg) {
2086 //In this method, the files base_cfg and new_cfg are all xml files.
2087
2088 Document base_cfg_doc = XMLTools.parseXMLFile(base_cfg);
2089 XMLTools.writeXMLFile(new_cfg, base_cfg_doc);
2090 Document new_cfg_doc = XMLTools.parseXMLFile(new_cfg);
2091 Element collection_config = new_cfg_doc.getDocumentElement();
2092
2093 Node browseNode = XMLTools.getChildByTagNameIndexed(collection_config, StaticStrings.BROWSE_STR, 0);
2094 NodeList classifier_children = ((Element)browseNode).getElementsByTagName(StaticStrings.CLASSIFIER_STR);
2095 int num_nodes = classifier_children.getLength();
2096
2097 if (num_nodes < 1) {
2098 return;
2099 }
2100
2101 // Read in the classifier command watching for hfile, metadata and sort arguments.
2102 String buttonname = null;
2103 String hfile = null;
2104 String metadata = null;
2105 String sort = null;
2106
2107 for (int i=0; i<num_nodes; i++) {
2108 Element classifier_element = (Element)classifier_children.item(i);
2109 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
2110 for (int j=0; j<option_children.getLength(); j++) {
2111 Element option_element = (Element)option_children.item(j);
2112 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
2113 String value_str = option_element.getAttribute(StaticStrings.VALUE_ATTRIBUTE);
2114
2115 if (name_str == null || name_str.equals("")) {
2116 continue;
2117 }
2118 if (name_str != null && value_str == null ) {
2119 value_str = "";
2120 }
2121 if (name_str.equals("hfile")) {
2122 hfile = value_str;
2123 }
2124 else if (name_str.equals("metadata") && value_str != null) {
2125 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
2126 if (replacement != null && !replacement.equals("")) {
2127 metadata = replacement;
2128 }
2129 }
2130 else if (name_str.equals("sort") && value_str != null) {
2131 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
2132 if (replacement != null && !replacement.equals("")) {
2133 sort = replacement;
2134 }
2135 }
2136 else if(name_str.equals("buttonname") && value_str != null) {
2137 buttonname = value_str;
2138 }
2139 }
2140 }
2141 for (int i=0; i<num_nodes; i++) {
2142 Element classifier_element = (Element)classifier_children.item(i);
2143 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
2144 for (int j=0; j<option_children.getLength(); j++) {
2145 Element option_element = (Element)option_children.item(j);
2146 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
2147
2148 if (name_str.equals("metadata") && metadata != null) {
2149 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
2150 }
2151 else if (name_str.equals("hfile") && hfile != null) {
2152 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata + ".txt");
2153 }
2154 else if (name_str.equals("sort") && sort != null) {
2155 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, sort);
2156 }
2157 else if(name_str.equals("buttonname") && (buttonname == "" || buttonname == null)) {
2158 // No buttonname has been specified. Lets create one using the metadata as its value
2159 Element option = new_cfg_doc.createElement(StaticStrings.OPTION_STR);
2160 option.setAttribute(StaticStrings.NAME_ATTRIBUTE, "buttonname");
2161 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
2162 classifier_element.appendChild(option);
2163 }
2164 }
2165 }
2166 }
2167
2168 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
2169 {
2170 boolean first_name = true;
2171 boolean first_extra = true;
2172
2173 // Now read in base_cfg line by line, parsing important onces and/or replacing them with information pertinent to our collection. Each line is then written back out to the new collect.cfg file.
2174 try {
2175 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
2176 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
2177 String command = null;
2178 while((command = in.readLine()) != null) {
2179 if (command.length()==0) {
2180 // output a new line
2181 out.newLine();
2182 continue;
2183 }
2184 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
2185 while(command.trim().endsWith("\\")) {
2186 command = command.substring(0, command.lastIndexOf("\\"));
2187 String next_line = in.readLine();
2188 if(next_line != null) {
2189 command = command + next_line;
2190 }
2191 }
2192 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
2193 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
2194 String command_type_str = tokenizer.nextToken().toLowerCase();
2195
2196 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
2197 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
2198 StringBuffer new_command = new StringBuffer(command_type_str);
2199 String meta_name = tokenizer.nextToken();
2200 new_command.append(' ');
2201 new_command.append(meta_name);
2202 while (tokenizer.hasMoreTokens()) {
2203 new_command.append(' ');
2204 new_command.append(tokenizer.nextToken());
2205 }
2206 if (meta_name.equals(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR)) {
2207 // dont save
2208 } else {
2209 write(out, new_command.toString());
2210 }
2211 new_command = null;
2212 continue;
2213 } // if collectionmeta
2214
2215 if (command_type_str.equals("classify")) {
2216 StringBuffer text = new StringBuffer(command_type_str);
2217 // Read in the classifier command watching for hfile, metadata and sort arguments.
2218 String buttonname = null;
2219 String hfile = null;
2220 String new_metadata = null;
2221 String old_metadata = null;
2222
2223 while(tokenizer.hasMoreTokens()) {
2224 String token = tokenizer.nextToken();
2225 if (token.equals("-hfile")) {
2226 if(tokenizer.hasMoreTokens()) {
2227 text.append(" ");
2228 text.append(token);
2229 token = tokenizer.nextToken();
2230 hfile = token;
2231 }
2232 }
2233 else if (token.equals("-metadata")) {
2234 if(tokenizer.hasMoreTokens()) {
2235 text.append(" ");
2236 text.append(token);
2237 String temp_metadata = tokenizer.nextToken();
2238 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2239 if (replacement != null && !replacement.equals("")) {
2240 token = replacement;
2241 old_metadata = temp_metadata;
2242 new_metadata = replacement;
2243 }
2244 else {
2245 token = temp_metadata;
2246 }
2247 temp_metadata = null;
2248 replacement = null;
2249 }
2250 }
2251 else if (token.equals("-sort")) {
2252 if(tokenizer.hasMoreTokens()) {
2253 text.append(" ");
2254 text.append(token);
2255 String temp_metadata = tokenizer.nextToken();
2256 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2257 if (replacement != null && !replacement.equals("")) {
2258 token = replacement;
2259 }
2260 else {
2261 token = temp_metadata;
2262 }
2263 temp_metadata = null;
2264 replacement = null;
2265 }
2266 }
2267 else if(token.equals("-buttonname")) {
2268 buttonname = token;
2269 }
2270 text.append(' ');
2271 text.append(token);
2272 token = null;
2273 }
2274
2275 // If we replaced the metadata argument and didn't encounter a buttonname, then add one now pointing back to the old metadata name in order to accomodate macro files which required such names (buttonname is metadata name by default)!
2276 if(old_metadata != null && new_metadata != null && buttonname == null) {
2277 text.append(' ');
2278 text.append("-buttonname");
2279 text.append(' ');
2280 text.append(old_metadata);
2281 }
2282 command = text.toString();
2283 // Replace the hfile if we found it
2284 if(hfile != null && new_metadata != null) {
2285 command = command.replaceAll(hfile, new_metadata + ".txt");
2286 }
2287
2288 buttonname = null;
2289 hfile = null;
2290 new_metadata = null;
2291 old_metadata = null;
2292 write(out, command);
2293 } else {
2294 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
2295 StringBuffer new_command = new StringBuffer(command_type_str);
2296 while (tokenizer.hasMoreTokens()) {
2297 new_command.append(' ');
2298 new_command.append(tokenizer.nextToken());
2299 }
2300
2301 command = new_command.toString();
2302
2303 // There is still one special case, that of the format command. In such a command we have to search for [<target>] to ensure we don't change parts of the format which have nothing to do with the metadata elements.
2304 // we really want to build up the whole command here
2305 boolean format_command = command_type_str.equals("format");
2306 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
2307 if (metadata_mapping != null) {
2308 Iterator keys = metadata_mapping.keySet().iterator();
2309 while (keys.hasNext()) {
2310 String target = (String) keys.next();
2311 String replacement = (String) metadata_mapping.get(target);
2312 if (replacement != null && !replacement.equals("")) {
2313 if (format_command) {
2314 target = "\\[" + target + "\\]";
2315 replacement = "{Or}{[" + replacement + "]," + target + "}";
2316 }
2317 command = command.replaceAll(target, replacement);
2318 }
2319 }
2320 }
2321
2322 write(out, command);
2323 }
2324 tokenizer = null;
2325 }
2326 in.close();
2327 in = null;
2328 out.flush();
2329 out.close();
2330 out = null;
2331 }
2332 catch(Exception error) {
2333 DebugStream.printStackTrace(error);
2334 }
2335 // All done, I hope.
2336 }
2337
2338 private void write(BufferedWriter out, String message)
2339 throws Exception {
2340 out.write(message, 0, message.length());
2341 out.newLine();
2342 }
2343
2344
2345 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
2346 private class FMTreeModelListener
2347 implements TreeModelListener {
2348 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2349 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2350 */
2351 public void treeNodesChanged(TreeModelEvent event) {
2352 if(collection != null) {
2353 collection.setSaved(false);
2354 collection.setFilesChanged(true);
2355 }
2356 }
2357 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2358 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2359 */
2360 public void treeNodesInserted(TreeModelEvent event) {
2361 if(collection != null) {
2362 collection.setSaved(false);
2363 collection.setFilesChanged(true);
2364 }
2365 }
2366 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2367 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2368 */
2369 public void treeNodesRemoved(TreeModelEvent event) {
2370 if(collection != null) {
2371 collection.setSaved(false);
2372 collection.setFilesChanged(true);
2373
2374 }
2375 }
2376 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2377 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2378 */
2379 public void treeStructureChanged(TreeModelEvent event) {
2380 if(collection != null) {
2381 collection.setSaved(false);
2382 }
2383 }
2384 }
2385}
Note: See TracBrowser for help on using the repository browser.