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

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

Finally committing the (many) changes to the GLI to use the new metadata code... I hope this doesn't have too many bugs in it and committing it now doesn't stuff anyone up! (Katherine said I could commit it, so blame her if anything goes wrong).

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