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

Last change on this file since 13326 was 12808, checked in by mdewsnip, 18 years ago

Removed CollectionManager.configurationChanged() and all 50 calls to it. The CollectionConfiguration class now works out itself whether it needs to be changed or not -- a far more reliable approach.

  • Property svn:keywords set to Author Date Id Revision
File size: 64.3 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 private boolean building = false;
87 /** Are we currently in the process of importing? */
88 private boolean importing = false;
89 /** The objects listening for CollectionContentsChanged events. */
90 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 private CollectionTreeModel collection_tree_model = null;
97 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
98 private FMTreeModelListener fm_tree_model_listener = null;
99 /** The monitor resposible for parsing the build process. */
100 private GShellProgressMonitor build_monitor = null;
101 /** The monitor resposible for parsing the import process. */
102 private GShellProgressMonitor import_monitor = null;
103
104 /** 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 import_directory = new File(collection_directory_path + "import");
398 if (!import_directory.exists()) {
399 import_directory.mkdirs();
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 // Always import the extracted metadata set if we didn't already
452 if (MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE) == null) {
453 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
454 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
455 }
456
457 collection.cdm = new CollectionDesignManager(new File(getCollectionConfigFilePath()));
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 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
533 }
534
535
536 public void fireFileAddedToCollection(File file)
537 {
538 // Send the event off to all the CollectionContentsChangedListeners
539 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
540 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
541 }
542 }
543
544
545 /** Retrieve the current collection.
546 * @return The <strong>Collection</strong> itself.
547 */
548 public Collection getCollection() {
549 return collection;
550 }
551
552
553 static public String getCollectionDirectoryPath()
554 {
555 return Gatherer.getCollectDirectoryPath() + collection.getName() + File.separator;
556 }
557
558
559 /** Constructs the absolute filename of the collection's directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;"
560 * @return A <strong>String</strong> containing the directory name.
561 */
562 static public String getCollectionDirectoryPath(String collection_name)
563 {
564 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
565 }
566
567
568 /** Constructs the absolute filename of the collection's .col file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/&lt;col_name&gt;.col"
569 * @return A <strong>String</strong> containing the filename.
570 */
571 public String getCollectionFilePath()
572 {
573 return getCollectionDirectoryPath() + collection.getName() + ".col";
574 }
575
576
577 /** Constructs the absolute filename of the collection's archives directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/archives/"
578 * @return A <strong>String</strong> containing the filename.
579 */
580 public String getCollectionArchivesDirectoryPath()
581 {
582 return getCollectionDirectoryPath() + "archives" + File.separator;
583 }
584
585
586 /** Constructs the absolute filename of the collection's building directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/building/"
587 * @return A <strong>String</strong> containing the filename.
588 */
589 public String getCollectionBuildingDirectoryPath()
590 {
591 return getCollectionDirectoryPath() + "building" + File.separator;
592 }
593
594
595 /** Constructs the absolute filename of the collection's collect.cfg file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/collect.cfg"
596 * @return A <strong>String</strong> containing the filename.
597 */
598 public String getCollectionConfigFilePath()
599 {
600 return getCollectionEtcDirectoryPath() + "collect.cfg";
601 }
602
603
604 /** Constructs the absolute filename of the collection's etc directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/"
605 * @return A <strong>String</strong> containing the filename.
606 */
607 public String getCollectionEtcDirectoryPath()
608 {
609 return getCollectionDirectoryPath() + "etc" + File.separator;
610 }
611
612
613 /** Constructs the absolute filename of the collection's images directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/images/"
614 * @return A <strong>String</strong> containing the filename.
615 */
616 public String getCollectionImagesDirectoryPath()
617 {
618 return getCollectionDirectoryPath() + "images" + File.separator;
619 }
620
621
622 /** Constructs the absolute filename of the collection's import directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/import/"
623 * @return A <strong>String</strong> containing the filename.
624 */
625 public String getCollectionImportDirectoryPath()
626 {
627 return getCollectionDirectoryPath() + "import" + File.separator;
628 }
629
630
631 /** Constructs the absolute filename of the collection's index directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/index/"
632 * @return A <strong>String</strong> containing the filename.
633 */
634 public String getCollectionIndexDirectoryPath()
635 {
636 return getCollectionDirectoryPath() + "index" + File.separator;
637 }
638
639
640 /** Constructs the absolute filename of the collection's log directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/log/"
641 * @return A <strong>String</strong> containing the filename.
642 */
643 public String getCollectionLogDirectoryPath()
644 {
645 return getCollectionDirectoryPath() + "log" + File.separator;
646 }
647
648
649 /** Constructs the absolute filename of the collection's metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
650 * @return A <strong>String</strong> containing the filename.
651 */
652 public String getCollectionMetadataDirectoryPath()
653 {
654 return getCollectionDirectoryPath() + "metadata" + File.separator;
655 }
656
657 /** Constructs the absolute filename of the collection's metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
658 * @return A <strong>String</strong> containing the filename.
659 */
660 public String getCollectionPluginsDirectoryPath()
661 {
662 return getCollectionDirectoryPath() + "perllib" + File.separator +
663 "plugins" + File.separator;
664 }
665 /** Constructs the absolute filename of the collection's metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
666 * @return A <strong>String</strong> containing the filename.
667 */
668 public String getCollectionClassifiersDirectoryPath()
669 {
670 return getCollectionDirectoryPath() + "perllib" + File.separator +
671 "classify" + File.separator;
672 }
673
674
675 public CollectionTree getCollectionTree()
676 {
677 if (collection_tree == null) {
678 collection_tree = new CollectionTree(collection_tree_model, true);
679 }
680
681 return collection_tree;
682 }
683
684
685 /** Retrieve the tree model associated with the current collection. */
686 public CollectionTreeModel getCollectionTreeModel()
687 {
688 if (collection_tree_model == null && collection != null) {
689 // Use the import directory to generate a new CollectionTreeModel
690 collection_tree_model = new CollectionTreeModel(new CollectionTreeNode(new File(getCollectionImportDirectoryPath())));
691 // Ensure that the manager is a change listener for the tree.
692 if (fm_tree_model_listener == null) {
693 fm_tree_model_listener = new FMTreeModelListener();
694 }
695 collection_tree_model.addTreeModelListener(fm_tree_model_listener);
696 }
697 return collection_tree_model;
698 }
699
700
701 /** This method when called, creates a new GShell in order to run the import.pl script.
702 * @see org.greenstone.gatherer.Configuration
703 * @see org.greenstone.gatherer.Gatherer
704 * @see org.greenstone.gatherer.gui.BuildOptions
705 * @see org.greenstone.gatherer.shell.GShell
706 * @see org.greenstone.gatherer.shell.GShellListener
707 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
708 * @see org.greenstone.gatherer.util.Utility
709 */
710 public void importCollection() {
711 importing = true;
712
713 if (!saved()) {
714 DebugStream.println("CollectionManager.importCollection().forcesave");
715 import_monitor.saving();
716 saveCollection();
717 }
718
719 DebugStream.println("CollectionManager.importCollection()");
720 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
721 //check that we can remove the old index before starting import
722 File index_dir = new File(getCollectionIndexDirectoryPath());
723 if (index_dir.exists()) {
724 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
725 if (!canDelete(index_dir)) {
726 // tell the user
727 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
728 // tell the gui manager
729 // a message for the building log
730 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
731 Gatherer.g_man.create_pane.message(event);
732 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
733 Gatherer.g_man.create_pane.processComplete(event);
734 importing = false;
735 return;
736 }
737 }
738
739 // Generate the import.pl command
740 ArrayList command_parts_list = new ArrayList();
741 if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
742 command_parts_list.add(Configuration.perl_path);
743 command_parts_list.add("-S");
744 }
745 command_parts_list.add(LocalGreenstone.getBinScriptDirectoryPath() + "import.pl");
746 command_parts_list.add("-gli");
747 command_parts_list.add("-language");
748 command_parts_list.add(Configuration.getLanguage());
749 command_parts_list.add("-collectdir");
750 command_parts_list.add(getCollectDirectory());
751
752 String[] import_options = collection.import_options.getValues();
753 for (int i = 0; i < import_options.length; i++) {
754 command_parts_list.add(import_options[i]);
755 }
756
757 command_parts_list.add(collection.getName());
758
759 // Run the import.pl command
760 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
761 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
762 shell.addGShellListener(Gatherer.g_man.create_pane);
763 shell.addGShellListener(Gatherer.g_man.format_pane);
764 shell.start();
765 DebugStream.println("CollectionManager.importCollection().return");
766
767 importing = false;
768 }
769
770
771 public void importMetadataSet(MetadataSet external_metadata_set)
772 {
773 // Copy the .mds file into the collection's "metadata" folder...
774 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
775
776 // ...but not if it is the redundant "hidden.mds" file
777 if (external_metadata_set_file.getName().equals("hidden.mds")) {
778 return;
779 }
780
781 // ...and only if it doesn't already exist
782 File metadata_set_file = new File(getCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
783 if (!metadata_set_file.exists()) {
784 try {
785 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, false);
786
787 // If we're using a remote Greenstone server, upload the metadata file
788 if (Gatherer.isGsdlRemote) {
789 RemoteGreenstoneServer.uploadCollectionFile(collection.getName(), metadata_set_file);
790 }
791 }
792 catch (Exception ex) {
793 ex.printStackTrace();
794 }
795
796 // Load it into the MetadataSetManager
797 MetadataSetManager.loadMetadataSet(metadata_set_file);
798 }
799 }
800
801
802 /** 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.
803 * @return true if the gli is currently importing
804 */
805 public boolean isImporting() {
806 return importing;
807 }
808
809
810 public void loadCollection(String collection_file_path)
811 {
812 // Display a modal progress popup to indicate that the collection is being loaded
813 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
814 load_collection_progress_popup.display();
815
816 // Load the collection on a separate thread so the progress bar updates correctly
817 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
818 }
819
820
821 private class LoadCollectionTask
822 extends Thread
823 {
824 private String collection_file_path = null;
825 private ModalProgressPopup load_collection_progress_popup = null;
826
827 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
828 {
829 this.collection_file_path = collection_file_path;
830 this.load_collection_progress_popup = load_collection_progress_popup;
831 }
832
833 public void run()
834 {
835 loadCollectionInternal(collection_file_path);
836 load_collection_progress_popup.close();
837 }
838 }
839
840
841 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
842 * @param location The path to the collection as a <strong>String</strong>.
843 * @see org.greenstone.gatherer.Configuration
844 * @see org.greenstone.gatherer.Gatherer
845 * @see org.greenstone.gatherer.collection.Collection
846 * @see org.greenstone.gatherer.util.Utility
847 */
848 private void loadCollectionInternal(String location)
849 {
850 DebugStream.println("Loading collection " + location + "...");
851
852 if (Gatherer.isGsdlRemote) {
853 String collection_name = location.substring(location.lastIndexOf(File.separator) + 1, location.length() - ".col".length());
854 if (RemoteGreenstoneServer.downloadCollection(collection_name).equals("")) {
855 return;
856 }
857 }
858
859 boolean non_gli_collection = false;
860
861 // Check we have actually been given a .col file.
862 if (!location.endsWith(".col")) {
863 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
864 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
865 return;
866 }
867
868 // Check that the collection configuration file is available
869 File collection_file = new File(location);
870
871 // Ensure that the directory exists
872 File collection_directory = collection_file.getParentFile();
873 if (collection_directory == null || !collection_directory.exists()) {
874 // We can't open this
875 System.err.println("CollectionManager.loadCollection: No collection directory.");
876 return;
877 }
878
879 File collection_config_file = new File(collection_directory, Utility.CONFIG_FILE);
880 if (!collection_config_file.exists()) {
881 System.err.println("CollectionManager.loadCollection: No config file.");
882 collection_directory = null;
883 collection_config_file = null;
884 return;
885 }
886
887 // Ensure that an import directory exists for this collection
888 File collection_import_directory = new File(collection_directory, "import");
889 if (!collection_import_directory.exists()) {
890 collection_import_directory.mkdir();
891 }
892
893 // Special case of a user trying to open an old greenstone collection.
894 File collection_metadata_directory = new File(collection_directory, "metadata");
895 if (!collection_metadata_directory.exists()) {
896 DebugStream.println("Loading non-gatherer collection...");
897 // Show a warning message in case user wants to quit now
898 non_gli_collection = true;
899 WarningDialog legacy_dialog = new WarningDialog("warning.LegacyCollection", Dictionary.get("LegacyCollection.Title"), Dictionary.get("LegacyCollection.Message"), null, true);
900 if (legacy_dialog.display()==JOptionPane.CANCEL_OPTION) {
901 legacy_dialog.dispose();
902 collection_directory = null;
903 collection_config_file = null;
904 return;
905 }
906 legacy_dialog.dispose();
907
908 }
909
910 // Now determine if a lock already exists on this collection.
911 String collection_name = collection_directory.getName();
912 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
913 if (lock_file.exists()) {
914 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, collection_name, lock_file);
915 int choice = dialog.getChoice();
916 dialog.dispose();
917 dialog = null;
918
919 if (choice != LockFileDialog.YES_OPTION) {
920 // user has cancelled
921 lock_file = null;
922 collection_directory = null;
923 collection_config_file = null;
924 return;
925 }
926
927 lock_file.delete();
928 }
929
930 try {
931 // Create a lock file.
932 createLockFile(lock_file);
933 // This lock file may not have been created so check
934 if(!lock_file.canWrite()) {
935 // The lock file cannot be written to. Most likely cause incorrect file permissions.
936 System.err.println("Cannot write lock file!");
937 String args[] = new String[2];
938 args[0] = location;
939 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Title");
940 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
941 args = null;
942 return;
943 }
944
945 // Open the collection file
946 this.collection = new Collection(collection_file);
947 if (collection.error) {
948 collection = null;
949 // Remove lock file
950 if (lock_file.exists()) {
951 lock_file.delete();
952 }
953 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
954 }
955
956 MetadataSetManager.clearMetadataSets();
957 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
958
959 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
960
961 // If this is a non-GLI (legacy) collection, load the
962 // default metadata
963 if (non_gli_collection) {
964 addDefaultMetadataSets(collection_directory);
965
966 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
967 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
968 }
969
970 // Read through the metadata.xml files in the import directory, building up the metadata value trees
971 MetadataXMLFileManager.clearMetadataXMLFiles();
972 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory);
973
974 // Read through the doc.xml files in the archives directory
975 File collection_archives_directory = new File(getCollectionArchivesDirectoryPath());
976 DocXMLFileManager.clearDocXMLFiles();
977 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory);
978
979 // Get a list of the collection specific classifiers and plugins
980 Classifiers.loadClassifiersList(collection_name);
981 Plugins.loadPluginsList(collection_name);
982
983 collection.cdm = new CollectionDesignManager(collection_config_file);
984 if (non_gli_collection) {
985 // Change the classifiers to use the namespaced element names
986 LegacyCollectionImporter.updateClassifiers(collection.cdm);
987 }
988
989 // We're done. Let everyone know.
990 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", collection_name));
991 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
992 }
993 catch (Exception error) {
994 // There is obviously no existing collection present.
995 DebugStream.printStackTrace(error);
996 if(error.getMessage() != null) {
997 String[] args = new String[2];
998 args[0] = location;
999 args[1] = error.getMessage();
1000 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1001 }
1002 else {
1003 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1004 }
1005 }
1006
1007 lock_file = null;
1008 collection_directory = null;
1009 collection_config_file = null;
1010 }
1011
1012
1013 private void makeCollection(String name, String email)
1014 {
1015 // Generate the mkcol.pl command
1016 ArrayList command_parts_list = new ArrayList();
1017 if (Utility.isWindows() && (!Gatherer.isGsdlRemote)) {
1018 command_parts_list.add(Configuration.perl_path);
1019 command_parts_list.add("-S");
1020 }
1021 command_parts_list.add(LocalGreenstone.getBinScriptDirectoryPath() + "mkcol.pl");
1022
1023 command_parts_list.add("-collectdir");
1024 command_parts_list.add(getCollectDirectory());
1025 command_parts_list.add("-win31compat");
1026 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
1027
1028 if (email != null && !email.equals("")) {
1029 command_parts_list.add("-creator");
1030 command_parts_list.add(email);
1031 }
1032
1033 command_parts_list.add(name);
1034
1035 // Run the mkcol.pl command
1036 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1037 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1038 process.run(); // Don't bother threading this... yet
1039 }
1040
1041
1042 /** 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.
1043 * @param event A <strong>GShellEvent</strong> which contains a the message.
1044 */
1045 public synchronized void message(GShellEvent event) {
1046 }
1047
1048
1049 public void metadataChanged(CollectionTreeNode[] file_nodes)
1050 {
1051 if (collection != null) {
1052 collection.setMetadataChanged(true);
1053 }
1054 }
1055
1056
1057 public void openCollectionFromLastTime()
1058 {
1059 // If there was an open collection last session, reopen it
1060 if (Gatherer.open_collection_file_path != null && !Gatherer.isGsdlRemote) {
1061 // If we're using a remote Greenstone server we must download the collection configurations first
1062 // if (Gatherer.isGsdlRemote && !CollectionManager.downloaded_collection_configurations) {
1063 // if (RemoteGreenstoneServer.downloadCollectionConfigurations().equals("")) {
1064 // Something went wrong downloading the collection configurations
1065 // return;
1066 // }
1067
1068 // CollectionManager.downloaded_collection_configurations = true;
1069 // }
1070
1071 // Load the collection now
1072 loadCollection(Gatherer.open_collection_file_path);
1073 }
1074 }
1075
1076
1077 /** This call is fired whenever a process within a GShell created by this class begins.
1078 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1079 * @see org.greenstone.gatherer.Gatherer
1080 * @see org.greenstone.gatherer.gui.GUIManager
1081 * @see org.greenstone.gatherer.shell.GShell
1082 */
1083 public synchronized void processBegun(GShellEvent event) {
1084 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1085 ///ystem.err.println("ProcessBegun " + event.getType());
1086 // If this is one of the types where we wish to lock user control
1087 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1088 }
1089 /** This call is fired whenever a process within a GShell created by this class ends.
1090 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1091 * @see org.greenstone.gatherer.Gatherer
1092 * @see org.greenstone.gatherer.gui.GUIManager
1093 * @see org.greenstone.gatherer.shell.GShell
1094 */
1095 public synchronized void processComplete(GShellEvent event) {
1096 //ystem.err.println("CollectionManager.processComplete(" + event.getType() + ")");
1097 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1098 ///ystem.err.println("Recieved process complete event - " + event);
1099 // If we were running an import, now run a build.
1100 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1101 // Finish import.
1102 collection.setImported(true);
1103 collection.setFilesChanged(false);
1104 collection.setMetadataChanged(false);
1105 buildCollection(false);
1106 }
1107 // If we were running a build, now is when we move files across.
1108 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1109
1110 if(installCollection()) {
1111 // If we have a local library running then ask it to add our newly create collection
1112 if (LocalLibraryServer.isRunning() == true) {
1113 LocalLibraryServer.addCollection(collection.getName());
1114 }
1115 else if (Gatherer.GS3) {
1116 convertToGS3Collection();
1117 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1118 }
1119
1120 // Fire a collection changed first to update the preview etc buttons
1121 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1122
1123 // Now display a message dialog saying its all built
1124 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", "CollectionBuilt.Title", Dictionary.get("CollectionBuilt.Message"), null, false);
1125 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1126 collection_built_warning_dialog.display();
1127 collection_built_warning_dialog.dispose();
1128 collection_built_warning_dialog = null;
1129
1130 //Set nothing as needing rebuilding, as a build has just finished :-)
1131 CollectionDesignManager.resetRebuildTypeRequired();
1132 }
1133 else {
1134 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1135 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1136 DebugStream.println("Status is ok but !installCollection()");
1137 }
1138 }
1139 else if (event.getStatus() == GShell.CANCELLED) {
1140 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1141 Gatherer.g_man.repaint();
1142 }
1143 else if (event.getStatus() == GShell.ERROR) {
1144 DebugStream.println("There was an error in the gshell:"+ event.getMessage());
1145 if (event.getType() == GShell.NEW) {
1146 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1147 } else {
1148 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1149 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1150 }
1151
1152 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.
1153 }
1154 }
1155
1156
1157 /** Determine if the manager is ready for actions apon its collection.
1158 * @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.
1159 */
1160 static public synchronized boolean ready() {
1161 if(collection != null) {
1162 return true;
1163 }
1164 else {
1165 return false;
1166 }
1167 }
1168
1169
1170 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1171 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1172 */
1173 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1174 build_monitor = monitor;
1175 }
1176 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1177 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1178 */
1179 public void registerImportMonitor(GShellProgressMonitor monitor) {
1180 import_monitor = monitor;
1181 }
1182
1183
1184 public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1185 {
1186 collection_contents_changed_listeners.remove(listener);
1187 }
1188
1189
1190 public void removeMetadataSet(MetadataSet metadata_set)
1191 {
1192 DebugStream.println("Removing metadata set...");
1193
1194 // Delete the .mds file from the collection's "metadata" folder...
1195 File metadata_set_file = metadata_set.getMetadataSetFile();
1196
1197 // ...but not if it is the "ex.mds" file
1198 if (metadata_set_file.getName().equals("ex.mds")) {
1199 return;
1200 }
1201
1202 // ...and only if it exists
1203 if (metadata_set_file.exists()) {
1204 metadata_set_file.delete();
1205
1206 // Unload it from the MetadataSetManager
1207 MetadataSetManager.unloadMetadataSet(metadata_set);
1208
1209 // If we're using a remote Greenstone server, delete the metadata file on the server
1210 if (Gatherer.isGsdlRemote) {
1211 RemoteGreenstoneServer.deleteCollectionFile(collection.getName(), metadata_set_file);
1212 }
1213 }
1214 }
1215
1216
1217 /** Used to check whether all open collections have a 'saved' state.
1218 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1219 * @see org.greenstone.gatherer.collection.Collection
1220 */
1221 public boolean saved() {
1222 boolean result = true;
1223 if(collection != null) {
1224 result = collection.getSaved();
1225 }
1226 return result;
1227 }
1228
1229
1230 /** Saves the currently loaded collection. */
1231 public void saveCollection()
1232 {
1233 DebugStream.println("Saving collection " + collection.getName() + "...");
1234
1235 // Change cursor to hourglass
1236 Gatherer.g_man.wait(true);
1237
1238 // Create a backup of the collection file, just in case anything goes wrong
1239 File collection_file = new File(getCollectionFilePath());
1240 if (collection_file.exists()) {
1241 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1242 if (!collection_file.renameTo(collection_file_backup)) {
1243 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1244 }
1245 collection_file_backup.deleteOnExit();
1246 }
1247
1248 // Write out the collection file
1249 collection.save();
1250
1251 // Write out the collection configuration file
1252 collection.cdm.save();
1253
1254 // Change cursor back to normal
1255 Gatherer.g_man.wait(false);
1256 }
1257
1258
1259 /** 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] */
1260 private boolean addDefaultMetadataSets(File collection_dir) {
1261
1262
1263 // Add dublin core which is the default metadata set. The user
1264 // can change this later
1265 File dc_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"dublin.mds");
1266 if (dc_file.exists()) {
1267 importMetadataSet(new MetadataSet(dc_file));
1268 }
1269
1270 // Always import the extracted metadata set
1271 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1272 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1273
1274 return true;
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(getCollectionIndexDirectoryPath());
1309 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1310
1311 File building_dir = new File(getCollectionBuildingDirectoryPath());
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(getCollectionIndexDirectoryPath()));
1334 RemoteGreenstoneServer.moveCollectionFile(collection.getName(), new File(getCollectionBuildingDirectoryPath()), new File(getCollectionIndexDirectoryPath()));
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.