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

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

A bug fix to the dodgy "incremental rebuild" code -- it would only work until some files or metadata was changed!

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