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

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

Made a bit more of CollectionManager static.

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