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

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

Made several changes to the way collections are opened - thus to create a more consistant state when things go wrong. For instance opening a collection with a corrupt or missing collect.cfg no longer provides the user with a seemingly working gli bar a title of #ERROR... oh and a nasty trick of crashing when you try to save your past hours worth of work ;P

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