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

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

Made major rearrangements to the way path variables are calculated. This is to facilitate being able to specify a "-collectdir" argument to the GLI so one GLI installation can be used in a networked, multi-user situation. Hope I haven't broken anything!

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