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

Last change on this file since 13586 was 13586, checked in by mdewsnip, 17 years ago

Added a static getLoadedCollectionName() function into CollectionManager, as part of making CollectionManager fully static.

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