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

Last change on this file since 6204 was 6153, checked in by jmt12, 21 years ago

Removed a command which referred to the defunct preview pane

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