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

Last change on this file since 5527 was 5527, checked in by mdewsnip, 21 years ago

Partway through tooltip assignment. Made lots of little changes, registered a whole lot more components, removed dead code, fixed help links (with SimpleMenuBars). Lots more of the same to come.

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