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

Last change on this file since 6061 was 6061, checked in by jmt12, 20 years ago

Classes that I forgot to rewrite to use the new BasicCollectionConfiguration

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