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

Last change on this file since 8601 was 8600, checked in by mdewsnip, 20 years ago

The first smidgeon of the "plugin suggestion" code.

  • Property svn:keywords set to Author Date Id Revision
File size: 66.5 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <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.lang.Class;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.event.*;
44import javax.swing.filechooser.FileSystemView;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.Configuration;
47import org.greenstone.gatherer.DebugStream;
48import org.greenstone.gatherer.Dictionary;
49import org.greenstone.gatherer.Gatherer;
50import org.greenstone.gatherer.ServletConfiguration;
51import org.greenstone.gatherer.cdm.CollectionDesignManager;
52import org.greenstone.gatherer.cdm.CollectionMeta;
53import org.greenstone.gatherer.cdm.CollectionMetaManager;
54import org.greenstone.gatherer.cdm.CommandTokenizer;
55import org.greenstone.gatherer.file.FileNode;
56import org.greenstone.gatherer.file.FileQueue;
57import org.greenstone.gatherer.file.FileSystemModel;
58import org.greenstone.gatherer.gui.LockFileDialog;
59import org.greenstone.gatherer.gui.NewCollectionMetadataPrompt;
60import org.greenstone.gatherer.gui.ExternalCollectionPrompt;
61import org.greenstone.gatherer.gui.WarningDialog;
62import org.greenstone.gatherer.gui.tree.WorkspaceTree;
63import org.greenstone.gatherer.metadata.DocXMLFileManager;
64import org.greenstone.gatherer.metadata.MetadataSet;
65import org.greenstone.gatherer.metadata.MetadataSetManager;
66import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
67import org.greenstone.gatherer.metadata.ProfileXMLFileManager;
68import org.greenstone.gatherer.shell.GShell;
69import org.greenstone.gatherer.shell.GShellEvent;
70import org.greenstone.gatherer.shell.GShellListener;
71import org.greenstone.gatherer.shell.GShellProgressMonitor;
72import org.greenstone.gatherer.util.ArrayTools;
73import org.greenstone.gatherer.util.Codec;
74import org.greenstone.gatherer.util.GSDLSiteConfig;
75import org.greenstone.gatherer.util.StaticStrings;
76import org.greenstone.gatherer.util.Utility;
77import org.greenstone.gatherer.util.XMLTools;
78import org.w3c.dom.*;
79
80/** 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 resposible for firing appropriate event when significant changes have occured within the collection, and for creating a new metadata set manager as necessary.
81 * @author John Thompson
82 * @version 2.3
83 */
84public class CollectionManager
85 implements GShellListener {
86
87 /** Are we currently in the process of building? */
88 private boolean building = false;
89 /** Are we currently in the process of importing? */
90 private boolean importing = false;
91 /** The objects listening for CollectionContentsChanged events. */
92 private ArrayList collection_contents_changed_listeners = new ArrayList();
93 /** The collection this manager is managing! */
94 static private Collection collection = null;
95 /** The collection_model. */
96 private FileSystemModel collection_model = null;
97 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
98 private FMTreeModelListener fm_tree_model_listener = null;
99 /** The monitor resposible for parsing the build process. */
100 private GShellProgressMonitor build_monitor = null;
101 /** The monitor resposible for parsing the import process. */
102 private GShellProgressMonitor import_monitor = null;
103
104 /** The name of the standard lock file. */
105 static final public String LOCK_FILE = "gli.lck";
106
107 static public final int COLLECTION_OPENED = 10;
108 static public final int COLLECTION_CLOSED = 11;
109 static public final int COLLECTION_REBUILT = 12;
110
111 /** Used to indicate the source of the message is the file collection methods. */
112 static final public int COLLECT = 3;
113 /** Used to indicate the source of the message is the building methods. */
114 static final public int BUILDING = 5;
115
116 /** Constructor. */
117 public CollectionManager() {
118 // Initialisation.
119 this.building = false;
120 this.importing = false;
121 this.collection = null;
122 }
123
124
125 public void addCollectionContentsChangedListener(CollectionContentsChangedListener listener)
126 {
127 collection_contents_changed_listeners.add(listener);
128 }
129
130
131 /** Add a special directory mapping.
132 * @param name The name for this directory mapping as a <strong>String</strong>.
133 * @param file The directory this mapping maps to as a <strong>File</strong>.
134 */
135 public void addDirectoryMapping(String name, File file)
136 {
137 // Update the information stored in the Gatherer config
138 Configuration.addDirectoryMapping(name, file);
139 // Now update the tree
140 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.FOLDER_SHORTCUTS_CHANGED);
141 }
142
143
144 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
145 * @see org.greenstone.gatherer.Configuration
146 * @see org.greenstone.gatherer.Gatherer
147 * @see org.greenstone.gatherer.collection.Collection
148 * @see org.greenstone.gatherer.gui.BuildOptions
149 * @see org.greenstone.gatherer.shell.GShell
150 * @see org.greenstone.gatherer.shell.GShellListener
151 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
152 * @see org.greenstone.gatherer.util.Utility
153 */
154 private void buildCollection() {
155 DebugStream.println("CollectionManager.buildCollection()");
156 building = true;
157 String lang = Configuration.getLanguage();
158 String collect_dir = getCollectDirectory();
159
160 String args[];
161 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
162 args = new String[8];
163 args[0] = Configuration.perl_path;
164 args[1] = "-S";
165 args[2] = Configuration.getScriptPath() + "buildcol.pl";
166 args[3] = "-gli";
167 args[4] = "-language";
168 args[5] = lang;
169 args[6] = "-collectdir";
170 args[7] = collect_dir;
171 }
172 else {
173 args = new String[6];
174 args[0] = Configuration.getScriptPath() + "buildcol.pl";
175 args[1] = "-gli";
176 args[2] = "-language";
177 args[3] = lang;
178 args[4] = "-collectdir";
179 args[5] = collect_dir;
180 }
181 args = ArrayTools.add(args, collection.build_options.getBuildValues());
182 args = ArrayTools.add(args, collection.getName());
183 GShell shell = new GShell(args, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
184 shell.addGShellListener(Gatherer.g_man.create_pane);
185 shell.start();
186 DebugStream.println("CollectionManager.buildCollection().return");
187 }
188
189 /** Used to determine whether the currently active collection has been built.
190 * @return A boolean indicating the built status of the collection.
191 */
192 public boolean built() {
193 if(collection != null) {
194 // Determine if the collection has been built by looking for the build.cfg file
195 File build_cfg_file = new File(getCollectionIndex() + Utility.BUILD_CFG_FILENAME);
196 return build_cfg_file.exists();
197 }
198 return false;
199 }
200
201 /** 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 */
202 public boolean canDelete(File file) {
203 if (!file.isDirectory()) {
204 return file.canWrite();
205 }
206 File [] file_list = file.listFiles();
207 for (int i=0; i<file_list.length; i++) {
208 if (!canDelete(file_list[i])) {
209 return false;
210 }
211 }
212 return true;
213 }
214 /** Called to close the current collection and remove its lock file.
215 * @see org.greenstone.gatherer.Gatherer
216 * @see org.greenstone.gatherer.collection.Collection
217 * @see org.greenstone.gatherer.util.Utility
218 */
219 public void closeCollection() {
220 DebugStream.println("Close collection: " + collection.getName());
221
222 // Remove the lock on this file, then remove the collection.
223 String collection_dir;
224 if (Gatherer.GS3) {
225 collection_dir = Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName());
226 } else {
227 collection_dir = Utility.getCollectionDir(Configuration.gsdl_path, collection.getName());
228
229 }
230 File lock_file = new File(collection_dir + LOCK_FILE);
231 lock_file.delete();
232 if(lock_file.exists()) {
233 System.err.println("Lockfile was not successfully deleted.");
234 }
235
236 MetadataSetManager.clearMetadataSets();
237 MetadataXMLFileManager.clearMetadataXMLFiles();
238 DocXMLFileManager.clearDocXMLFiles();
239 ProfileXMLFileManager.clearProfileXMLFile();
240
241 collection = null;
242 collection_model = null;
243 // workspace_model = null;
244 Configuration.setCollectionConfiguration(null);
245 if (Gatherer.g_man != null) {
246 Gatherer.g_man.updateUI();
247 Gatherer.g_man.refresh(COLLECTION_CLOSED, false);
248 }
249 }
250
251 /** Method that is called whenever something has changed in the configuration of this collection. */
252 public void configurationChanged() {
253 if(collection != null) {
254 collection.setSaved(false);
255 }
256 }
257
258 public void convertToGS3Collection() {
259 // Run the mkcol command.
260 String command[];
261 String collect_dir = getCollectDirectory();
262
263 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
264 command = new String[6];
265 command[0] = Configuration.perl_path;
266 command[1] = "-S";
267 command[2] = Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl";
268 command[3] = "-collectdir";
269 command[4] = collect_dir;
270 command[5] = collection.getName();
271 } else {
272 command = new String[4];
273 command[0] = Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl";
274 command[1] = "-collectdir";
275 command[2] = collect_dir;
276 command[3] = collection.getName();
277 }
278 DebugStream.println("running command "+command);
279 GShell process = new GShell(command, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
280 process.addGShellListener(this);
281 process.run(); // Don't bother threading this... yet
282
283 }
284
285 /** When basing a new collection on an existing one, we need to copy
286 * over some extra directories: images and macros
287 */
288 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
289 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
290 return false;
291 }
292 DebugStream.println("Copying images and macros dirs from the base collection");
293 FileQueue fq = Gatherer.f_man.getQueue();
294 try {
295 // do the images dir
296 File base_coll_images = new File(base_coll_dir, Utility.IMAGES_DIR);
297 if (base_coll_images.isDirectory()) {
298 // copy all the images over
299 File new_coll_images = new File(new_coll_dir, Utility.IMAGES_DIR);
300 new_coll_images.mkdirs();
301
302 // copy the contents over
303 fq.copyDirectoryContents(base_coll_images, new_coll_images, null);
304 }
305 } catch (Exception e) {
306 DebugStream.println("Couldn't copy over the images dir from the base collection: "+e.toString());
307 }
308 try {
309 // do the macros dir
310 File base_coll_macros = new File(base_coll_dir, Utility.MACROS_DIR);
311 if (base_coll_macros.isDirectory()) {
312 // copy all the macros over
313 File new_coll_macros = new File(new_coll_dir, Utility.MACROS_DIR);
314 new_coll_macros.mkdirs();
315
316 // copy the contents over
317 fq.copyDirectoryContents(base_coll_macros, new_coll_macros, null);
318 }
319 } catch (Exception e) {
320 DebugStream.println("Couldn't copy over the macros dir from the base collection: "+e.toString());
321 }
322 return true;
323 }
324
325 /** 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 colection 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.
326 * @param description a description of the collection as a String
327 * @param email the email address of the author/maintainer as a String
328 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
329 * @param title the longer title of the collection as a String
330 * @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
331 * @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
332 */
333 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
334 {
335 try {
336 // first make sure that the collect directory exists
337 File collect_dir = new File(getCollectDirectory());
338 if (!collect_dir.exists()) {
339 collect_dir.mkdirs();
340 }
341 // Create a progress monitor.
342 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, Dictionary.get("CollectionManager.Creating_New"), "mkcol.pl", 0, 7);
343 // Create the new collection.
344 makeCollection(description, email, name, title);
345
346 // *******************
347 //check that this creation has worked - simply by checking for the existence of the collect.cfg file
348 String collection_dir;
349 if (Gatherer.GS3) {
350 collection_dir = Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, name);
351 } else {
352 collection_dir = Utility.getCollectionDir(Configuration.gsdl_path, name);
353 }
354 File config_file = new File(Utility.getConfigFile(collection_dir));
355 if (!config_file.exists()) {
356 // no point continuing
357 DebugStream.println("The collection could not be created");
358 progress.close();
359 return;
360 }
361 progress.setProgress(1);
362
363 // ACTIVE_DIR/log/
364 File log_dir_temp = new File(Utility.getLogDir(collection_dir)+"temp.dat");
365 File log_dir = log_dir_temp.getParentFile();
366 log_dir.mkdirs();
367 if(progress != null) {
368 progress.setNote(Dictionary.get("CollectionManager.Log_Created"));
369 }
370
371 // Make sure an import folder exists
372 File import_directory = new File(Utility.getImportDir(collection_dir));
373 if (!import_directory.exists()) {
374 import_directory.mkdirs();
375 }
376
377 progress.setProgress(2);
378
379 // Now create the collection object around the directory.
380 collection = new Collection(new File(collection_dir, name + ".col"));
381
382 MetadataSetManager.clearMetadataSets();
383 MetadataXMLFileManager.clearMetadataXMLFiles();
384 DocXMLFileManager.clearDocXMLFiles();
385
386 // Import default metadata sets, if any
387 for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
388 importMetadataSet((MetadataSet) metadata_sets.get(i));
389 }
390
391 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_dir, Utility.META_DIR));
392
393 // Before we create the CollectionDesignManager we have to check if we are basing it upon some other collection.
394 if (base_collection_directory != null) {
395 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
396 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
397 // copy over other needed directories
398 copyExtraBaseCollStuff(new File(collection_dir), base_collection_directory);
399 // Try to import any existing metadata sets for this collection
400 // Look in base_collection_directory/metadata and import any metadata sets found.
401 File base_metadata_directory = new File(base_collection_directory, Utility.META_DIR);
402 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
403 if (base_metadata_sets != null) {
404 for (int i = 0; i < base_metadata_sets.size(); i++) {
405 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
406 }
407 }
408 else {
409 DebugStream.println("This base collection has no metadata directory.");
410 }
411
412 // If no sets were imported...
413 if (MetadataSetManager.getMetadataSets().size() == 0) {
414 // Prompt the user so that they can choose at least one initial metadata set. We're sneaky here and just create a ncm_prompt
415 DebugStream.println("This collection has no metadata sets. Present the user with the metadata set selection prompt.");
416 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt();
417 // If cancelled then they really do mean to start a collection with no metadata sets.
418 if (!ncm_prompt.isCancelled()) {
419 ArrayList initial_sets = ncm_prompt.getSets();
420 for (int i = 0; initial_sets != null && i < initial_sets.size(); i++) {
421 importMetadataSet((MetadataSet) initial_sets.get(i));
422 }
423 }
424 ncm_prompt.dispose();
425 ncm_prompt = null;
426 }
427
428 // Now we update our collect.cfg
429 DebugStream.println("Copy and update collect.cfg from base collection.");
430 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE), new File(collection_dir, Utility.CONFIG_FILE), description, email, title);
431 }
432
433 // Always import the extracted metadata set if we didn't already
434 if (MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE) == null) {
435 File extracted_metadata_set_file = new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
436 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
437 }
438
439 collection.cdm = new CollectionDesignManager(new File(getCollectionConfig()));
440
441 // 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
442 if (base_collection_directory != null) {
443 // Update the creator and maintainer
444 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
445 creator_collectionmeta.setValue(email);
446 creator_collectionmeta = null;
447 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
448 maintainer_collectionmeta.setValue(email);
449 maintainer_collectionmeta = null;
450
451 // Update the collection title
452 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
453 collection_name_collectionmeta.setValue(title);
454 collection_name_collectionmeta = null;
455
456 // And now the description
457 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
458 collection_extra_collectionmeta.setValue(description);
459 collection_extra_collectionmeta = null;
460
461 // All collections based on others are automatically public
462 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
463 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
464 public_collectionmeta = null;
465
466 // Finally reset the icons
467 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
468 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
469 icon_collection_collectionmeta = null;
470 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
471 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
472 icon_collection_small_collectionmeta = null;
473 }
474
475 progress.setProgress(3);
476
477 // Create a lock file.
478 File lock_file = new File(collection_dir, LOCK_FILE);
479 FileOutputStream out = new FileOutputStream(lock_file);
480 out.write(LOCK_FILE.getBytes());
481 out.close();
482 out = null;
483
484 progress.setProgress(7);
485 progress.setNote(Dictionary.get("CollectionManager.Session_Ready", name));
486 progress.close();
487 }
488 catch (Exception error) {
489 DebugStream.printStackTrace(error);
490 }
491
492 // Done, so refresh interface
493 if (Gatherer.g_man != null) {
494 // Return to some initial pane (Gather)
495 Gatherer.g_man.returnToInitialPane();
496
497 // Refresh the workspace tree to allow for the new collection
498 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.LIBRARY_CONTENTS_CHANGED);
499
500 Gatherer.g_man.refresh(COLLECTION_OPENED, ready());
501 }
502 }
503
504
505 public void createLockFile(File destination) {
506 try {
507 Document default_lockfile = Utility.parse("xml/" + LOCK_FILE, true);
508 String user_name = System.getProperty("user.name");
509 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
510 person_element.appendChild(default_lockfile.createTextNode(user_name));
511 person_element = null;
512 user_name = null;
513 String machine_name = Utility.getMachineName();
514 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
515 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
516 machine_element = null;
517 machine_name = null;
518 String date_time = Utility.getDateString();
519 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
520 date_element.appendChild(default_lockfile.createTextNode(date_time));
521 date_element = null;
522 date_time = null;
523 Utility.export(default_lockfile, destination);
524 }
525 catch (Exception error) {
526 DebugStream.printStackTrace(error);
527 }
528 }
529
530
531 public void fireFileAddedToCollection(File file)
532 {
533 // Send the event off to all the CollectionContentsChangedListeners
534 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
535 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
536 }
537 }
538
539
540 /** Retrieve the current collection.
541 * @return The <strong>Collection</strong> itself.
542 */
543 public Collection getCollection() {
544 return collection;
545 }
546
547
548 /** Constructs the absolute filename of the collection archive directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/archive/"
549 * @return A <strong>String</strong> containing the filename.
550 * @see org.greenstone.gatherer.Configuration
551 * @see org.greenstone.gatherer.Gatherer
552 * @see org.greenstone.gatherer.collection.Collection
553 * @see org.greenstone.gatherer.util.Utility
554 */
555 public String getCollectionArchive() {
556 if (Gatherer.GS3) {
557 return Utility.getArchiveDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
558 } else {
559 return Utility.getArchiveDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
560 }
561 }
562 /** Constructs the absolute filename of the collection building directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/building/"
563 * @return A <strong>String</strong> containing the filename.
564 * @see org.greenstone.gatherer.Configuration
565 * @see org.greenstone.gatherer.Gatherer
566 * @see org.greenstone.gatherer.collection.Collection
567 * @see org.greenstone.gatherer.util.Utility
568 */
569 public String getCollectionBuild() {
570 if (Gatherer.GS3) {
571 return Utility.getBuildDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
572 } else {
573 return Utility.getBuildDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
574 }
575 }
576 /** Constructs the absolute filename of the collection config file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/collect.cfg"
577 * @return A <strong>String</strong> containing the filename.
578 * @see org.greenstone.gatherer.Configuration
579 * @see org.greenstone.gatherer.Gatherer
580 * @see org.greenstone.gatherer.collection.Collection
581 * @see org.greenstone.gatherer.util.Utility
582 */
583 public String getCollectionConfig() {
584 if (Gatherer.GS3) {
585 return Utility.getConfigFile(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
586 } else {
587 return Utility.getConfigFile(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
588 }
589 }
590
591 /** Constructs the absolute filename of the collection directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;"
592 * @return A <strong>String</strong> containing the directory name.
593 * @see org.greenstone.gatherer.Configuration
594 * @see org.greenstone.gatherer.Gatherer
595 * @see org.greenstone.gatherer.collection.Collection
596 * @see org.greenstone.gatherer.util.Utility
597 */
598 public String getCollectionDirectory() {
599 if (Gatherer.GS3) {
600 return Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName());
601 } else {
602 return Utility.getCollectionDir(Configuration.gsdl_path, collection.getName());
603 }
604 }
605
606 /** Constructs the absolute filename of the collection etc directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/"
607 * @return A <strong>String</strong> containing the filename.
608 * @see org.greenstone.gatherer.Configuration
609 * @see org.greenstone.gatherer.Gatherer
610 * @see org.greenstone.gatherer.collection.Collection
611 * @see org.greenstone.gatherer.util.Utility
612 */
613 public String getCollectionEtc() {
614 if (Gatherer.GS3) {
615 return Utility.getEtcDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
616 } else {
617 return Utility.getEtcDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
618 }
619 }
620 /** Constructs the absolute filename of the collection file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/&lt;col_name&gt;.col"
621 * @return A <strong>String</strong> containing the filename.
622 * @see org.greenstone.gatherer.Configuration
623 * @see org.greenstone.gatherer.Gatherer
624 * @see org.greenstone.gatherer.collection.Collection
625 * @see org.greenstone.gatherer.util.Utility
626 */
627 public String getCollectionFilename() {
628 if (Gatherer.GS3) {
629 return Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()) + collection.getName() + ".col";
630 } else {
631 return Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()) + collection.getName() + ".col";
632 }
633 }
634 /** Constructs the absolute filename of the collection images directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/images/"
635 * @return A <strong>String</strong> containing the filename.
636 * @see org.greenstone.gatherer.Configuration
637 * @see org.greenstone.gatherer.Gatherer
638 * @see org.greenstone.gatherer.collection.Collection
639 * @see org.greenstone.gatherer.util.Utility
640 */
641 public String getCollectionImages() {
642 if (Gatherer.GS3) {
643 return Utility.getImagesDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
644 } else {
645 return Utility.getImagesDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
646 }
647 }
648 /** Constructs the absolute filename of the collection import directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/import/"
649 * @return A <strong>String</strong> containing the filename.
650 * @see org.greenstone.gatherer.Configuration
651 * @see org.greenstone.gatherer.Gatherer
652 * @see org.greenstone.gatherer.collection.Collection
653 * @see org.greenstone.gatherer.util.Utility
654 */
655 public String getCollectionImport() {
656 if (Gatherer.GS3) {
657 return Utility.getImportDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
658 } else {
659 return Utility.getImportDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
660 }
661 }
662 /** Constructs the absolute filename of the collection index directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/index/"
663 * @return A <strong>String</strong> containing the filename.
664 * @see org.greenstone.gatherer.Configuration
665 * @see org.greenstone.gatherer.Gatherer
666 * @see org.greenstone.gatherer.collection.Collection
667 * @see org.greenstone.gatherer.util.Utility
668 */
669 public String getCollectionIndex() {
670 if (Gatherer.GS3) {
671 return Utility.getIndexDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
672 } else {
673 return Utility.getIndexDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
674 }
675 }
676 /** Constructs the absolute filename of the collection log directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/log/"
677 * @return A <strong>String</strong> containing the filename.
678 * @see org.greenstone.gatherer.Configuration
679 * @see org.greenstone.gatherer.Gatherer
680 * @see org.greenstone.gatherer.collection.Collection
681 * @see org.greenstone.gatherer.util.Utility
682 */
683 public String getCollectionLog() {
684 if (Gatherer.GS3) {
685 return Utility.getLogDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
686 } else {
687 return Utility.getLogDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
688 }
689 }
690 /** Constructs the absolute filename of the collection metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
691 * @return A <strong>String</strong> containing the filename.
692 * @see org.greenstone.gatherer.Configuration
693 * @see org.greenstone.gatherer.Gatherer
694 * @see org.greenstone.gatherer.collection.Collection
695 * @see org.greenstone.gatherer.util.Utility
696 */
697 public String getCollectionMetadata() {
698 if (Gatherer.GS3) {
699 return Utility.getMetadataDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
700 } else {
701 return Utility.getMetadataDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
702 }
703 }
704
705
706 static public String getCollectionMetadataDirectory()
707 {
708 if (Gatherer.GS3) {
709 return Utility.getMetadataDir(Utility.getCollectionDir(Configuration.gsdl3_path, Configuration.site_name, collection.getName()));
710 } else {
711 return Utility.getMetadataDir(Utility.getCollectionDir(Configuration.gsdl_path, collection.getName()));
712 }
713 }
714
715
716 /** Retrieve the record set (tree model) associated with the current collection. */
717 public TreeModel getRecordSet() {
718 if(collection_model == null && collection != null) {
719 // Use the import directory to generate a new FileSystemModel
720 collection_model = new FileSystemModel(new FileNode(new File(getCollectionImport()), false));
721 // Ensure that the manager is a change listener for the tree.
722 if(fm_tree_model_listener == null) {
723 fm_tree_model_listener = new FMTreeModelListener();
724 }
725 collection_model.addTreeModelListener(fm_tree_model_listener);
726 }
727 return collection_model;
728 }
729
730
731 static public FileNode getGreenstoneCollectionsMapping()
732 {
733 FileNode greenstone_collections_node = new FileNode(Dictionary.get("Tree.World"));
734 greenstone_collections_node.unmap();
735 return greenstone_collections_node;
736 }
737
738
739 static public FileNode[] getFolderShortcuts()
740 {
741 // Return any predefined special directories
742 HashMap mappings = Configuration.getDirectoryMappings();
743 FileNode[] mapping_nodes = new FileNode[mappings.size()];
744 Iterator mappings_iterator = mappings.keySet().iterator();
745 for (int i = 0; mappings_iterator.hasNext(); i++) {
746 String mapping_name = (String) mappings_iterator.next();
747 File mapping_file = (File) mappings.get(mapping_name);
748 mapping_nodes[i] = new FileNode(mapping_file, mapping_name);
749 }
750 return mapping_nodes;
751 }
752
753
754 /** This method when called, creates a new GShell in order to run the import.pl script.
755 * @see org.greenstone.gatherer.Configuration
756 * @see org.greenstone.gatherer.Gatherer
757 * @see org.greenstone.gatherer.gui.BuildOptions
758 * @see org.greenstone.gatherer.shell.GShell
759 * @see org.greenstone.gatherer.shell.GShellListener
760 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
761 * @see org.greenstone.gatherer.util.Utility
762 */
763 public void importCollection() {
764 importing = true;
765
766 if (!saved()) {
767 DebugStream.println("CollectionManager.importCollection().forcesave");
768 import_monitor.saving();
769 saveCollection();
770 }
771
772 DebugStream.println("CollectionManager.importCollection()");
773 //check that we can remove the old index before starting import
774 File index_dir = new File(getCollectionIndex(), "temp.txt");
775 index_dir = index_dir.getParentFile();
776 if(index_dir.exists()) {
777 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
778 if (!canDelete(index_dir)) {
779 // tell the user
780 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
781 // tell the gui manager
782 // a message for the building log
783 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
784 Gatherer.g_man.create_pane.message(event);
785 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
786 Gatherer.g_man.create_pane.processComplete(event);
787 importing = false;
788 return;
789 }
790 }
791
792 String collect_dir = getCollectDirectory();
793
794 String args[];
795 String lang = Configuration.getLanguage();
796 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
797 args = new String[8];
798 args[0] = Configuration.perl_path;
799 args[1] = "-S";
800 args[2] = Configuration.getScriptPath() + "import.pl";
801 args[3] = "-gli";
802 args[4] = "-language";
803 args[5] = lang;
804 args[6] = "-collectdir";
805 args[7] = collect_dir;
806 }
807 else {
808 args = new String[6];
809 args[0] = Configuration.getScriptPath() + "import.pl";
810 args[1] = "-gli";
811 args[2] = "-language";
812 args[3] = lang;
813 args[4] = "-collectdir";
814 args[5] = collect_dir;
815 }
816 collect_dir = null;
817 args = ArrayTools.add(args, collection.build_options.getImportValues());
818 args = ArrayTools.add(args, collection.getName());
819
820 GShell shell = new GShell(args, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
821 shell.addGShellListener(Gatherer.g_man.create_pane);
822 shell.start();
823 DebugStream.println("CollectionManager.importCollection().return");
824
825 importing = false;
826 }
827
828
829 public void importMetadataSet(MetadataSet external_metadata_set)
830 {
831 // Copy the .mds file into the collection's "metadata" folder...
832 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
833
834 // ...but not if it is the redundant "hidden.mds" file
835 if (external_metadata_set_file.getName().equals("hidden.mds")) {
836 return;
837 }
838
839 // ...and only if it doesn't already exist
840 File metadata_set_file = new File(getCollectionMetadata(), external_metadata_set_file.getName());
841 if (!metadata_set_file.exists()) {
842 try {
843 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, null);
844 }
845 catch (Exception ex) {
846 ex.printStackTrace();
847 }
848
849 // Load it into the MetadataSetManager
850 MetadataSetManager.loadMetadataSet(metadata_set_file);
851 }
852 }
853
854
855 /** 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.
856 * @return true if the gli is currently importing
857 */
858 public boolean isImporting() {
859 return importing;
860 }
861
862 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
863 * @param location The path to the collection as a <strong>String</strong>.
864 * @see org.greenstone.gatherer.Configuration
865 * @see org.greenstone.gatherer.Gatherer
866 * @see org.greenstone.gatherer.collection.Collection
867 * @see org.greenstone.gatherer.util.Utility
868 */
869 public boolean loadCollection(String location)
870 {
871 DebugStream.println("Loading collection " + location + "...");
872 boolean non_gatherer_collection = false;
873
874 // Check we have actually been given a .col file.
875 if (!location.endsWith(".col")) {
876 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
877 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
878 return false;
879 }
880
881 // Check that there is the collection configuration file available
882 File collection_file = new File(location);
883 // Ensure that the directory exists.
884 File collection_directory = collection_file.getParentFile();
885 if (!collection_directory.exists()) {
886 // we cant open this
887 collection_directory = null;
888 return false;
889 }
890 File collection_config_file = new File(collection_directory, Utility.CONFIG_FILE);
891 if (!collection_config_file.exists()) {
892 DebugStream.println("CollectionManager.loadCollection: No config file");
893 collection_directory = null;
894 collection_config_file = null;
895 return false;
896 }
897
898 // Special case of a user trying to open an old greenstone collection.
899 File collection_metadata_directory = new File(collection_directory, Utility.META_DIR);
900 if (!collection_metadata_directory.exists()) {
901 DebugStream.println("Loading non-gatherer collection...");
902 non_gatherer_collection = true;
903 }
904
905 // Now determine if a lock already exists on this collection.
906 String name = collection_directory.getName();
907 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
908 if (lock_file.exists()) {
909 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, name, lock_file);
910 int choice = dialog.getChoice();
911 dialog.dispose();
912 dialog = null;
913
914 if (choice != LockFileDialog.YES_OPTION) {
915 // user has cancelled
916 lock_file = null;
917 collection_directory = null;
918 collection_config_file = null;
919 return false;
920 }
921
922 lock_file.delete();
923 }
924
925 boolean result = false;
926 try {
927 // Create a lock file.
928 createLockFile(lock_file);
929 // This lock file may not have been created so check
930 if(!lock_file.canWrite()) {
931 // The lock file cannot be written to. Most likely cause incorrect file permissions.
932 System.err.println("Cannot write lock file!");
933 String args[] = new String[2];
934 args[0] = location;
935 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Title");
936 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
937 args = null;
938 return false;
939 }
940
941 // Open the collection file
942 this.collection = new Collection(collection_file);
943 if (collection.error) {
944 collection = null;
945 // Remove lock file
946 if (lock_file.exists()) {
947 lock_file.delete();
948 }
949 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
950 }
951
952 MetadataSetManager.clearMetadataSets();
953 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
954
955 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
956
957 // If this is a non-GLI (legacy) collection, ask the user to choose some metadata sets
958 if (non_gatherer_collection) {
959 if (!addSomeMetadataSets(collection_directory)) {
960 lock_file = null;
961 collection_directory = null;
962 closeCollection();
963 return false;
964 }
965
966 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
967 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
968 }
969
970 // Read through the metadata.xml files in the import directory, building up the metadata value trees
971 File collection_import_directory = new File(collection_directory, Utility.IMPORT_DIR);
972 MetadataXMLFileManager.clearMetadataXMLFiles();
973 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory);
974
975 // Read through the doc.xml files in the archives directory
976 File collection_archives_directory = new File(collection_directory, Utility.ARCHIVES_DIR);
977 DocXMLFileManager.clearDocXMLFiles();
978 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory);
979
980 collection.cdm = new CollectionDesignManager(collection_config_file);
981 if (non_gatherer_collection) {
982 // Change the classifiers to use the namespaced element names
983 LegacyCollectionImporter.updateClassifiers(collection.cdm);
984 }
985
986 // Tell everyone that it worked.
987 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", name));
988
989 // We're done. Let everyone know.
990 if (Gatherer.g_man != null) {
991 Gatherer.g_man.refresh(COLLECTION_OPENED, ready());
992 }
993 result = true;
994 }
995 catch (Exception error) {
996 // There is obviously no existing collection present.
997 DebugStream.printStackTrace(error);
998 if(error.getMessage() != null) {
999 String[] args = new String[2];
1000 args[0] = location;
1001 args[1] = error.getMessage();
1002 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1003 }
1004 else {
1005 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1006 }
1007 }
1008
1009 lock_file = null;
1010 collection_directory = null;
1011 collection_config_file = null;
1012
1013 return result;
1014 }
1015
1016
1017 public void makeCollection(String description, String email, String name, String title) {
1018 // Encode the description so it is safe to write to shell
1019 if(Utility.isWindows()) {
1020 description = Codec.transform(description, Codec.TEXT_TO_SHELL_WINDOWS);
1021 }
1022 else {
1023 description = Codec.transform(description, Codec.TEXT_TO_SHELL_UNIX);
1024 }
1025 String collect_dir = getCollectDirectory();
1026 // Run the mkcol command.
1027 String command[];
1028 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
1029 if(description == null || title == null) {
1030 command = new String[8];
1031 command[0] = Configuration.perl_path;
1032 command[1] = "-S";
1033 command[2] = Configuration.getScriptPath() + "mkcol.pl";
1034 command[3] = "-collectdir";
1035 command[4] = collect_dir;
1036 command[5] = "-win31compat";
1037 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
1038 command[7] = name;
1039 }
1040 // Users are no longer required to supply an email
1041 else if(email == null) {
1042 command = new String[12];
1043 command[0] = Configuration.perl_path;
1044 command[1] = "-S";
1045 command[2] = Configuration.getScriptPath() + "mkcol.pl";
1046 command[3] = "-collectdir";
1047 command[4] = collect_dir;
1048 command[5] = "-win31compat";
1049 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
1050 command[7] = "-title";
1051 command[8] = title;
1052 command[9] = "-about";
1053 command[10] = description;
1054 command[11] = name;
1055 }
1056 else {
1057 command = new String[14];
1058 command[0] = Configuration.perl_path;
1059 command[1] = "-S";
1060 command[2] = Configuration.getScriptPath() + "mkcol.pl";
1061 command[3] = "-collectdir";
1062 command[4] = collect_dir;
1063 command[5] = "-win31compat";
1064 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
1065 command[7] = "-title";
1066 command[8] = title;
1067 command[9] = "-creator";
1068 command[10] = email;
1069 command[11] = "-about";
1070 command[12] = description;
1071 command[13] = name;
1072 }
1073 }
1074 else {
1075 if(description == null || title == null) {
1076 command = new String[6];
1077 command[0] = "mkcol.pl";
1078 command[1] = "-collectdir";
1079 command[2] = collect_dir;
1080 command[3] = "-win31compat";
1081 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
1082 command[5] = name;
1083 }
1084 else if(email == null) {
1085 command = new String[10];
1086 command[0] = "mkcol.pl";
1087 command[1] = "-collectdir";
1088 command[2] = collect_dir;
1089 command[3] = "-win31compat";
1090 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
1091 command[5] = "-title";
1092 command[6] = title;
1093 command[7] = "-about";
1094 command[8] = description;
1095 command[9] = name;
1096 }
1097 else {
1098 command = new String[12];
1099 command[0] = "mkcol.pl";
1100 command[1] = "-collectdir";
1101 command[2] = collect_dir;
1102 command[3] = "-win31compat";
1103 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
1104 command[5] = "-title";
1105 command[6] = title;
1106 command[7] = "-creator";
1107 command[8] = email;
1108 command[9] = "-about";
1109 command[10] = description;
1110 command[11] = name;
1111 }
1112 }
1113 GShell process = new GShell(command, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1114 process.run(); // Don't bother threading this... yet
1115 }
1116
1117 /** 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.
1118 * @param event A <strong>GShellEvent</strong> which contains a the message.
1119 */
1120 public synchronized void message(GShellEvent event) {
1121 }
1122
1123 /** This call is fired whenever a process within a GShell created by this class begins.
1124 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1125 * @see org.greenstone.gatherer.Gatherer
1126 * @see org.greenstone.gatherer.gui.GUIManager
1127 * @see org.greenstone.gatherer.shell.GShell
1128 */
1129 public synchronized void processBegun(GShellEvent event) {
1130 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1131 ///ystem.err.println("ProcessBegun " + event.getType());
1132 // If this is one of the types where we wish to lock user control
1133 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1134 }
1135 /** This call is fired whenever a process within a GShell created by this class ends.
1136 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1137 * @see org.greenstone.gatherer.Gatherer
1138 * @see org.greenstone.gatherer.gui.GUIManager
1139 * @see org.greenstone.gatherer.shell.GShell
1140 */
1141 public synchronized void processComplete(GShellEvent event) {
1142 DebugStream.println("CollectionManager.processComplete(" + event.getType() + ")");
1143 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1144 ///ystem.err.println("Recieved process complete event - " + event);
1145 // If we were running an import, now run a build.
1146 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1147 // Finish import.
1148 collection.setImported(true);
1149 buildCollection();
1150 }
1151 // If we were running a build, now is when we move files across.
1152 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1153 if(installCollection()) {
1154 // If we have a local library running (that we know about) then we ask it to add our newly create collection
1155 if (Configuration.exec_file != null) {
1156 Gatherer.configServer(GSDLSiteConfig.ADD_COMMAND + collection.getName());
1157 // This is very important -- it ensures that the above command has finished
1158 Gatherer.configServer("");
1159 }
1160 else if (Gatherer.GS3) {
1161 convertToGS3Collection();
1162 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1163 }
1164
1165 // Fire a collection changed first to update the preview etc buttons
1166 Gatherer.g_man.refresh(COLLECTION_REBUILT, ready());
1167
1168 // Now display a message dialog saying its all built
1169 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", false);
1170 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1171 collection_built_warning_dialog.display();
1172 collection_built_warning_dialog.dispose();
1173 collection_built_warning_dialog = null;
1174 }
1175 else {
1176 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1177 Gatherer.g_man.refresh(COLLECTION_REBUILT, ready());
1178 }
1179 }
1180 else if(event.getStatus() == GShell.ERROR || event.getStatus() == GShell.CANCELLED) {
1181 DebugStream.println("There was an error in the gshell:"+ event.getMessage());
1182 if (event.getType() == GShell.NEW) {
1183 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1184 } else {
1185 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1186 Gatherer.g_man.refresh(COLLECTION_REBUILT, ready());
1187 }
1188
1189 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.
1190 }
1191 }
1192
1193
1194 /** Determine if the manager is ready for actions apon its collection.
1195 * @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.
1196 */
1197 static public synchronized boolean ready() {
1198 if(collection != null) {
1199 return true;
1200 }
1201 else {
1202 return false;
1203 }
1204 }
1205
1206
1207 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1208 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1209 */
1210 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1211 build_monitor = monitor;
1212 }
1213 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1214 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1215 */
1216 public void registerImportMonitor(GShellProgressMonitor monitor) {
1217 import_monitor = monitor;
1218 }
1219 /** Remove a previously assigned special directory mapping.
1220 * @param target The <string>FileNode</strong> representing the special directory mapping to remove as a <strong>String</strong>.
1221 * @return The <strong>File</strong> of the mapping removed.
1222 * @see org.greenstone.gatherer.file.FileNode
1223 */
1224 public File removeDirectoryMapping(FileNode target) {
1225 // Remove from config, remembering file
1226 File file = Configuration.removeDirectoryMapping(target.toString());
1227 // Update tree.
1228 Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.FOLDER_SHORTCUTS_CHANGED);
1229 return file;
1230 }
1231
1232
1233 public void removeMetadataSet(MetadataSet metadata_set)
1234 {
1235 System.err.println("Removing metadata set...");
1236
1237 // Delete the .mds file from the collection's "metadata" folder...
1238 File metadata_set_file = metadata_set.getMetadataSetFile();
1239
1240 // ...but not if it is the "ex.mds" file
1241 if (metadata_set_file.getName().equals("ex.mds")) {
1242 return;
1243 }
1244
1245 // ...and only if it exists
1246 if (metadata_set_file.exists()) {
1247 metadata_set_file.delete();
1248
1249 // Unload it from the MetadataSetManager
1250 MetadataSetManager.unloadMetadataSet(metadata_set);
1251 }
1252 }
1253
1254
1255 /** Used to check whether all open collections have a 'saved' state.
1256 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1257 * @see org.greenstone.gatherer.collection.Collection
1258 */
1259 public boolean saved() {
1260 boolean result = true;
1261 if(collection != null) {
1262 result = collection.getSaved();
1263 }
1264 return result;
1265 }
1266
1267
1268 /** Saves the currently loaded collection. */
1269 public void saveCollection()
1270 {
1271 DebugStream.println("Saving collection " + collection.getName() + "...");
1272
1273 // Change cursor to hourglass
1274 Gatherer.g_man.wait(true);
1275
1276 // Create a backup of the collection file, just in case anything goes wrong
1277 File collection_file = new File(Gatherer.c_man.getCollectionFilename());
1278 if (collection_file.exists()) {
1279 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1280 if (!collection_file.renameTo(collection_file_backup)) {
1281 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1282 }
1283 collection_file_backup.deleteOnExit();
1284 }
1285
1286 // Write out the collection file
1287 collection.save();
1288
1289 // Write out the collection configuration file
1290 Gatherer.g_man.design_pane.saveConfiguration();
1291
1292 // Write hfiles for the loaded metadata elements into the collection "etc" directory
1293 MetadataSetManager.writeHierarchyFiles(new File(Gatherer.c_man.getCollectionEtc()));
1294
1295 // Change cursor back to normal
1296 Gatherer.g_man.wait(false);
1297 }
1298
1299
1300 /** 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] */
1301 private boolean addSomeMetadataSets(File collection_dir) {
1302
1303
1304 ExternalCollectionPrompt external_prompt = new ExternalCollectionPrompt();
1305 int meta_choice = external_prompt.getMetadataChoice();
1306 boolean cancelled = external_prompt.isCancelled();
1307 if (cancelled) {
1308 return false;
1309 }
1310
1311 // now we reuse the newcoll metadata prompt for the user to select metadata sets
1312 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt(true);
1313 if (ncm_prompt.isCancelled()) {
1314 return false;
1315 }
1316 ArrayList metadata_sets = ncm_prompt.getSets();
1317 // Import default metadata sets if any.
1318 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
1319 importMetadataSet((MetadataSet) metadata_sets.get(i));
1320 }
1321
1322 // Always import the extracted metadata set
1323 File extracted_metadata_set_file = new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1324 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1325
1326 return true;
1327 }
1328
1329
1330 // used as arg in the perl scripts
1331 private String getCollectDirectory() {
1332 String collect_dir;
1333 if (Gatherer.GS3) {
1334 collect_dir = Utility.getCollectDir(Configuration.gsdl3_path, Configuration.site_name);
1335 } else {
1336 collect_dir = Utility.getCollectDir(Configuration.gsdl_path);
1337 }
1338
1339 // Remove erroneous file windows file separator as it causes problems when running import.pl
1340 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1341 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1342 }
1343
1344 return collect_dir;
1345 }
1346
1347
1348 /** Install collection by moving its files from building to index after a successful build.
1349 * @see org.greenstone.gatherer.Gatherer
1350 * @see org.greenstone.gatherer.util.Utility
1351 */
1352 private boolean installCollection()
1353 {
1354 DebugStream.println("Build complete. Moving files.");
1355
1356 try {
1357 // Ensure that the local library has released this collection so we can delete the index directory
1358 if (!Gatherer.GS3 && Configuration.exec_file != null) {
1359 Gatherer.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1360
1361 // This is very important -- it ensures that the above command has finished
1362 // This prevents the nasty "could not remove index directory" race condition!
1363 Gatherer.configServer("");
1364 }
1365
1366 File index_dir = new File(getCollectionIndex());
1367 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1368
1369 File building_dir = new File(getCollectionBuild());
1370 DebugStream.println("Building = " + building_dir.getAbsolutePath());
1371
1372 // Get the build mode from the build options
1373 String build_mode = collection.build_options.getBuildValue("mode");
1374
1375 // Special case for build mode "all": replace index dir with building dir
1376 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1377 // Remove the old index directory
1378 if (index_dir.exists()) {
1379 Utility.delete(index_dir);
1380
1381 // Wait for a couple of seconds, just for luck
1382 wait(2000);
1383
1384 // Check the delete worked
1385 if (index_dir.exists()) {
1386 throw new Exception("Index directory could not be removed.");
1387 }
1388 }
1389
1390 // Move the building directory to become the new index directory
1391 if (building_dir.renameTo(index_dir) == false) {
1392 throw new Exception("Build directory could not be moved.");
1393 }
1394 }
1395
1396 // Otherwise copy everything in the building dir into the index dir
1397 else {
1398 moveContentsInto(building_dir, index_dir);
1399 }
1400 }
1401 catch (Exception exception) {
1402 JOptionPane.showMessageDialog(Gatherer.g_man, "Exception detected during collection install.\nMost likely caused by Windows or Local Library holding locks on files:\n" + exception.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
1403 return false;
1404 }
1405 return true;
1406 }
1407
1408
1409 /** Moves all the files in one directory into another, overwriting existing files */
1410 private void moveContentsInto(File source_directory, File target_directory)
1411 {
1412 File[] source_files = source_directory.listFiles();
1413 for (int i = 0; i < source_files.length; i++) {
1414 File source_file = source_files[i];
1415 File target_file = new File(target_directory, source_file.getName());
1416
1417 if (source_file.isDirectory()) {
1418 moveContentsInto(source_file, target_file);
1419 source_file.delete();
1420 }
1421 else {
1422 if (target_file.exists()) {
1423 target_file.delete();
1424 }
1425
1426 source_file.renameTo(target_file);
1427 }
1428 }
1429 }
1430
1431
1432 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
1433 {
1434 boolean first_name = true;
1435 boolean first_extra = true;
1436 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath();
1437
1438 // 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.
1439 try {
1440 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
1441 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
1442 String command = null;
1443 while((command = in.readLine()) != null) {
1444 if (command.length()==0) {
1445 // output a new line
1446 out.newLine();
1447 continue;
1448 }
1449 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
1450 while(command.trim().endsWith("\\")) {
1451 command = command.substring(0, command.lastIndexOf("\\"));
1452 String next_line = in.readLine();
1453 if(next_line != null) {
1454 command = command + next_line;
1455 }
1456 }
1457 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
1458 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
1459 String command_type_str = tokenizer.nextToken().toLowerCase();
1460
1461 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
1462 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
1463 StringBuffer new_command = new StringBuffer(command_type_str);
1464 String meta_name = tokenizer.nextToken();
1465 new_command.append(' ');
1466 new_command.append(meta_name);
1467 while (tokenizer.hasMoreTokens()) {
1468 new_command.append(' ');
1469 new_command.append(tokenizer.nextToken());
1470 }
1471 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)) {
1472 // dont save
1473 } else {
1474 write(out, new_command.toString());
1475 }
1476 new_command = null;
1477 continue;
1478 } // if collectionmeta
1479
1480 if(command_type_str.equals(Utility.CFG_CLASSIFY)) {
1481 StringBuffer text = new StringBuffer(command_type_str);
1482 // Read in the classifier command watching for hfile, metadata and sort arguments.
1483 String buttonname = null;
1484 String hfile = null;
1485 String new_metadata = null;
1486 String old_metadata = null;
1487
1488 while(tokenizer.hasMoreTokens()) {
1489 String token = tokenizer.nextToken();
1490 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) {
1491 if(tokenizer.hasMoreTokens()) {
1492 text.append(" ");
1493 text.append(token);
1494 token = tokenizer.nextToken();
1495 hfile = token;
1496 }
1497 }
1498 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) {
1499 if(tokenizer.hasMoreTokens()) {
1500 text.append(" ");
1501 text.append(token);
1502 String temp_metadata = tokenizer.nextToken();
1503 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
1504 if (replacement != null && !replacement.equals("")) {
1505 token = replacement;
1506 old_metadata = temp_metadata;
1507 new_metadata = replacement;
1508 }
1509 else {
1510 token = temp_metadata;
1511 }
1512 temp_metadata = null;
1513 replacement = null;
1514 }
1515 }
1516 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) {
1517 if(tokenizer.hasMoreTokens()) {
1518 text.append(" ");
1519 text.append(token);
1520 String temp_metadata = tokenizer.nextToken();
1521 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
1522 if (replacement != null && !replacement.equals("")) {
1523 token = replacement;
1524 }
1525 else {
1526 token = temp_metadata;
1527 }
1528 temp_metadata = null;
1529 replacement = null;
1530 }
1531 }
1532 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) {
1533 buttonname = token;
1534 }
1535 text.append(' ');
1536 text.append(token);
1537 token = null;
1538 }
1539
1540 // 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)!
1541 if(old_metadata != null && new_metadata != null && buttonname == null) {
1542 text.append(' ');
1543 text.append(Utility.CFG_CLASSIFY_BUTTONNAME);
1544 text.append(' ');
1545 text.append(old_metadata);
1546 }
1547 command = text.toString();
1548 // Replace the hfile if we found it
1549 if(hfile != null && new_metadata != null) {
1550 command = command.replaceAll(hfile, new_metadata + ".txt");
1551 }
1552
1553 buttonname = null;
1554 hfile = null;
1555 new_metadata = null;
1556 old_metadata = null;
1557 write(out, command);
1558 } else {
1559 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
1560 StringBuffer new_command = new StringBuffer(command_type_str);
1561 while (tokenizer.hasMoreTokens()) {
1562 new_command.append(' ');
1563 new_command.append(tokenizer.nextToken());
1564 }
1565
1566 command = new_command.toString();
1567
1568 // 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.
1569 // we really want to build up the whole command here
1570 boolean format_command = command_type_str.equals(Utility.CFG_FORMAT);
1571 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
1572 if (metadata_mapping != null) {
1573 Iterator keys = metadata_mapping.keySet().iterator();
1574 while (keys.hasNext()) {
1575 String target = (String) keys.next();
1576 String replacement = (String) metadata_mapping.get(target);
1577 if (replacement != null && !replacement.equals("")) {
1578 if (format_command) {
1579 target = "\\[" + target + "\\]";
1580 replacement = "{Or}{[" + replacement + "]," + target + "}";
1581 }
1582 command = command.replaceAll(target, replacement);
1583 }
1584 }
1585 }
1586
1587 write(out, command);
1588 }
1589 tokenizer = null;
1590 }
1591 in.close();
1592 in = null;
1593 out.flush();
1594 out.close();
1595 out = null;
1596 }
1597 catch(Exception error) {
1598 DebugStream.printStackTrace(error);
1599 }
1600 // All done, I hope.
1601 }
1602
1603 private void write(BufferedWriter out, String message)
1604 throws Exception {
1605 out.write(message, 0, message.length());
1606 out.newLine();
1607 }
1608
1609
1610 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
1611 private class FMTreeModelListener
1612 implements TreeModelListener {
1613 /** 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.
1614 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1615 */
1616 public void treeNodesChanged(TreeModelEvent event) {
1617 if(collection != null) {
1618 collection.setSaved(false);
1619 }
1620 }
1621 /** 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.
1622 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1623 */
1624 public void treeNodesInserted(TreeModelEvent event) {
1625 if(collection != null) {
1626 collection.setSaved(false);
1627 }
1628 }
1629 /** 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.
1630 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1631 */
1632 public void treeNodesRemoved(TreeModelEvent event) {
1633 if(collection != null) {
1634 collection.setSaved(false);
1635 }
1636 }
1637 /** 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.
1638 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1639 */
1640 public void treeStructureChanged(TreeModelEvent event) {
1641 if(collection != null) {
1642 collection.setSaved(false);
1643 }
1644 }
1645 }
1646}
Note: See TracBrowser for help on using the repository browser.