source: branches/gsdl-2_62-distribution-branch/gli/src/org/greenstone/gatherer/collection/CollectionManager.java@ 10876

Last change on this file since 10876 was 10876, checked in by kjdon, 18 years ago

added the macros directory to be uploaded along with images directory

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