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

Last change on this file since 14817 was 14817, checked in by anna, 16 years ago

Improved error message when GLI had permission problems when opening and creating collections.

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