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

Last change on this file since 6842 was 6842, checked in by mdewsnip, 20 years ago

Variable names changes needed because of the name change of the Gather, Enrich and Design pane java files.

  • Property svn:keywords set to Author Date Id Revision
File size: 69.8 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.lang.Class;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.event.*;
44import javax.swing.filechooser.FileSystemView;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.Dictionary;
47import org.greenstone.gatherer.Gatherer;
48import org.greenstone.gatherer.cdm.CollectionDesignManager;
49import org.greenstone.gatherer.cdm.CollectionMeta;
50import org.greenstone.gatherer.cdm.CollectionMetaManager;
51import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
52import org.greenstone.gatherer.collection.Collection;
53import org.greenstone.gatherer.collection.SaveCollectionTask;
54import org.greenstone.gatherer.file.FileNode;
55import org.greenstone.gatherer.file.FileSystemModel;
56import org.greenstone.gatherer.gui.LockFileDialog;
57import org.greenstone.gatherer.gui.NewCollectionMetadataPrompt;
58import org.greenstone.gatherer.gui.ExternalCollectionPrompt;
59import org.greenstone.gatherer.gui.NewMetaSetPrompt;
60import org.greenstone.gatherer.gui.WarningDialog;
61import org.greenstone.gatherer.gui.tree.WorkspaceTree;
62import org.greenstone.gatherer.msm.ElementWrapper;
63import org.greenstone.gatherer.msm.GDMManager;
64import org.greenstone.gatherer.msm.GreenstoneArchiveParser;
65import org.greenstone.gatherer.msm.LegacyCollectionImporter;
66import org.greenstone.gatherer.msm.MetadataSet;
67import org.greenstone.gatherer.msm.MetadataSetManager;
68import org.greenstone.gatherer.msm.MSMEvent;
69import org.greenstone.gatherer.msm.MSMListener;
70import org.greenstone.gatherer.msm.MSMProfiler;
71import org.greenstone.gatherer.msm.MSMUtils;
72import org.greenstone.gatherer.shell.GShell;
73import org.greenstone.gatherer.shell.GShellEvent;
74import org.greenstone.gatherer.shell.GShellListener;
75import org.greenstone.gatherer.shell.GShellProgressMonitor;
76import org.greenstone.gatherer.undo.UndoManager;
77import org.greenstone.gatherer.util.ArrayTools;
78import org.greenstone.gatherer.util.Codec;
79import org.greenstone.gatherer.util.GSDLSiteConfig;
80import org.greenstone.gatherer.util.StaticStrings;
81import org.greenstone.gatherer.util.SynchronizedTreeModelTools;
82import org.greenstone.gatherer.util.Utility;
83import org.w3c.dom.*;
84
85/** 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.
86 * @author John Thompson
87 * @version 2.3
88 */
89public class CollectionManager
90 implements GShellListener, MSMListener {
91 /** A reference to the metadata set manager. */
92 public MetadataSetManager msm;
93 /** A reference to the undo manager. Although only one instance is shared between all collections, the undo queues are emptied between each. */
94 public UndoManager undo;
95 /** Are we currently in the process of building? */
96 private boolean building = false;
97 /** Are we currently in the process of importing? */
98 private boolean importing = false;
99 /** The collection this manager is managing! */
100 static private Collection collection = null;
101 /** The collection_model. */
102 private FileSystemModel collection_model = null;
103 /** The workspace model. This becomes invalid on a collection change. */
104 // private FileSystemModel workspace_model = null;
105 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
106 private FMTreeModelListener fm_tree_model_listener = null;
107 /** The monitor resposible for parsing the build process. */
108 private GShellProgressMonitor build_monitor = null;
109 /** The monitor resposible for parsing the copy process. */
110 private GShellProgressMonitor copy_monitor = null;
111 /** The monitor resposible for parsing the import process. */
112 private GShellProgressMonitor import_monitor = null;
113
114 private int special_case = -1;
115 /** Holds a reference to the thread responsible for closing the collection. If non-null then only calls from the given thread will see the collection is non-ready. All other threads will have to wait until closing thread, and all of it consequential calls, are completely finished. */
116 private Thread closing_thread = null;
117
118 /** The name of the standard lock file. */
119 static final public String LOCK_FILE = "gli.lck";
120 static final public int NO_SPECIAL_CASE = -1;
121 static final public int SPECIAL_DLS = 0;
122
123 /** Used to indicate the source of the message is the file collection methods. */
124 static final public int COLLECT = 3;
125 /** Used to indicate the source of the message is the building methods. */
126 static final public int BUILDING = 5;
127
128 /** Constructor. */
129 public CollectionManager() {
130 // Initialisation.
131 this.building = false;
132 this.importing = false;
133 this.collection = null;
134 this.undo = new UndoManager();
135 }
136 /** Add a special directory mapping.
137 * @param name The name for this directory mapping as a <strong>String</strong>.
138 * @param file The directory this mapping maps to as a <strong>File</strong>.
139 */
140 public void addDirectoryMapping(String name, File file) {
141 // Update the information stored in the Gatherer config
142 Gatherer.config.addDirectoryMapping(name, file);
143 // Now update the tree
144 Gatherer.g_man.gather_pane.refreshWorkspaceTree(WorkspaceTree.MAPPED_DIRECTORIES_CHANGED);
145 }
146 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
147 * @see org.greenstone.gatherer.Configuration
148 * @see org.greenstone.gatherer.Gatherer
149 * @see org.greenstone.gatherer.collection.Collection
150 * @see org.greenstone.gatherer.gui.BuildOptions
151 * @see org.greenstone.gatherer.shell.GShell
152 * @see org.greenstone.gatherer.shell.GShellListener
153 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
154 * @see org.greenstone.gatherer.util.Utility
155 */
156 public void buildCollection() {
157 Gatherer.println("CollectionManager.buildCollection()");
158 building = true;
159
160 String args[];
161 if(Utility.isWindows()) {
162 args = new String[5];
163 args[0] = Gatherer.config.perl_path;
164 args[1] = "-S";
165 args[2] = Gatherer.config.getScriptPath() + "buildcol.pl";
166 args[3] = "-gli";
167 args[4] = collection.getName();
168 }
169 else {
170 args = new String[3];
171 args[0] = Gatherer.config.getScriptPath() + "buildcol.pl";
172 args[1] = "-gli";
173 args[2] = collection.getName();
174 }
175 args = ArrayTools.add(args, collection.build_options.getBuildValues());
176 GShell shell = new GShell(args, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
177 shell.addGShellListener(Gatherer.g_man.create_pane);
178 shell.start();
179 Gatherer.println("CollectionManager.buildCollection().return");
180 }
181
182 /** Used to determine whether the currently active collection has been built.
183 * @return A boolean indicating the built status of the collection.
184 */
185 public boolean built() {
186 if(collection != null) {
187 // Determine if the collection has been built by looking for the build.cfg file
188 File build_cfg_file = new File(getCollectionIndex() + Utility.BUILD_CFG_FILENAME);
189 return build_cfg_file.exists();
190 }
191 return false;
192 }
193
194 /** 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 */
195 public boolean canDelete(File file) {
196 if (!file.isDirectory()) {
197 return file.canWrite();
198 }
199 File [] file_list = file.listFiles();
200 for (int i=0; i<file_list.length; i++) {
201 if (!canDelete(file_list[i])) {
202 return false;
203 }
204 }
205 return true;
206 }
207 /** Called to close the current collection and remove its lock file.
208 * @see org.greenstone.gatherer.Gatherer
209 * @see org.greenstone.gatherer.collection.Collection
210 * @see org.greenstone.gatherer.util.Utility
211 */
212 public void closeCollection() {
213 Gatherer.println("Close collection: " + collection.getName());
214 // We set the closing thread, as it should be the only one who can actually see the collection is closed, at least until the closing thread expires.
215 closing_thread = Thread.currentThread();
216 // Remove the lock on this file, then remove the collection.
217 File lock_file = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + LOCK_FILE);
218 lock_file.delete();
219 if(lock_file.exists()) {
220 System.err.println("Lockfile was not successfully deleted.");
221 }
222 collection.msm.destroy();
223 collection = null;
224 collection_model = null;
225 // workspace_model = null;
226 undo.clear();
227 Gatherer.config.setCollectionConfiguration(null);
228 Gatherer.g_man.collectionChanged(false);
229 // All of the consequences of a close should have been processed by now, so others should now see the collection as non-ready.
230 closing_thread = null;
231 }
232
233 /** Method that is called whenever something has changed in the configuration of this collection. */
234 public void configurationChanged() {
235 if(collection != null) {
236 collection.setSaved(false);
237 }
238 }
239
240 /** Used to set the current collection to the given collection. Note that this call should -always- be proceeded by a ready call, and if the colection is ready and the saved flag is unset then the user should be prompted to save. Also note that this method creates yet another GShell to run buildcol.pl.
241 * @param description a description of the collection as a String
242 * @param email the email address of the author/maintainer as a String
243 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
244 * @param title the longer title of the collection as a String
245 * @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
246 * @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
247 */
248 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets) {
249 boolean cancelled = false;
250 special_case = NO_SPECIAL_CASE;
251 try {
252 // Create a progress monitor.
253 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, Dictionary.get("CollectionManager.Creating_New"), "mkcol.pl", 0, 7);
254 // Create the new collection.
255 makeCollection(description, email, name, title);
256 progress.setProgress(1);
257
258 String a_dir = Utility.getCollectionDir(Gatherer.config.gsdl_path) + name + File.separator;
259
260 // ACTIVE_DIR/log/
261 File log_dir_temp = new File(Utility.getLogDir(a_dir)+"temp.dat");
262 File log_dir = log_dir_temp.getParentFile();
263 log_dir.mkdirs();
264 if(progress != null) {
265 progress.setNote(Dictionary.get("CollectionManager.Log_Created"));
266 }
267
268 progress.setProgress(2);
269
270 // Now create the collection object around the directory.
271 collection = new Collection(new File(a_dir, name + ".col"));
272 collection.msm = new MetadataSetManager();
273 msm = collection.msm; // Legacy
274 collection.msm.load();
275
276 // Import default metadata sets if any.
277 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
278 MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i);
279 collection.msm.importMDS(metadata_set.getFile(), false);
280 }
281
282 boolean skip_import_phase = false;
283
284 // Before we create the CollectionDesignManager we have to check if we are basing it upon some other collection.
285 if(base_collection_directory != null) {
286 Gatherer.println("Basing new collection on existing one: " + base_collection_directory);
287 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
288 // Try to import any existing metadata sets for this collection. Look in base_collection_directory/metadata and import any metadata sets found.
289 File base_metadata = new File(base_collection_directory, Utility.META_DIR);
290 if(base_metadata.exists()) {
291 Gatherer.println("Found the metadata directory.");
292 File[] possible_metadata_sets = base_metadata.listFiles();
293 for(int i = 0; possible_metadata_sets != null && i < possible_metadata_sets.length; i++) {
294 String filename = possible_metadata_sets[i].getName();
295 if(filename.endsWith(".mds")) {
296 Gatherer.println("+ Found a metadata set. Importing: " + possible_metadata_sets[i].getAbsolutePath());
297 collection.msm.importMDS(possible_metadata_sets[i], false);
298 skip_import_phase = true;
299 }
300 }
301 }
302 else {
303 Gatherer.println("This base collection has no metadata directory.");
304 }
305 // If no sets were imported, then create a new metadata with this new collections name. Note that if this is one of the 'big five' collections then I already know the importing mappings as well.
306 if(collection.msm.getSets().size() == 0) {
307 Gatherer.println("As there are no metadata sets loaded yet, we'll make special checks for the collections available on the GS2.39CD.");
308 BasicCollectionConfiguration col_con = new BasicCollectionConfiguration(new File(base_collection_directory, Utility.CONFIG_DIR));
309 // If the source collection is one of the 'big five' then we know what the metadata set is.
310 String collection_name = col_con.getName();
311 // Demo collection - part of the DLS
312 if(collection_name.equals(Utility.COLLECTION_DEMO) || collection_name.equals(Utility.COLLECTION_DLS)) {
313 Gatherer.println("+ This is a DLS.mds collection.");
314 special_case = SPECIAL_DLS;
315 String demo_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DEMO_DIRECTORY)).getAbsolutePath();
316 String dls_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DLS_DIRECTORY)).getAbsolutePath();
317 // Add the dls.mds
318 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.DLS_MDS), false);
319 // Add the mappings for the dls (even if its not present).
320 collection.msm.profiler.addAction(dls_directory, "AZList", "dls.AZList");
321 collection.msm.profiler.addAction(dls_directory, "Keyword", "dls.Keyword");
322 collection.msm.profiler.addAction(dls_directory, "Language", "dls.Language");
323 collection.msm.profiler.addAction(dls_directory, "Organization", "dls.Organization");
324 collection.msm.profiler.addAction(dls_directory, "Subject", "dls.Subject");
325 collection.msm.profiler.addAction(dls_directory, "Title", "dls.Title");
326 // Add the mappings for the demo dls (even if its not present).
327 collection.msm.profiler.addAction(demo_directory, "AZList", "dls.AZList");
328 collection.msm.profiler.addAction(demo_directory, "Keyword", "dls.Keyword");
329 collection.msm.profiler.addAction(demo_directory, "Language", "dls.Language");
330 collection.msm.profiler.addAction(demo_directory, "Organization", "dls.Organization");
331 collection.msm.profiler.addAction(demo_directory, "Subject", "dls.Subject");
332 collection.msm.profiler.addAction(demo_directory, "Title", "dls.Title");
333 // Skip the import phase
334 skip_import_phase = true;
335 }
336 // Prompt the user so that they can choose at least one initial metadata set. We're sneaky here and just create a ncm_prompt
337 else {
338 Gatherer.println("This collection has no metadata sets, nor is it one of our special collections, thus we cannot import any metadata sets automatically. Present the user with the metadata set selection prompt.");
339 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt();
340 // If cancelled then they really do mean to start a collection with no metadata sets.
341 if(!ncm_prompt.isCancelled()) {
342 ArrayList initial_sets = ncm_prompt.getSets();
343 for(int i = 0; initial_sets != null && i < initial_sets.size(); i++) {
344 MetadataSet metadata_set = (MetadataSet) initial_sets.get(i);
345 collection.msm.importMDS(metadata_set.getFile(), false);
346 metadata_set = null;
347 }
348 initial_sets = null;
349 }
350 ncm_prompt.dispose();
351 ncm_prompt = null;
352 }
353 }
354 // Do a dry metadata import run over the entire base collection, recording profile mappings. We do this by finding the archive files, and then iterating over them using the GreenstoneArchiveParser to retrieve metadata from them. We then process the importing of new metadata elements using the selectElement prompt used in a file action metadata import. However the big change is that we don't actually import any metadata, just create importing profiles.
355 if(!skip_import_phase) {
356 File base_archive = new File(base_collection_directory, Utility.ARCHIVE_DIR);
357 if(base_archive.exists()) {
358 Gatherer.println("+ Archive directory found. Inspecting archives for metadata information.");
359 ArrayList metadata_elements = GreenstoneArchiveParser.extractMetadataElements(base_archive);
360 for(int i = 0; !cancelled && i < metadata_elements.size(); i++) {
361 String metadata_name = (String) metadata_elements.get(i);
362 ElementWrapper target = collection.msm.prompt.selectElement(metadata_name);
363 cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled();
364 if(!cancelled) {
365 if(target != null) {
366 collection.msm.profiler.addAction(base_collection_directory.getAbsolutePath(), metadata_name, target.getName());
367 }
368 else {
369 collection.msm.profiler.addAction(base_collection_directory.getAbsolutePath(), metadata_name, null);
370 }
371 }
372 }
373 // Hopefully mappings should now be in place for metadata extracted from this collection.
374 }
375 else {
376 Gatherer.println("+ Searching files for metadata.xml information.");
377 // Find the import directory
378 File base_import = new File(base_collection_directory, Utility.IMPORT_DIR);
379 if(base_import.exists()) {
380 searchForMetadata(base_import);
381 }
382 }
383 }
384 // And if that fails then we must have been asked by Satan himself to build the very digital collections of hell, because they don't match any goodly greenstone collection I have ever seen, so you can't blame me if I can't import them.
385
386 // Now we update our collect.cfg
387 Gatherer.println("Copy and update collect.cfg from base collection.");
388 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_DIR), new File(a_dir, Utility.CONFIG_DIR), description, email, title);
389 }
390
391 // Always import the extracted metadata set if we didn't already
392 if(collection.msm.getSet(Utility.EXTRACTED_METADATA_NAMESPACE) == null) {
393 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION), false);
394 }
395
396 collection.cdm = new CollectionDesignManager(new File(getCollectionConfig()));
397
398 // 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
399 if(base_collection_directory != null) {
400 // Update the creator and maintainer
401 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
402 creator_collectionmeta.setValue(email);
403 creator_collectionmeta = null;
404 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
405 maintainer_collectionmeta.setValue(email);
406 maintainer_collectionmeta = null;
407 // Update the collection title
408 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
409 collection_name_collectionmeta.setValue(title);
410 collection_name_collectionmeta = null;
411 // And now the description
412 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
413 collection_extra_collectionmeta.setValue(description);
414 collection_extra_collectionmeta = null;
415 // All collections based on others are automatically public
416 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
417 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
418 public_collectionmeta = null;
419 // Finally reset the icons
420 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
421 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
422 icon_collection_collectionmeta = null;
423 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
424 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
425 icon_collection_small_collectionmeta = null;
426 }
427
428 collection.gdm = new GDMManager();
429
430 progress.setProgress(3);
431
432 // Has to be done after creating metadata set manager.
433 File gmeta_dir_temp = new File(getCollectionMetadata()+"temp.dat");
434 File gmeta_dir = gmeta_dir_temp.getParentFile();
435 gmeta_dir.mkdirs();
436 if(progress != null) {
437 progress.setNote("GMeta created");
438 }
439 progress.setProgress(4);
440
441 progress.setProgress(6);
442 // Register ourselves as being interested in what the msm has to say.
443 collection.msm.addMSMListener(this);
444 // Create a lock file.
445 File lock_file = new File(a_dir, LOCK_FILE);
446 FileOutputStream out = new FileOutputStream(lock_file);
447 out.write(LOCK_FILE.getBytes());
448 out.close();
449 out = null;
450 progress.setProgress(7);
451 String args[] = new String[1];
452 args[0] = name;
453 progress.setNote(Dictionary.get("CollectionManager.Session_Ready", args));
454 progress.close();
455 }
456 catch (Exception error) {
457 Gatherer.printStackTrace(error);
458 }
459 // Done.
460 if(Gatherer.g_man != null) {
461 // workspace_model = null;
462 // set the view to Gather pane
463 Gatherer.g_man.setSelectedView(Gatherer.g_man.gather_pane);
464 Gatherer.g_man.collectionChanged(ready());
465 }
466 }
467
468 public void createLockFile(File destination) {
469 try {
470 Document default_lockfile = Utility.parse("xml/" + LOCK_FILE, true);
471 String user_name = System.getProperty("user.name");
472 Element person_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
473 person_element.appendChild(default_lockfile.createTextNode(user_name));
474 person_element = null;
475 user_name = null;
476 String machine_name = Utility.getMachineName();
477 Element machine_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
478 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
479 machine_element = null;
480 machine_name = null;
481 String date_time = Utility.getDateString();
482 Element date_element = (Element) MSMUtils.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
483 date_element.appendChild(default_lockfile.createTextNode(date_time));
484 date_element = null;
485 date_time = null;
486 Utility.export(default_lockfile, destination);
487 }
488 catch (Exception error) {
489 Gatherer.printStackTrace(error);
490 }
491 }
492
493 /** Method that is called whenever an element within a set is changed or modified. We want to mark the collection so that it needs saving again.
494 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
495 * @see org.greenstone.gatherer.collection.Collection
496 */
497 public void elementChanged(MSMEvent event) {
498 // This means the state of the collections has changed, so we should set saved to false.
499 collection.setSaved(false);
500 }
501 /** Used to retrieve the build options associated with the currently loaded collection. If none yet exist, default ones are created.
502 * @return A <strong>BuildOptions</strong> object containing the build options for the current collection.
503 * @see org.greenstone.gatherer.collection.Collection
504 */
505 /* private BuildOptions getBuildOptions() {
506 return collection.build_options;
507 } */
508
509 /** Retrieve the current collection.
510 * @return The <strong>Collection</strong> itself.
511 */
512 public Collection getCollection() {
513 return collection;
514 }
515 /** Constructs the absolute filename of the collection archive directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/archive/"
516 * @return A <strong>String</strong> containing the filename.
517 * @see org.greenstone.gatherer.Configuration
518 * @see org.greenstone.gatherer.Gatherer
519 * @see org.greenstone.gatherer.collection.Collection
520 * @see org.greenstone.gatherer.util.Utility
521 */
522 public String getCollectionArchive() {
523 return Utility.getArchiveDir(Gatherer.config.gsdl_path, collection.getName());
524 }
525 /** Constructs the absolute filename of the collection building directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/building/"
526 * @return A <strong>String</strong> containing the filename.
527 * @see org.greenstone.gatherer.Configuration
528 * @see org.greenstone.gatherer.Gatherer
529 * @see org.greenstone.gatherer.collection.Collection
530 * @see org.greenstone.gatherer.util.Utility
531 */
532 public String getCollectionBuild() {
533 return Utility.getBuildDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
534 }
535
536 /** Constructs the absolute filename of the collection config file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/collect.cfg"
537 * @return A <strong>String</strong> containing the filename.
538 * @see org.greenstone.gatherer.Configuration
539 * @see org.greenstone.gatherer.Gatherer
540 * @see org.greenstone.gatherer.collection.Collection
541 * @see org.greenstone.gatherer.util.Utility
542 */
543 public String getCollectionConfig() {
544 return Utility.getConfigDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
545 }
546
547 /** Constructs the absolute filename of the collection directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;"
548 * @return A <strong>String</strong> containing the directory name.
549 * @see org.greenstone.gatherer.Configuration
550 * @see org.greenstone.gatherer.Gatherer
551 * @see org.greenstone.gatherer.collection.Collection
552 * @see org.greenstone.gatherer.util.Utility
553 */
554 public String getCollectionDirectory() {
555 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator;
556 }
557
558 /** Constructs the absolute filename of the collection etc directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/"
559 * @return A <strong>String</strong> containing the filename.
560 * @see org.greenstone.gatherer.Configuration
561 * @see org.greenstone.gatherer.Gatherer
562 * @see org.greenstone.gatherer.collection.Collection
563 * @see org.greenstone.gatherer.util.Utility
564 */
565 public String getCollectionEtc() {
566 return Utility.getEtcDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
567 }
568 /** Constructs the absolute filename of the collection file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/&lt;col_name&gt;.col"
569 * @return A <strong>String</strong> containing the filename.
570 * @see org.greenstone.gatherer.Configuration
571 * @see org.greenstone.gatherer.Gatherer
572 * @see org.greenstone.gatherer.collection.Collection
573 * @see org.greenstone.gatherer.util.Utility
574 */
575 public String getCollectionFilename() {
576 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + collection.getName() + ".col";
577 }
578 /** Constructs the absolute filename of the collection images directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/images/"
579 * @return A <strong>String</strong> containing the filename.
580 * @see org.greenstone.gatherer.Configuration
581 * @see org.greenstone.gatherer.Gatherer
582 * @see org.greenstone.gatherer.collection.Collection
583 * @see org.greenstone.gatherer.util.Utility
584 */
585 public String getCollectionImages() {
586 return Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + StaticStrings.IMAGES_FOLDER;
587 }
588 /** Constructs the absolute filename of the collection import directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/import/"
589 * @return A <strong>String</strong> containing the filename.
590 * @see org.greenstone.gatherer.Configuration
591 * @see org.greenstone.gatherer.Gatherer
592 * @see org.greenstone.gatherer.collection.Collection
593 * @see org.greenstone.gatherer.util.Utility
594 */
595 public String getCollectionImport() {
596 return Utility.getImportDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
597 }
598 /** Constructs the absolute filename of the collection index directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/index/"
599 * @return A <strong>String</strong> containing the filename.
600 * @see org.greenstone.gatherer.Configuration
601 * @see org.greenstone.gatherer.Gatherer
602 * @see org.greenstone.gatherer.collection.Collection
603 * @see org.greenstone.gatherer.util.Utility
604 */
605 public String getCollectionIndex() {
606 return Utility.getIndexDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
607 }
608 /** Constructs the absolute filename of the collection log directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/log/"
609 * @return A <strong>String</strong> containing the filename.
610 * @see org.greenstone.gatherer.Configuration
611 * @see org.greenstone.gatherer.Gatherer
612 * @see org.greenstone.gatherer.collection.Collection
613 * @see org.greenstone.gatherer.util.Utility
614 */
615 public String getCollectionLog() {
616 return Utility.getLogDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
617 }
618 /** Constructs the absolute filename of the collection metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
619 * @return A <strong>String</strong> containing the filename.
620 * @see org.greenstone.gatherer.Configuration
621 * @see org.greenstone.gatherer.Gatherer
622 * @see org.greenstone.gatherer.collection.Collection
623 * @see org.greenstone.gatherer.util.Utility
624 */
625 public String getCollectionMetadata() {
626 return Utility.getMetadataDir(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator);
627 }
628
629 /** This method either returns the title of the current collection, or a placeholder string of 'No Collection'.
630 * @return A <strong>String</strong> which represents what we wish to display for a collection title.
631 * @see org.greenstone.gatherer.collection.Collection
632 */
633 /* private String getCollectionTitle() {
634 if(collection != null) {
635 return collection.getTitle();
636 }
637 return Dictionary.get("Collection.No_Collection");
638 } */
639
640 /** Retrieve the record set (tree model) associated with the current collection. */
641 public TreeModel getRecordSet() {
642 if(collection_model == null && collection != null) {
643 // Use the import directory to generate a new FileSystemModel
644 collection_model = new FileSystemModel(new FileNode(new File(getCollectionImport()), false));
645 // Ensure that the manager is a change listener for the tree.
646 if(fm_tree_model_listener == null) {
647 fm_tree_model_listener = new FMTreeModelListener();
648 }
649 collection_model.addTreeModelListener(fm_tree_model_listener);
650 }
651 return collection_model;
652 }
653
654
655 static public FileNode getGreenstoneCollectionsMapping()
656 {
657 FileNode greenstone_collections_node = new FileNode(Dictionary.get("Tree.World"));
658 greenstone_collections_node.unmap();
659 return greenstone_collections_node;
660 }
661
662
663 static public FileNode[] getCollectionSpecificMappings()
664 {
665 // Return any predefined special directories
666 HashMap mappings = Gatherer.config.getDirectoryMappings();
667 FileNode[] mapping_nodes = new FileNode[mappings.size()];
668 Iterator mappings_iterator = mappings.keySet().iterator();
669 for (int i = 0; mappings_iterator.hasNext(); i++) {
670 String mapping_name = (String) mappings_iterator.next();
671 File mapping_file = (File) mappings.get(mapping_name);
672 mapping_nodes[i] = new FileNode(mapping_file, mapping_name);
673 }
674 return mapping_nodes;
675 }
676
677
678 /** This method when called, creates a new GShell in order to run the import.pl script.
679 * @see org.greenstone.gatherer.Configuration
680 * @see org.greenstone.gatherer.Gatherer
681 * @see org.greenstone.gatherer.gui.BuildOptions
682 * @see org.greenstone.gatherer.shell.GShell
683 * @see org.greenstone.gatherer.shell.GShellListener
684 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
685 * @see org.greenstone.gatherer.util.Utility
686 */
687 public void importCollection() {
688 importing = true;
689 if(!saved()) {
690 Gatherer.println("CollectionManager.importCollection().forcesave");
691 import_monitor.saving();
692 // Force save.
693 try {
694 SaveCollectionTask save_task = new SaveCollectionTask(collection);
695 save_task.setImportAfter(true);
696 save_task.start();
697 }
698 catch(Exception error) {
699 Gatherer.printStackTrace(error);
700 }
701 }
702 else {
703 Gatherer.println("CollectionManager.importCollection()");
704 //check that we can remove the old index before starting import
705 File index_dir = new File(getCollectionIndex(), "temp.txt");
706 index_dir = index_dir.getParentFile();
707 if(index_dir.exists()) {
708 Gatherer.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
709 if (!canDelete(index_dir)) {
710 // tell the user
711 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
712 // tell the gui manager
713 // a message for the building log
714 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
715 Gatherer.g_man.create_pane.message(event);
716 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
717 Gatherer.g_man.create_pane.processComplete(event);
718 importing = false;
719 return;
720 }
721
722 }
723
724 // Remove erroneous file windows file separator as it causes problems when running import.pl
725 String collection_import = getCollectionImport();
726 if(collection_import.length() > 2 && collection_import.endsWith("\\")) {
727 collection_import = collection_import.substring(0, collection_import.length() - 1);
728 }
729 String args[];
730 if(Utility.isWindows()) {
731 args = new String[7];
732 args[0] = Gatherer.config.perl_path;
733 args[1] = "-S";
734 args[2] = Gatherer.config.getScriptPath() + "import.pl";
735 args[3] = "-gli";
736 args[4] = "-importdir";
737 args[5] = collection_import;
738 args[6] = collection.getName();
739 }
740 else {
741 args = new String[5];
742 args[0] = Gatherer.config.getScriptPath() + "import.pl";
743 args[1] = "-gli";
744 args[2] = "-importdir";
745 args[3] = collection_import;
746 args[4] = collection.getName();
747 }
748 collection_import = null;
749 args = ArrayTools.add(args, collection.build_options.getImportValues());
750 GShell shell = new GShell(args, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
751 shell.addGShellListener(Gatherer.g_man.create_pane);
752 shell.start();
753 Gatherer.println("CollectionManager.importCollection().return");
754 }
755 importing = false;
756 }
757
758 /** 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.
759 * @return true if the gli is currently importing
760 */
761 public boolean isImporting() {
762 return importing;
763 }
764
765 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
766 * @param location The path to the collection as a <strong>String</strong>.
767 * @see org.greenstone.gatherer.Configuration
768 * @see org.greenstone.gatherer.Gatherer
769 * @see org.greenstone.gatherer.collection.Collection
770 * @see org.greenstone.gatherer.msm.MetadataSetManager
771 * @see org.greenstone.gatherer.msm.MSMListener
772 * @see org.greenstone.gatherer.util.Utility
773 */
774 public boolean loadCollection(String location) {
775 Gatherer.println("Load Collection '" + location + "'");
776 String[] args2 = new String[1];
777 args2[0] = location;
778 boolean result = false;
779 boolean non_gatherer_collection = false;
780 // Check we have actually been given a .col file.
781 if(!location.endsWith(".col")) {
782 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", args2), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
783 Gatherer.println("CollectionManager.loadCollection: Haven't been given a .col file.");
784 return false;
785 }
786 // Check that there is the collection configuration file available
787
788 File collection_file = new File(location);
789 // Ensure that the directory exists.
790 File collection_directory = collection_file.getParentFile();
791 if(!collection_directory.exists()) {
792 // we cant open this
793 collection_directory = null;
794 return false;
795 }
796
797 // Special case of a user trying to open an old greenstone collection.
798 File metadata_directory = new File(collection_directory, Utility.META_DIR);
799 if(!metadata_directory.exists()) {
800
801 Gatherer.println("CollectionManager.loadCollection: trying to load up a non-gatherer collection");
802 non_gatherer_collection = true;
803 }
804
805 String name = collection_directory.getName();
806 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
807 // Now determine if a lock already exists on this collection.
808 int choice = LockFileDialog.YES_OPTION;
809 if(lock_file.exists()) {
810 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, name, lock_file);
811 choice = dialog.getChoice();
812 dialog.dispose();
813 dialog = null;
814 }
815
816 if(choice != LockFileDialog.YES_OPTION) {
817 // user has cancelled
818 lock_file = null;
819 collection_directory = null;
820 return false;
821 }
822
823
824 try {
825 if(lock_file.exists()) {
826 lock_file.delete();
827 }
828 // Create a lock file.
829 createLockFile(lock_file);
830 // This lock file may not have been created so check
831 if(!lock_file.canWrite()) {
832 // The lock file cannot be written to. Most likely cause incorrect file permissions.
833 String args[] = new String[2];
834 args[0] = args2[0];
835 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Title");
836 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
837 args = null;
838 return false;
839 }
840 // Open the collection file
841 collection = new Collection(collection_file);
842 if(collection.getTitle().equals(StaticStrings.ERROR_STR)) {
843 collection = null;
844 // Remove lock file
845 if(lock_file.exists()) {
846 lock_file.delete();
847 }
848 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config")));
849 }
850
851 collection.msm = new MetadataSetManager();
852 msm = collection.msm; // Legacy
853 collection.msm.load();
854
855 // if non-gatherer collection, need to add some metadata sets
856 if (non_gatherer_collection) {
857 if (!addSomeMetadataSets(collection_directory)) {
858 // for now - return of false means its been cancelled. Any error messages should be sent from the function itself
859 lock_file = null;
860 collection_directory = null;
861 closeCollection();
862 return false;
863 }
864 }
865
866 collection.cdm = new CollectionDesignManager(new File(collection_file.getParent(), Utility.CONFIG_DIR));
867 if (non_gatherer_collection) {
868 // We first recurse the Import folder tree, reading in any metadata.xml files, and then altering the non-namespaced element names to be valid GLI names
869 LegacyCollectionImporter lci = new LegacyCollectionImporter(collection_directory, collection.cdm);
870 lci.importMetadata();
871 lci = null;
872 }
873
874 // Whether the collection is legacy or not, we should now be able to prepared the GDMManager
875 collection.gdm = new GDMManager();
876
877 // Tell everyone that it worked.
878 String[] args = new String[1];
879 args[0] = name;
880 Gatherer.println(Dictionary.get("CollectionManager.Loading_Successful", args));
881 // Now we need to hook up classes that depend on messages from the metadata set manager to keep their content fresh.
882 collection.msm.addMSMListener(this);
883 // We're done. Let everyone know.
884 if(Gatherer.g_man != null) {
885 // workspace_model = null;
886 Gatherer.g_man.collectionChanged(ready());
887 }
888 result = true;
889 ///ystem.err.println("Done loadCollection().");
890 } catch (Exception error) {
891 // There is obviously no existing collection present.
892 Gatherer.printStackTrace(error);
893 if(error.getMessage() != null) {
894 String[] args = new String[2];
895 args[0] = args2[0];
896 args[1] = error.getMessage();
897 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
898 }
899 else {
900 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", args2), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
901 }
902 }
903
904 lock_file = null;
905 collection_directory = null;
906
907 args2 = null;
908 return result;
909 }
910
911 /** 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 shoudl be added back in and finished. [kjdon] */
912 private boolean addSomeMetadataSets(File collection_dir) {
913
914
915 ExternalCollectionPrompt external_prompt = new ExternalCollectionPrompt();
916 int meta_choice = external_prompt.getMetadataChoice();
917 boolean cancelled = external_prompt.isCancelled();
918 if (cancelled) {
919 return false;
920 }
921
922 /*if (meta_choice == ExternalCollectionPrompt.NEW_META_SET) {
923 NewMetaSetPrompt nmsp = new NewMetaSetPrompt();
924 if (nmsp.isCancelled()) {
925 return false;
926}
927 String namespace_str = nmsp.getNamespace();
928 String name_str = nmsp.getName();
929 MetadataSet set = Gatherer.c_man.msm.addSet(namespace_str, name_str);
930 } else if (meta_choice == ExternalCollectionPrompt.EXISTING_META_SET) {
931 */
932 // now we reuse the newcoll metadata prompt for the user to select metadata sets
933 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt(true);
934 if (ncm_prompt.isCancelled()) {
935 return false;
936 }
937 ArrayList metadata_sets = ncm_prompt.getSets();
938 // Import default metadata sets if any.
939 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
940 MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i);
941 collection.msm.importMDS(metadata_set.getFile(), false);
942 }
943 /*} else {
944 return false;
945 }*/
946 // Always import the extracted metadata set
947 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION), false);
948
949 return true;
950 }
951
952 private boolean searchForMetadata(TreeModel collection_tree, FileNode current_node) {
953 File source_file = current_node.getFile();
954 String source_file_name = source_file.getName();
955 if (source_file_name.equals(Utility.METADATA_XML) || source_file_name.equals("CVS")) {
956 return true;
957 }
958 if (collection.msm.searchForMetadata(current_node, current_node, false)== false) {
959 return false;
960 }
961 int num_children = collection_tree.getChildCount(current_node);
962 for (int i=0; i<num_children; i++) {
963 FileNode child = (FileNode)collection_tree.getChild(current_node, i);
964 if (searchForMetadata(collection_tree, child)==false) {
965 return false;
966 }
967 }
968 return true;
969 }
970
971 public void makeCollection(String description, String email, String name, String title) {
972 // Encode the description so it is safe to write to shell
973 if(Utility.isWindows()) {
974 description = Codec.transform(description, Codec.TEXT_TO_SHELL_WINDOWS);
975 }
976 else {
977 description = Codec.transform(description, Codec.TEXT_TO_SHELL_UNIX);
978 }
979 // Run the mkcol command.
980 String command[];
981 if(Utility.isWindows()) {
982 if(description == null || title == null) {
983 command = new String[4];
984 command[0] = Gatherer.config.perl_path;
985 command[1] = "-S";
986 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
987 command[3] = name;
988 }
989 // Users are no longer required to supply an email
990 else if(email == null) {
991 command = new String[8];
992 command[0] = Gatherer.config.perl_path;
993 command[1] = "-S";
994 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
995 command[3] = "-title";
996 command[4] = title;
997 command[5] = "-about";
998 command[6] = description;
999 command[7] = name;
1000 }
1001 else {
1002 command = new String[10];
1003 command[0] = Gatherer.config.perl_path;
1004 command[1] = "-S";
1005 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
1006 command[3] = "-title";
1007 command[4] = title;
1008 command[5] = "-creator";
1009 command[6] = email;
1010 command[7] = "-about";
1011 command[8] = description;
1012 command[9] = name;
1013 }
1014 }
1015 else {
1016 if(description == null || title == null) {
1017 command = new String[2];
1018 command[0] = "mkcol.pl";
1019 command[1] = name;
1020 }
1021 else if(email == null) {
1022 command = new String[6];
1023 command[0] = "mkcol.pl";
1024 command[1] = "-title";
1025 command[2] = title;
1026 command[3] = "-about";
1027 command[4] = description;
1028 command[5] = name;
1029 }
1030 else {
1031 command = new String[8];
1032 command[0] = "mkcol.pl";
1033 command[1] = "-title";
1034 command[2] = title;
1035 command[3] = "-creator";
1036 command[4] = email;
1037 command[5] = "-about";
1038 command[6] = description;
1039 command[7] = name;
1040 }
1041 }
1042 GShell process = new GShell(command, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1043 process.addGShellListener(this);
1044 process.run(); // Don't bother threading this... yet
1045 }
1046
1047 /** 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.
1048 * @param event A <strong>GShellEvent</strong> which contains a the message.
1049 */
1050 public synchronized void message(GShellEvent event) {
1051 }
1052 /** Called whenever the metadata value changes in some way, such as the addition of a new value. We want to mark the collection so that it needs saving again.
1053 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1054 * @see org.greenstone.gatherer.collection.Collection
1055 */
1056 public void metadataChanged(MSMEvent event) {
1057 // Again this change means we need to save the collection again.
1058 collection.setSaved(false);
1059 }
1060 /** This call is fired whenever a process within a GShell created by this class begins.
1061 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1062 * @see org.greenstone.gatherer.Gatherer
1063 * @see org.greenstone.gatherer.gui.GUIManager
1064 * @see org.greenstone.gatherer.shell.GShell
1065 */
1066 public synchronized void processBegun(GShellEvent event) {
1067 Gatherer.println("CollectionManager.processBegun(" + event.getType() + ")");
1068 ///ystem.err.println("ProcessBegun " + event.getType());
1069 // If this is one of the types where we wish to lock user control
1070 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1071 }
1072 /** This call is fired whenever a process within a GShell created by this class ends.
1073 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1074 * @see org.greenstone.gatherer.Gatherer
1075 * @see org.greenstone.gatherer.gui.GUIManager
1076 * @see org.greenstone.gatherer.shell.GShell
1077 */
1078 public synchronized void processComplete(GShellEvent event) {
1079 Gatherer.println("CollectionManager.processComplete(" + event.getType() + ")");
1080 ///ystem.err.println("ProcessComplete " + event.getType());
1081 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1082 ///ystem.err.println("Recieved process complete event - " + event);
1083 // If we were running an import, now run a build.
1084 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1085 // Finish import.
1086 collection.setImported(true);
1087 buildCollection();
1088 }
1089 // If we were running a build, now is when we move files across.
1090 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1091 if(installCollection()) {
1092 // If we have a local library running (that we know about) then we ask it to add our newly create collection
1093 if(Gatherer.config.exec_file != null) {
1094 Gatherer.self.configServer(GSDLSiteConfig.ADD_COMMAND + collection.getName());
1095 }
1096
1097 // Fire a collection changed first to update the preview etc buttons
1098 Gatherer.g_man.collectionChanged(ready());
1099
1100 // Now display a message dialog saying its all built
1101 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", false);
1102 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1103 collection_built_warning_dialog.display();
1104 collection_built_warning_dialog.dispose();
1105 collection_built_warning_dialog = null;
1106 }
1107 else {
1108 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1109 Gatherer.g_man.collectionChanged(ready());
1110 }
1111 }
1112 else if(event.getStatus() == GShell.ERROR || event.getStatus() == GShell.CANCELLED) {
1113 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1114 Gatherer.g_man.collectionChanged(ready());
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 /** Determine if the manager is ready for actions apon its collection.
1119 * @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.
1120 */
1121 static public synchronized boolean ready() {
1122 if(collection != null) {
1123 return true;
1124 }
1125 else {
1126 return false;
1127 }
1128 }
1129
1130 public synchronized boolean reallyReady() {
1131 // If the closing thread is non-null we should only allow that thread to see the collection as closed.
1132 if(closing_thread != null) {
1133 // Only the closing thread sees the truth
1134 if(Thread.currentThread() == closing_thread) {
1135 return (collection == null);
1136 }
1137 // All other threads are told a lie.
1138 else {
1139 return true;
1140 }
1141 }
1142 else {
1143 if(collection != null) {
1144 return true;
1145 }
1146 else {
1147 return false;
1148 }
1149 }
1150 }
1151
1152 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1153 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1154 */
1155 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1156 build_monitor = monitor;
1157 }
1158 /** This method associates the collection copy monitor with the copy monitor created in CreatePane.
1159 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the copy monitor.
1160 */
1161 public void registerCopyMonitor(GShellProgressMonitor monitor) {
1162 copy_monitor = monitor;
1163 }
1164 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1165 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1166 */
1167 public void registerImportMonitor(GShellProgressMonitor monitor) {
1168 import_monitor = monitor;
1169 }
1170 /** Remove a previously assigned special directory mapping.
1171 * @param target The <string>FileNode</strong> representing the special directory mapping to remove as a <strong>String</strong>.
1172 * @return The <strong>File</strong> of the mapping removed.
1173 * @see org.greenstone.gatherer.file.FileNode
1174 */
1175 public File removeDirectoryMapping(FileNode target) {
1176 // Remove from config, remembering file
1177 File file = Gatherer.config.removeDirectoryMapping(target.toString());
1178 // Update tree.
1179 Gatherer.g_man.gather_pane.refreshWorkspaceTree(WorkspaceTree.MAPPED_DIRECTORIES_CHANGED);
1180 return file;
1181 }
1182 /** Used to check whether all open collections have a 'saved' state.
1183 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1184 * @see org.greenstone.gatherer.collection.Collection
1185 */
1186 public boolean saved() {
1187 boolean result = true;
1188 if(collection != null) {
1189 result = collection.getSaved();
1190 }
1191 return result;
1192 }
1193 /** Saves a collection by serializing it to file.
1194 * @param close_after <i>true</i> to cause the Gatherer to close the collection once save is complete, <i>false</i> otherwise.
1195 * @param exit_after <i>true</i> to cause the Gatherer to exit once save is complete, <i>false</i> otherwise.
1196 * @see org.greenstone.gatherer.Gatherer
1197 * @see org.greenstone.gatherer.gui.GUIManager
1198 * @see org.greenstone.gatherer.collection.Collection
1199 */
1200 public void saveCollection(boolean close_after, boolean exit_after) {
1201 Gatherer.println("Save collection: " + collection.getName());
1202 try {
1203 SaveCollectionTask save_task = new SaveCollectionTask(collection, close_after, exit_after);
1204 save_task.start();
1205 // Run this in the same thread
1206 //save_task.run();
1207 }
1208 catch(Exception error) {
1209 Gatherer.printStackTrace(error);
1210 }
1211 }
1212 /** Saves the current collection to a new filename, then restores the original collection. Finally opens the collection copy.
1213 * @param name The name collection name.
1214 */
1215 public void saveCollectionAs(String name) {
1216 // We need to do this in a separate thread so create a SaveCollectionAsTask
1217 try {
1218 SaveCollectionTask save_task = new SaveCollectionTask(collection, name);
1219 save_task.start();
1220 }
1221 catch(Exception error) {
1222 Gatherer.printStackTrace(error);
1223 }
1224 }
1225
1226 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. We want to mark the collection so that it needs saving again.
1227 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1228 * @see org.greenstone.gatherer.collection.Collection
1229 */
1230 public void setChanged(MSMEvent event) {
1231 // Invalidate saved
1232 collection.setSaved(false);
1233 }
1234
1235 public void setClosingThread(boolean set) {
1236 if(set) {
1237 closing_thread = Thread.currentThread();
1238 }
1239 else {
1240 closing_thread = null;
1241 }
1242 }
1243
1244 /** Updates the given workspace tree model to reference the private cache of the currently loaded collection. */
1245 /* private void updatePrivateWorkspace(DefaultTreeModel model) {
1246 // Add Private workspace if a collection has been loaded.
1247 if(ready() && !Gatherer.config.get("workflow.mirror", true)) {
1248 FileNode root = (FileNode)model.getRoot();
1249 // Remove old private workspace
1250 FileNode old = (FileNode)model.getChild(root, 2);
1251 model.removeNodeFromParent(old);
1252 // Create and insert new.
1253 FileNode private_workspace = new FileNode(new File(getCollectionCache()), Dictionary.get("Tree.Private"));
1254 model.insertNodeInto(private_workspace, root, 2);
1255 }
1256 } */
1257 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. We want to mark the collection so that it needs saving again.
1258 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1259 * @see org.greenstone.gatherer.collection.Collection
1260 */
1261 public void valueChanged(MSMEvent event) {
1262 collection.setSaved(false);
1263 }
1264
1265 /** Install collection by moving its files from building to index after a successful build.
1266 * @see org.greenstone.gatherer.Gatherer
1267 * @see org.greenstone.gatherer.util.Utility
1268 */
1269 private boolean installCollection() {
1270 Gatherer.println("Build complete. Moving files.");
1271
1272 try {
1273 // We have to ensure that the local library
1274 if(Gatherer.config.exec_file != null) {
1275 ///ystem.err.println("Local Library Found!");
1276 //Gatherer.g_man./review_pane.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1277 Gatherer.self.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1278 }
1279
1280 File index_dir = new File(getCollectionIndex(), "temp.txt");
1281 index_dir = index_dir.getParentFile();
1282 if(index_dir.exists()) {
1283 Gatherer.println("Index = " + index_dir.getAbsolutePath());
1284 }
1285
1286 File build_dir = new File(getCollectionBuild(), "temp.txt");
1287 build_dir = build_dir.getParentFile();
1288 Gatherer.println("Build = " + build_dir.getAbsolutePath());
1289
1290 // Get the build mode from the build options
1291 String build_mode = collection.build_options.getBuildValue("mode");
1292
1293 // Special case for build mode "all": replace index dir with building dir
1294 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1295 // Remove the old index directory
1296 if (index_dir.exists()) {
1297 Utility.delete(index_dir);
1298 // Check the delete worked
1299 if (index_dir.exists()) {
1300 throw new Exception("Index directory cannot be removed.");
1301 }
1302 }
1303
1304 // Move the building directory to become the new index directory
1305 if (build_dir.renameTo(index_dir) == false) {
1306 throw new Exception("Build directory cannot be moved.");
1307 }
1308
1309 // Create a new building directory
1310 File new_build = new File(getCollectionBuild(), "temp.txt");
1311 new_build = new_build.getParentFile();
1312 new_build.mkdir();
1313 }
1314
1315 // Otherwise copy everything in the building dir into the index dir
1316 else {
1317 File[] build_files = build_dir.listFiles();
1318 for (int i = 0; i < build_files.length; i++) {
1319 File build_file = build_files[i];
1320 File index_equivalent = new File(index_dir, build_file.getName());
1321
1322 // Remove the old file in the index directory (if it exists)
1323 if (index_equivalent.exists()) {
1324 Utility.delete(index_equivalent);
1325 // Check the delete worked
1326 if (index_equivalent.exists()) {
1327 throw new Exception("Index file " + index_equivalent + " cannot be removed.");
1328 }
1329 }
1330
1331 // Move the file into the index directory
1332 if (build_file.renameTo(index_equivalent) == false) {
1333 throw new Exception("File " + build_file + " cannot be moved into index directory.");
1334 }
1335 }
1336 }
1337 }
1338 catch (Exception exception) {
1339 JOptionPane.showMessageDialog(Gatherer.g_man, "Exception detected during collection install.\nMost likely caused by Windows or Local Library holding locks on files:\n" + exception.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
1340 return false;
1341 }
1342 return true;
1343 }
1344
1345 private boolean searchArchivesForMetadata(File archive_directory) {
1346 /** @todo - ensure GreenstoneArchiveParser works as expected. */
1347 return true;
1348 }
1349
1350 /** now this returns true if successful, false if unsuccessful or cancelled */
1351 private boolean searchForMetadata(File current_file) {
1352 boolean success = true;
1353 if(current_file.isFile() && current_file.getName().equals(Utility.METADATA_XML)) {
1354 success = collection.msm.searchForMetadata(null, new FileNode(current_file), false, true); // A dummy run only.
1355 }
1356 else {
1357 File[] children_files = current_file.listFiles();
1358 for(int i = 0; success && children_files != null && i < children_files.length; i++) {
1359 success = searchForMetadata(children_files[i]);
1360 }
1361 }
1362 return success;
1363 }
1364
1365 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title) {
1366 boolean first_name = true;
1367 boolean first_extra = true;
1368 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath();
1369
1370 HashMap mappings = collection.msm.profiler.getActions(collection_path);
1371 if(mappings == null) {
1372 Gatherer.println("Mappings is null, which is odd. Leaving all configuration commands which use metadata as they are.");
1373 }
1374
1375 // 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.
1376 try {
1377 BufferedReader in = new BufferedReader(new FileReader(base_cfg));
1378 BufferedWriter out = new BufferedWriter(new FileWriter(new_cfg, false)); // Overwrite whats there.
1379 String command = null;
1380 while((command = in.readLine()) != null) {
1381 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
1382 while(command.trim().endsWith("\\")) {
1383 command = command.substring(0, command.lastIndexOf("\\"));
1384 String next_line = in.readLine();
1385 if(next_line != null) {
1386 command = command + next_line;
1387 }
1388 }
1389 ///ystem.err.println("Read: " + command);
1390 // Now we've finished parsing a line, determine what to do with it.
1391 String command_lc = command.toLowerCase();
1392 // We replace the creator string with our own.
1393 /*
1394 if(command_lc.startsWith(Utility.CFG_CREATOR)) {
1395 ///ystem.err.println("Found creator - replace with current");
1396 write(out, Utility.CFG_CREATOR + " " + email);
1397 }
1398 else if(command_lc.startsWith(Utility.CFG_MAINTAINER)) {
1399 ///ystem.err.println("Found maintainer - replace with current");
1400 write(out, Utility.CFG_MAINTAINER + " " + email);
1401 }
1402 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONNAME)) {
1403 if(first_name) {
1404 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONNAME + " \"" + title + "\"");
1405 first_name = false;
1406 }
1407 }
1408 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA)) {
1409 if(first_extra) {
1410 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA + " \"" + description + "\"");
1411 first_extra = false;
1412 }
1413 }
1414 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_ICONCOLLECTION)) {
1415 write(out, Utility.CFG_COLLECTIONMETA_ICONCOLLECTION + " \"\"");
1416 }
1417 */
1418 // Just before we try more general parsing there are the special cases to check. These are explicit changes required by some collections to produce sensible results.
1419 if(special_case == SPECIAL_DLS && command_lc.equals("classify hierarchy -hfile azlist.txt -metadata azlist -sort title -buttonname title -hlist_at_top")) {
1420 write(out, "classify AZList -metadata dls.Title -buttonname Title");
1421 }
1422 else if(command_lc.startsWith(Utility.CFG_CLASSIFY)) {
1423 StringTokenizer tokenizer = new StringTokenizer(command);
1424 StringBuffer text = new StringBuffer(tokenizer.nextToken());
1425 // Read in the classifier command watching for hfile, metadata and sort arguments.
1426 String buttonname = null;
1427 String hfile = null;
1428 String new_metadata = null;
1429 String old_metadata = null;
1430 while(tokenizer.hasMoreTokens()) {
1431 String token = tokenizer.nextToken();
1432 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) {
1433 if(tokenizer.hasMoreTokens()) {
1434 text.append(" ");
1435 text.append(token);
1436 token = tokenizer.nextToken();
1437 hfile = token;
1438 }
1439 }
1440 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) {
1441 if(tokenizer.hasMoreTokens()) {
1442 text.append(" ");
1443 text.append(token);
1444 String temp_metadata = tokenizer.nextToken();
1445 String replacement = null;
1446 if(mappings != null) {
1447 replacement = (String) mappings.get(temp_metadata);
1448 }
1449 if(replacement != null) {
1450 token = replacement;
1451 old_metadata = temp_metadata;
1452 new_metadata = replacement;
1453 }
1454 else {
1455 token = temp_metadata;
1456 }
1457 temp_metadata = null;
1458 replacement = null;
1459 }
1460 }
1461 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) {
1462 if(tokenizer.hasMoreTokens()) {
1463 text.append(" ");
1464 text.append(token);
1465 String temp_metadata = tokenizer.nextToken();
1466 String replacement = null;
1467 if(mappings != null) {
1468 replacement = (String) mappings.get(temp_metadata);
1469 }
1470 if(replacement != null) {
1471 token = replacement;
1472 }
1473 else {
1474 token = temp_metadata;
1475 }
1476 temp_metadata = null;
1477 replacement = null;
1478 }
1479 }
1480 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) {
1481 buttonname = token;
1482 }
1483 text.append(' ');
1484 text.append(token);
1485 token = null;
1486 }
1487 tokenizer = null;
1488
1489 // 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)!
1490 if(old_metadata != null && new_metadata != null && buttonname == null) {
1491 text.append(' ');
1492 text.append(Utility.CFG_CLASSIFY_BUTTONNAME);
1493 text.append(' ');
1494 text.append(old_metadata);
1495 }
1496 command = text.toString();
1497 // Replace the hfile if we found it
1498 if(hfile != null && new_metadata != null) {
1499 command = command.replaceAll(hfile, new_metadata + ".txt");
1500 }
1501 buttonname = null;
1502 hfile = null;
1503 new_metadata = null;
1504 old_metadata = null;
1505 write(out, command);
1506 }
1507 else {
1508 // 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.
1509 boolean format_command = command_lc.startsWith(Utility.CFG_FORMAT);
1510 // Replace mapping strings
1511 if(mappings != null) {
1512 for(Iterator keys = mappings.keySet().iterator(); keys.hasNext(); ) {
1513 String target = (String) keys.next();
1514 String replacement = (String) mappings.get(target);
1515 if(format_command) {
1516 target = "\\[" + target + "\\]";
1517 replacement = "{Or}{[" + replacement + "]," + target + "}";
1518 }
1519 command = command.replaceAll(target, replacement);
1520 }
1521 }
1522 write(out, command);
1523 }
1524 }
1525 in.close();
1526 in = null;
1527 out.flush();
1528 out.close();
1529 out = null;
1530 }
1531 catch(Exception error) {
1532 Gatherer.printStackTrace(error);
1533 }
1534 // All done, I hope.
1535 }
1536
1537 private void write(BufferedWriter out, String message)
1538 throws Exception {
1539 ///ystem.err.println("Writing: " + message);
1540 out.write(message, 0, message.length());
1541 out.newLine();
1542 }
1543
1544 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
1545 private class FMTreeModelListener
1546 implements TreeModelListener {
1547 /** 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.
1548 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1549 */
1550 public void treeNodesChanged(TreeModelEvent event) {
1551 if(collection != null) {
1552 collection.setSaved(false);
1553 }
1554 }
1555 /** 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.
1556 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1557 */
1558 public void treeNodesInserted(TreeModelEvent event) {
1559 if(collection != null) {
1560 collection.setSaved(false);
1561 }
1562 }
1563 /** 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.
1564 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1565 */
1566 public void treeNodesRemoved(TreeModelEvent event) {
1567 if(collection != null) {
1568 collection.setSaved(false);
1569 }
1570 }
1571 /** 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.
1572 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1573 */
1574 public void treeStructureChanged(TreeModelEvent event) {
1575 if(collection != null) {
1576 collection.setSaved(false);
1577 }
1578 }
1579 }
1580}
Note: See TracBrowser for help on using the repository browser.