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

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

Removed some more stuff from Utility.

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