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

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

GLI applet: collection building is now much happier about being cancelled. By Matthew Whyte.

  • 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("Warning: 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 createLockFile(new File(collection_directory_path, LOCK_FILE));
447
448 progress.setProgress(4);
449 progress.setNote(Dictionary.get("CollectionManager.Session_Ready", name));
450 progress.close();
451 }
452 catch (Exception error) {
453 DebugStream.printStackTrace(error);
454 }
455 }
456
457
458 private void createLockFile(File lock_file)
459 {
460 try {
461 Document default_lockfile = Utility.parse("xml/" + LOCK_FILE, true);
462 String user_name = System.getProperty("user.name");
463 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
464 person_element.appendChild(default_lockfile.createTextNode(user_name));
465 person_element = null;
466 user_name = null;
467 String machine_name = Utility.getMachineName();
468 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
469 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
470 machine_element = null;
471 machine_name = null;
472 String date_time = Utility.getDateString();
473 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
474 date_element.appendChild(default_lockfile.createTextNode(date_time));
475 date_element = null;
476 date_time = null;
477 XMLTools.writeXMLFile(lock_file, default_lockfile);
478 }
479 catch (Exception exception) {
480 DebugStream.printStackTrace(exception);
481 }
482 }
483
484
485 public boolean deleteCollection(String collection_name)
486 {
487 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
488 }
489
490
491 public void fireFileAddedToCollection(File file)
492 {
493 // Send the event off to all the CollectionContentsChangedListeners
494 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
495 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
496 }
497 }
498
499
500 /** Retrieve the current collection.
501 * @return The <strong>Collection</strong> itself.
502 */
503 public Collection getCollection() {
504 return collection;
505 }
506
507
508 /** Constructs the absolute filename of the collection's directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;"
509 * @return A <strong>String</strong> containing the directory name.
510 */
511 static public String getCollectionDirectoryPath(String collection_name)
512 {
513 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
514 }
515
516
517 /** Constructs the absolute filename of the collection's .col file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/&lt;col_name&gt;.col"
518 * @return A <strong>String</strong> containing the filename.
519 */
520 public String getCollectionFilePath()
521 {
522 return getCollectionDirectoryPath(collection.getName()) + collection.getName() + ".col";
523 }
524
525
526 /** Constructs the absolute filename of the collection's archives directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/archives/"
527 * @return A <strong>String</strong> containing the filename.
528 */
529 public String getCollectionArchivesDirectoryPath()
530 {
531 return getCollectionDirectoryPath(collection.getName()) + "archives" + File.separator;
532 }
533
534
535 /** Constructs the absolute filename of the collection's building directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/building/"
536 * @return A <strong>String</strong> containing the filename.
537 */
538 public String getCollectionBuildingDirectoryPath()
539 {
540 return getCollectionDirectoryPath(collection.getName()) + "building" + File.separator;
541 }
542
543
544 /** Constructs the absolute filename of the collection's collect.cfg file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/collect.cfg"
545 * @return A <strong>String</strong> containing the filename.
546 */
547 public String getCollectionConfigFilePath()
548 {
549 return getCollectionEtcDirectoryPath() + "collect.cfg";
550 }
551
552
553 /** Constructs the absolute filename of the collection's etc directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/"
554 * @return A <strong>String</strong> containing the filename.
555 */
556 public String getCollectionEtcDirectoryPath()
557 {
558 return getCollectionDirectoryPath(collection.getName()) + "etc" + File.separator;
559 }
560
561
562 /** Constructs the absolute filename of the collection's images directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/images/"
563 * @return A <strong>String</strong> containing the filename.
564 */
565 public String getCollectionImagesDirectoryPath()
566 {
567 return getCollectionDirectoryPath(collection.getName()) + "images" + File.separator;
568 }
569
570
571 /** Constructs the absolute filename of the collection's import directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/import/"
572 * @return A <strong>String</strong> containing the filename.
573 */
574 public String getCollectionImportDirectoryPath()
575 {
576 return getCollectionDirectoryPath(collection.getName()) + "import" + File.separator;
577 }
578
579
580 /** Constructs the absolute filename of the collection's index directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/index/"
581 * @return A <strong>String</strong> containing the filename.
582 */
583 public String getCollectionIndexDirectoryPath()
584 {
585 return getCollectionDirectoryPath(collection.getName()) + "index" + File.separator;
586 }
587
588
589 /** Constructs the absolute filename of the collection's log directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/log/"
590 * @return A <strong>String</strong> containing the filename.
591 */
592 public String getCollectionLogDirectoryPath()
593 {
594 return getCollectionDirectoryPath(collection.getName()) + "log" + File.separator;
595 }
596
597
598 /** Constructs the absolute filename of the collection's metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
599 * @return A <strong>String</strong> containing the filename.
600 */
601 public String getCollectionMetadataDirectoryPath()
602 {
603 return getCollectionDirectoryPath(collection.getName()) + "metadata" + File.separator;
604 }
605
606
607 /** Retrieve the tree model associated with the current collection. */
608 public CollectionTreeModel getCollectionTreeModel()
609 {
610 if (collection_model == null && collection != null) {
611 // Use the import directory to generate a new CollectionTreeModel
612 collection_model = new CollectionTreeModel(new CollectionTreeNode(new File(getCollectionImportDirectoryPath())));
613 // Ensure that the manager is a change listener for the tree.
614 if (fm_tree_model_listener == null) {
615 fm_tree_model_listener = new FMTreeModelListener();
616 }
617 collection_model.addTreeModelListener(fm_tree_model_listener);
618 }
619 return collection_model;
620 }
621
622
623 /** This method when called, creates a new GShell in order to run the import.pl script.
624 * @see org.greenstone.gatherer.Configuration
625 * @see org.greenstone.gatherer.Gatherer
626 * @see org.greenstone.gatherer.gui.BuildOptions
627 * @see org.greenstone.gatherer.shell.GShell
628 * @see org.greenstone.gatherer.shell.GShellListener
629 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
630 * @see org.greenstone.gatherer.util.Utility
631 */
632 public void importCollection() {
633 importing = true;
634
635 if (!saved()) {
636 DebugStream.println("CollectionManager.importCollection().forcesave");
637 import_monitor.saving();
638 saveCollection();
639 }
640
641 DebugStream.println("CollectionManager.importCollection()");
642 //check that we can remove the old index before starting import
643 File index_dir = new File(getCollectionIndexDirectoryPath());
644 if (index_dir.exists()) {
645 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
646 if (!canDelete(index_dir)) {
647 // tell the user
648 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
649 // tell the gui manager
650 // a message for the building log
651 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
652 Gatherer.g_man.create_pane.message(event);
653 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
654 Gatherer.g_man.create_pane.processComplete(event);
655 importing = false;
656 return;
657 }
658 }
659
660 String collect_dir = getCollectDirectory();
661
662 String args[];
663 String lang = Configuration.getLanguage();
664 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
665 args = new String[8];
666 args[0] = Configuration.perl_path;
667 args[1] = "-S";
668 args[2] = Configuration.getScriptPath() + "import.pl";
669 args[3] = "-gli";
670 args[4] = "-language";
671 args[5] = lang;
672 args[6] = "-collectdir";
673 args[7] = collect_dir;
674 }
675 else {
676 args = new String[6];
677 args[0] = Configuration.getScriptPath() + "import.pl";
678 args[1] = "-gli";
679 args[2] = "-language";
680 args[3] = lang;
681 args[4] = "-collectdir";
682 args[5] = collect_dir;
683 }
684 collect_dir = null;
685 args = ArrayTools.add(args, collection.build_options.getImportValues());
686 args = ArrayTools.add(args, collection.getName());
687
688 GShell shell = new GShell(args, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
689 shell.addGShellListener(Gatherer.g_man.create_pane);
690 shell.start();
691 DebugStream.println("CollectionManager.importCollection().return");
692
693 importing = false;
694 }
695
696
697 public void importMetadataSet(MetadataSet external_metadata_set)
698 {
699 // Copy the .mds file into the collection's "metadata" folder...
700 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
701
702 // ...but not if it is the redundant "hidden.mds" file
703 if (external_metadata_set_file.getName().equals("hidden.mds")) {
704 return;
705 }
706
707 // ...and only if it doesn't already exist
708 File metadata_set_file = new File(getCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
709 if (!metadata_set_file.exists()) {
710 try {
711 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, null);
712 }
713 catch (Exception ex) {
714 ex.printStackTrace();
715 }
716
717 // Load it into the MetadataSetManager
718 MetadataSetManager.loadMetadataSet(metadata_set_file);
719 }
720 }
721
722
723 /** 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.
724 * @return true if the gli is currently importing
725 */
726 public boolean isImporting() {
727 return importing;
728 }
729
730
731 public void loadCollection(String collection_file_path)
732 {
733 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"));
734 LoadCollectionTask load_collection_task = new LoadCollectionTask(collection_file_path, load_collection_progress_popup);
735 load_collection_task.start();
736 load_collection_progress_popup.display(); // Blocks, so must go *after* starting the task!
737 }
738
739
740 private class LoadCollectionTask
741 extends Thread
742 {
743 private String collection_file_path = null;
744 private ModalProgressPopup load_collection_progress_popup = null;
745
746 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
747 {
748 this.collection_file_path = collection_file_path;
749 this.load_collection_progress_popup = load_collection_progress_popup;
750 }
751
752 public void run()
753 {
754 loadCollectionInternal(collection_file_path);
755 load_collection_progress_popup.setVisible(false);
756 }
757 }
758
759
760 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
761 * @param location The path to the collection as a <strong>String</strong>.
762 * @see org.greenstone.gatherer.Configuration
763 * @see org.greenstone.gatherer.Gatherer
764 * @see org.greenstone.gatherer.collection.Collection
765 * @see org.greenstone.gatherer.util.Utility
766 */
767 private void loadCollectionInternal(String location)
768 {
769 DebugStream.println("Loading collection " + location + "...");
770 boolean non_gli_collection = false;
771
772 // Check we have actually been given a .col file.
773 if (!location.endsWith(".col")) {
774 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
775 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
776 return;
777 }
778
779 // Check that there is the collection configuration file available
780 File collection_file = new File(location);
781 // Ensure that the directory exists.
782 File collection_directory = collection_file.getParentFile();
783 if (!collection_directory.exists()) {
784 // we cant open this
785 collection_directory = null;
786 return;
787 }
788 File collection_config_file = new File(collection_directory, Utility.CONFIG_FILE);
789 if (!collection_config_file.exists()) {
790 DebugStream.println("CollectionManager.loadCollection: No config file");
791 collection_directory = null;
792 collection_config_file = null;
793 return;
794 }
795
796 // Special case of a user trying to open an old greenstone collection.
797 File collection_metadata_directory = new File(collection_directory, Utility.META_DIR);
798 if (!collection_metadata_directory.exists()) {
799 DebugStream.println("Loading non-gatherer collection...");
800 non_gli_collection = true;
801 }
802
803 // Now determine if a lock already exists on this collection.
804 String name = collection_directory.getName();
805 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
806 if (lock_file.exists()) {
807 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, name, lock_file);
808 int choice = dialog.getChoice();
809 dialog.dispose();
810 dialog = null;
811
812 if (choice != LockFileDialog.YES_OPTION) {
813 // user has cancelled
814 lock_file = null;
815 collection_directory = null;
816 collection_config_file = null;
817 return;
818 }
819
820 lock_file.delete();
821 }
822
823 try {
824 // Create a lock file.
825 createLockFile(lock_file);
826 // This lock file may not have been created so check
827 if(!lock_file.canWrite()) {
828 // The lock file cannot be written to. Most likely cause incorrect file permissions.
829 System.err.println("Cannot write lock file!");
830 String args[] = new String[2];
831 args[0] = location;
832 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Title");
833 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
834 args = null;
835 return;
836 }
837
838 // Open the collection file
839 this.collection = new Collection(collection_file);
840 if (collection.error) {
841 collection = null;
842 // Remove lock file
843 if (lock_file.exists()) {
844 lock_file.delete();
845 }
846 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
847 }
848
849 MetadataSetManager.clearMetadataSets();
850 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
851
852 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
853
854 // If this is a non-GLI (legacy) collection, ask the user to choose some metadata sets
855 if (non_gli_collection) {
856 if (!addSomeMetadataSets(collection_directory)) {
857 lock_file = null;
858 collection_directory = null;
859 closeCollection();
860 return;
861 }
862
863 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
864 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
865 }
866
867 // Read through the metadata.xml files in the import directory, building up the metadata value trees
868 File collection_import_directory = new File(collection_directory, Utility.IMPORT_DIR);
869 MetadataXMLFileManager.clearMetadataXMLFiles();
870 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory);
871
872 // Read through the doc.xml files in the archives directory
873 File collection_archives_directory = new File(getCollectionArchivesDirectoryPath());
874 DocXMLFileManager.clearDocXMLFiles();
875 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory);
876
877 collection.cdm = new CollectionDesignManager(collection_config_file);
878 if (non_gli_collection) {
879 // Change the classifiers to use the namespaced element names
880 LegacyCollectionImporter.updateClassifiers(collection.cdm);
881 }
882
883 // We're done. Let everyone know.
884 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", name));
885 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
886 }
887 catch (Exception error) {
888 // There is obviously no existing collection present.
889 DebugStream.printStackTrace(error);
890 if(error.getMessage() != null) {
891 String[] args = new String[2];
892 args[0] = location;
893 args[1] = error.getMessage();
894 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
895 }
896 else {
897 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
898 }
899 }
900
901 lock_file = null;
902 collection_directory = null;
903 collection_config_file = null;
904 }
905
906
907 private void makeCollection(String description, String email, String name, String title) {
908 // Encode the description so it is safe to write to shell
909 if(Utility.isWindows()) {
910 description = Codec.transform(description, Codec.TEXT_TO_SHELL_WINDOWS);
911 }
912 else {
913 description = Codec.transform(description, Codec.TEXT_TO_SHELL_UNIX);
914 }
915 String collect_dir = getCollectDirectory();
916 // Run the mkcol command.
917 String command[];
918 if((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
919 if(description == null || title == null) {
920 command = new String[8];
921 command[0] = Configuration.perl_path;
922 command[1] = "-S";
923 command[2] = Configuration.getScriptPath() + "mkcol.pl";
924 command[3] = "-collectdir";
925 command[4] = collect_dir;
926 command[5] = "-win31compat";
927 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
928 command[7] = name;
929 }
930 // Users are no longer required to supply an email
931 else if(email == null) {
932 command = new String[12];
933 command[0] = Configuration.perl_path;
934 command[1] = "-S";
935 command[2] = Configuration.getScriptPath() + "mkcol.pl";
936 command[3] = "-collectdir";
937 command[4] = collect_dir;
938 command[5] = "-win31compat";
939 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
940 command[7] = "-title";
941 command[8] = title;
942 command[9] = "-about";
943 command[10] = description;
944 command[11] = name;
945 }
946 else {
947 command = new String[14];
948 command[0] = Configuration.perl_path;
949 command[1] = "-S";
950 command[2] = Configuration.getScriptPath() + "mkcol.pl";
951 command[3] = "-collectdir";
952 command[4] = collect_dir;
953 command[5] = "-win31compat";
954 command[6] = (Gatherer.isGsdlRemote) ? "false" : "true";
955 command[7] = "-title";
956 command[8] = title;
957 command[9] = "-creator";
958 command[10] = email;
959 command[11] = "-about";
960 command[12] = description;
961 command[13] = name;
962 }
963 }
964 else {
965 if(description == null || title == null) {
966 command = new String[6];
967 command[0] = "mkcol.pl";
968 command[1] = "-collectdir";
969 command[2] = collect_dir;
970 command[3] = "-win31compat";
971 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
972 command[5] = name;
973 }
974 else if(email == null) {
975 command = new String[10];
976 command[0] = "mkcol.pl";
977 command[1] = "-collectdir";
978 command[2] = collect_dir;
979 command[3] = "-win31compat";
980 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
981 command[5] = "-title";
982 command[6] = title;
983 command[7] = "-about";
984 command[8] = description;
985 command[9] = name;
986 }
987 else {
988 command = new String[12];
989 command[0] = "mkcol.pl";
990 command[1] = "-collectdir";
991 command[2] = collect_dir;
992 command[3] = "-win31compat";
993 command[4] = (Gatherer.isGsdlRemote) ? "false" : "true";
994 command[5] = "-title";
995 command[6] = title;
996 command[7] = "-creator";
997 command[8] = email;
998 command[9] = "-about";
999 command[10] = description;
1000 command[11] = name;
1001 }
1002 }
1003 GShell process = new GShell(command, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1004 process.run(); // Don't bother threading this... yet
1005 }
1006
1007 /** 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.
1008 * @param event A <strong>GShellEvent</strong> which contains a the message.
1009 */
1010 public synchronized void message(GShellEvent event) {
1011 }
1012
1013 /** This call is fired whenever a process within a GShell created by this class begins.
1014 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1015 * @see org.greenstone.gatherer.Gatherer
1016 * @see org.greenstone.gatherer.gui.GUIManager
1017 * @see org.greenstone.gatherer.shell.GShell
1018 */
1019 public synchronized void processBegun(GShellEvent event) {
1020 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1021 ///ystem.err.println("ProcessBegun " + event.getType());
1022 // If this is one of the types where we wish to lock user control
1023 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1024 }
1025 /** This call is fired whenever a process within a GShell created by this class ends.
1026 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1027 * @see org.greenstone.gatherer.Gatherer
1028 * @see org.greenstone.gatherer.gui.GUIManager
1029 * @see org.greenstone.gatherer.shell.GShell
1030 */
1031 public synchronized void processComplete(GShellEvent event) {
1032 DebugStream.println("CollectionManager.processComplete(" + event.getType() + ")");
1033 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1034 ///ystem.err.println("Recieved process complete event - " + event);
1035 // If we were running an import, now run a build.
1036 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1037 // Finish import.
1038 collection.setImported(true);
1039 buildCollection();
1040 }
1041 // If we were running a build, now is when we move files across.
1042 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1043 if(installCollection()) {
1044 // If we have a local library running then ask it to add our newly create collection
1045 if (LocalLibraryServer.isRunning() == true) {
1046 LocalLibraryServer.addCollection(collection.getName());
1047 }
1048 else if (Gatherer.GS3) {
1049 convertToGS3Collection();
1050 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1051 }
1052
1053 // Fire a collection changed first to update the preview etc buttons
1054 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1055
1056 // Now display a message dialog saying its all built
1057 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", null, false);
1058 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1059 collection_built_warning_dialog.display();
1060 collection_built_warning_dialog.dispose();
1061 collection_built_warning_dialog = null;
1062 }
1063 else {
1064 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1065 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1066 }
1067 }
1068 else if (event.getStatus() == GShell.CANCELLED) {
1069 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1070 Gatherer.g_man.repaint();
1071 }
1072 else if (event.getStatus() == GShell.ERROR) {
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.