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

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

removed some unused variables

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