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

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

Can now base a new collection on an existing one, when using a remote Greenstone server.

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