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

Last change on this file since 10586 was 10586, checked in by mdewsnip, 19 years ago

Now handles mkcol.pl cancels a lot better -- no longer get multiple error dialog boxes.

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