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

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

Fix 203B142. Collections that are built outside of GLI will now be recognized as previewable

  • Property svn:keywords set to Author Date Id Revision
File size: 63.1 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.MetaEditPane;
54import org.greenstone.gatherer.gui.NewCollectionMetadataPrompt;
55import org.greenstone.gatherer.msm.ElementWrapper;
56import org.greenstone.gatherer.msm.GDMManager;
57import org.greenstone.gatherer.msm.GreenstoneArchiveParser;
58import org.greenstone.gatherer.msm.MetadataSet;
59import org.greenstone.gatherer.msm.MetadataSetManager;
60import org.greenstone.gatherer.msm.MSMEvent;
61import org.greenstone.gatherer.msm.MSMListener;
62import org.greenstone.gatherer.msm.MSMProfiler;
63import org.greenstone.gatherer.msm.MSMUtils;
64import org.greenstone.gatherer.shell.GShell;
65import org.greenstone.gatherer.shell.GShellEvent;
66import org.greenstone.gatherer.shell.GShellListener;
67import org.greenstone.gatherer.shell.GShellProgressMonitor;
68import org.greenstone.gatherer.undo.UndoManager;
69import org.greenstone.gatherer.util.ArrayTools;
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 ///ystem.err.println("Closing collection...");
191 // Remove the lock on this file, then remove the collection.
192 File lock_file = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + LOCK_FILE);
193 lock_file.delete();
194 collection.msm.destroy();
195 collection = null;
196 collection_model = null;
197 workspace_model = null;
198 undo.clear();
199 Gatherer.config.setCollectionConfiguration(null);
200 Gatherer.g_man.collectionChanged(false);
201 ///ystem.err.println("Closed collection.");
202 // All of the consequences of a close should have been processed by now, so others should now see the collection as non-ready.
203 closing_thread = null;
204 }
205
206 /** Method that is called whenever something has changed in the configuration of this collection. */
207 public void configurationChanged() {
208 if(collection != null) {
209 collection.setSaved(false);
210 }
211 }
212
213 /** 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.
214 * @param description a description of the collection as a String
215 * @param email the email address of the author/maintainer as a String
216 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
217 * @param title the longer title of the collection as a String
218 * @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
219 * @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
220 */
221 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets) {
222 boolean cancelled = false;
223 special_case = NO_SPECIAL_CASE;
224 try {
225 // Create a progress monitor.
226 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, get("Creating_New"), "mkcol.pl", 0, 7);
227 // Create the new collection.
228 makeCollection(description, email, name, title);
229 progress.setProgress(1);
230
231 String a_dir = Utility.getCollectionDir(Gatherer.config.gsdl_path) + name + File.separator;
232
233 // ACTIVE_DIR/gcache/
234 File gcache_dir_temp = new File(Utility.getCacheDir(a_dir)+"temp.dat");
235 File gcache_dir = gcache_dir_temp.getParentFile();
236 gcache_dir.mkdirs();
237 if(progress != null) {
238 progress.setNote(get("Gcache_Created"));
239 }
240
241 // ACTIVE_DIR/log/
242 File log_dir_temp = new File(Utility.getLogDir(a_dir)+"temp.dat");
243 File log_dir = log_dir_temp.getParentFile();
244 log_dir.mkdirs();
245 if(progress != null) {
246 progress.setNote(get("Log_Created"));
247 }
248
249 progress.setProgress(2);
250
251 // Now create the collection object around the directory.
252 collection = new Collection(new File(a_dir, name + ".col"));
253 collection.msm = new MetadataSetManager();
254 msm = collection.msm; // Legacy
255 collection.msm.load();
256 // Always import the extracted metadata set
257 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION), false);
258 // Import default metadata sets if any.
259 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
260 MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i);
261 collection.msm.importMDS(metadata_set.getFile(), false);
262 }
263
264 // Before we create the CollectionDesignManager we have to check if we are basing it upon some other collection.
265 if(base_collection_directory != null) {
266 Gatherer.println("Basing new collection on existing one: " + base_collection_directory);
267 // Try to import any existing metadata sets for this collection. Look in base_collection_directory/metadata and import any metadata sets found.
268 File base_metadata = new File(base_collection_directory, Utility.META_DIR);
269 if(base_metadata.exists()) {
270 Gatherer.println("Found the metadata directory.");
271 File[] possible_metadata_sets = base_metadata.listFiles();
272 for(int i = 0; possible_metadata_sets != null && i < possible_metadata_sets.length; i++) {
273 String filename = possible_metadata_sets[i].getName();
274 if(filename.endsWith(".mds")) {
275 Gatherer.println("+ Found a metadata set. Importing: " + possible_metadata_sets[i].getAbsolutePath());
276 collection.msm.importMDS(possible_metadata_sets[i], false);
277 }
278 }
279 }
280 else {
281 Gatherer.println("This base collection has no metadata directory.");
282 }
283 // 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.
284 boolean skip_import_phase = false;
285 if(collection.msm.getSets().size() == 0) {
286 Gatherer.println("As there are no metadata sets loaded yet, we'll make special checks for the collections available on the GS2.39CD.");
287 CollectionConfiguration col_con = new CollectionConfiguration(new File(base_collection_directory, Utility.CONFIG_DIR));
288 // If the source collection is one of the 'big five' then we know what the metadata set is.
289 String collection_name = col_con.getName();
290 // Demo collection - part of the DLS
291 if(collection_name.equals(Utility.COLLECTION_DEMO) || collection_name.equals(Utility.COLLECTION_DLS)) {
292 Gatherer.println("+ This is a DLS.mds collection.");
293 special_case = SPECIAL_DLS;
294 String demo_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DEMO_DIRECTORY)).getAbsolutePath();
295 String dls_directory = (new File(base_collection_directory.getParentFile(), Utility.COLLECTION_DLS_DIRECTORY)).getAbsolutePath();
296 // Add the dls.mds
297 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.DLS_MDS), false);
298 // Add the mappings for the dls (even if its not present).
299 collection.msm.profiler.addAction(dls_directory, "AZList", "dls.AZList");
300 collection.msm.profiler.addAction(dls_directory, "Keyword", "dls.Keyword");
301 collection.msm.profiler.addAction(dls_directory, "Language", "dls.Language");
302 collection.msm.profiler.addAction(dls_directory, "Organization", "dls.Organization");
303 collection.msm.profiler.addAction(dls_directory, "Subject", "dls.Subject");
304 collection.msm.profiler.addAction(dls_directory, "Title", "dls.Title");
305 // Add the mappings for the demo dls (even if its not present).
306 collection.msm.profiler.addAction(demo_directory, "AZList", "dls.AZList");
307 collection.msm.profiler.addAction(demo_directory, "Keyword", "dls.Keyword");
308 collection.msm.profiler.addAction(demo_directory, "Language", "dls.Language");
309 collection.msm.profiler.addAction(demo_directory, "Organization", "dls.Organization");
310 collection.msm.profiler.addAction(demo_directory, "Subject", "dls.Subject");
311 collection.msm.profiler.addAction(demo_directory, "Title", "dls.Title");
312 // Skip the import phase
313 skip_import_phase = true;
314 }
315 // Prompt the user so that they can choose at least one initial metadata set. We're sneaky here and just create a ncm_prompt
316 else {
317 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.");
318 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt();
319 // If cancelled then they really do mean to start a collection with no metadata sets.
320 if(!ncm_prompt.isCancelled()) {
321 ArrayList initial_sets = ncm_prompt.getSets();
322 for(int i = 0; initial_sets != null && i < initial_sets.size(); i++) {
323 MetadataSet metadata_set = (MetadataSet) initial_sets.get(i);
324 collection.msm.importMDS(metadata_set.getFile(), false);
325 metadata_set = null;
326 }
327 initial_sets = null;
328 }
329 ncm_prompt.dispose();
330 ncm_prompt = null;
331 }
332 }
333 // 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.
334 if(!skip_import_phase) {
335 File base_archive = new File(base_collection_directory, Utility.ARCHIVE_DIR);
336 if(base_archive.exists()) {
337 Gatherer.println("+ Archive directory found. Inspecting archives for metadata information.");
338 ArrayList metadata_elements = GreenstoneArchiveParser.extractMetadataElements(base_archive);
339 for(int i = 0; !cancelled && i < metadata_elements.size(); i++) {
340 String metadata_name = (String) metadata_elements.get(i);
341 ElementWrapper target = collection.msm.prompt.selectElement(metadata_name);
342 cancelled = Gatherer.c_man.getCollection().msm.prompt.wasDialogCancelled();
343 if(!cancelled) {
344 if(target != null) {
345 collection.msm.profiler.addAction(base_collection_directory.getAbsolutePath(), metadata_name, target.getName());
346 }
347 else {
348 collection.msm.profiler.addAction(base_collection_directory.getAbsolutePath(), metadata_name, null);
349 }
350 }
351 }
352 // Hopefully mappings should now be in place for metadata extracted from this collection.
353 }
354 else {
355 Gatherer.println("+ Searching files for metadata.xml information.");
356 // Find the import directory
357 File base_import = new File(base_collection_directory, Utility.IMPORT_DIR);
358 if(base_import.exists()) {
359 searchForMetadata(base_import);
360 }
361 }
362 }
363 // 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.
364
365
366 // Now we update our collect.cfg
367 Gatherer.println("Copy and update collect.cfg from base collection.");
368 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_DIR), new File(a_dir, Utility.CONFIG_DIR), description, email, title);
369 }
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("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 if(workspace_model != null) {
599 return workspace_model;
600 }
601 // Determine the local collection directory if any
602 String current_collection_directory = null;
603 if(collection != null) {
604 current_collection_directory = Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName();
605 }
606 // Create the workspace tree.
607 FileNode workspace_root = new FileNode("ABS_ROOT");
608 workspace_model = new FileSystemModel(workspace_root);
609 // Create and add Greenstone collections node.
610 // Starting at the collection directory of gsdl...
611 FileNode world_root = new FileNode(get("Tree.World"));
612 world_root.unmap();
613 workspace_root.insert(world_root);
614 // Create Local File space.
615 // Get all the available roots mounted on the system.
616 File roots[] = File.listRoots();
617 // If there is just one root use it as the tree root (linux)
618 if(roots != null) {
619 FileNode file_root;
620 String name = get("Tree.Root");
621 if(roots.length == 1) {
622 file_root = new FileNode(roots[0], name);
623 workspace_root.insert(file_root);
624 }
625 // Otherwise build a dummy node which has these nodes as children.
626 else {
627 file_root = new FileNode(name);
628 workspace_root.insert(file_root);
629 // Hopefully this does an alphabetic sort.
630 ArrayTools.sort(roots);
631 for(int i = 0; i < roots.length; i++) {
632 FileNode child_root = new FileNode(roots[i]);
633 file_root.insert(child_root);
634 child_root = null;
635 }
636 }
637 name = null;
638 file_root = null;
639 }
640 // If mirroring is enabled show the public and private caches.
641 if(Gatherer.config.get("workflow.mirror", false)) {
642 // Add Public workspace
643 FileNode public_root = new FileNode(new File(Utility.CACHE_DIR), get("Tree.Public"));
644 workspace_root.insert(public_root);
645 // Add Private workspace if a collection has been loaded.
646 if(ready()) {
647 FileNode private_root = new FileNode(new File(getCollectionCache()), get("Tree.Private"));
648 workspace_root.insert(private_root);
649 }
650 }
651 // Finally we retrieve and map any predefined special directories.
652 if(ready()) {
653 HashMap mappings = collection.getDirectoryMappings();
654 for(Iterator names = mappings.keySet().iterator(); names.hasNext(); ) {
655 String name = (String) names.next();
656 File file = (File) mappings.get(name);
657 FileNode special_root = new FileNode(file, name);
658 //workspace_root.insert(special_root);
659 SynchronizedTreeModelTools.insertNodeInto(workspace_model, workspace_root, special_root);
660 }
661 }
662 return workspace_model;
663 }
664 /** This method when called, creates a new GShell in order to run the import.pl script.
665 * @see org.greenstone.gatherer.Configuration
666 * @see org.greenstone.gatherer.Gatherer
667 * @see org.greenstone.gatherer.Message
668 * @see org.greenstone.gatherer.gui.BuildOptions
669 * @see org.greenstone.gatherer.shell.GShell
670 * @see org.greenstone.gatherer.shell.GShellListener
671 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
672 * @see org.greenstone.gatherer.util.Utility
673 */
674 public void importCollection() {
675 Gatherer.println("CollectionManager.importCollection()");
676 if(!saved()) {
677 import_monitor.saving();
678 // Force save.
679 try {
680 SaveCollectionTask save_task = new SaveCollectionTask(collection);
681 save_task.setImportAfter(true);
682 save_task.start();
683 }
684 catch(Exception error) {
685 Gatherer.printStackTrace(error);
686 }
687 }
688 else {
689 importing = true;
690 // Remove erroneous file windows file separator as it causes problems when running import.pl
691 String collection_import = getCollectionImport();
692 if(collection_import.length() > 2 && collection_import.endsWith("\\")) {
693 collection_import = collection_import.substring(0, collection_import.length() - 1);
694 }
695 String args[];
696 if(Utility.isWindows()) {
697 args = new String[6];
698 args[0] = Gatherer.config.perl_path;
699 args[1] = "-S";
700 args[2] = Gatherer.config.getScriptPath() + "import.pl";
701 args[3] = "-importdir";
702 args[4] = collection_import;
703 args[5] = collection.getName();
704 }
705 else {
706 args = new String[4];
707 args[0] = Gatherer.config.getScriptPath() + "import.pl";
708 args[1] = "-importdir";
709 args[2] = collection_import;
710 args[3] = collection.getName();
711 }
712 collection_import = null;
713 args = ArrayTools.add(args, collection.build_options.getImportValues());
714 GShell shell = new GShell(args, GShell.IMPORT, Message.BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
715 shell.addGShellListener(Gatherer.g_man.create_pane);
716 shell.start();
717 }
718 Gatherer.println("CollectionManager.importCollection().return");
719 }
720 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
721 * @param location The path to the collection as a <strong>String</strong>.
722 * @see org.greenstone.gatherer.Configuration
723 * @see org.greenstone.gatherer.Gatherer
724 * @see org.greenstone.gatherer.Message
725 * @see org.greenstone.gatherer.collection.Collection
726 * @see org.greenstone.gatherer.msm.MetadataSetManager
727 * @see org.greenstone.gatherer.msm.MSMListener
728 * @see org.greenstone.gatherer.util.Utility
729 */
730 public boolean loadCollection(String location) {
731 Gatherer.println("Load Collection '" + location + "'");
732 String[] args2 = new String[1];
733 args2[0] = location;
734 boolean result = false;
735 // Check we have actually been given a .col file.
736 if(location.endsWith(".col")) {
737 File collection_file = new File(location);
738 // Ensure that the directory exists.
739 File collection_directory = collection_file.getParentFile();
740 if(collection_directory.exists()) {
741 // 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
742 File metadata_directory = new File(collection_directory, Utility.META_DIR);
743 if(!metadata_directory.exists()) {
744 JOptionPane.showMessageDialog(Gatherer.g_man, get("CollectionManager.Old_Collection", args2), get("General.Warning"), JOptionPane.INFORMATION_MESSAGE);
745 }
746 // Normal GLI collection
747 else {
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 if(choice == LockFileDialog.YES_OPTION) {
759 try {
760 if(lock_file.exists()) {
761 lock_file.delete();
762 }
763 // Create a lock file.
764 createLockFile(lock_file);
765 // Open the collection file
766 collection = new Collection(collection_file);
767 ///ystem.err.println("In CollectionManager::loadCollection(), collection: " + collection);
768 collection.msm = new MetadataSetManager();
769 msm = collection.msm; // Legacy
770 collection.msm.load();
771 collection.cdm = new CollectionDesignManager(new File(collection_file.getParent(), Utility.CONFIG_DIR));
772 collection.gdm = new GDMManager();
773 // Tell everyone that it worked.
774 Gatherer.println(get("Loading_Successful", name));
775 // Now we need to hook up classes that depend on messages from the metadata set manager to keep their content fresh.
776 collection.msm.addMSMListener(this);
777 // We're done. Let everyone know.
778 if(Gatherer.g_man != null) {
779 workspace_model = null;
780 Gatherer.g_man.collectionChanged(ready());
781 }
782 result = true;
783 ///ystem.err.println("Done loadCollection().");
784 } catch (Exception error) {
785 // There is obviously no existing collection present.
786 Gatherer.printStackTrace(error);
787 JOptionPane.showMessageDialog(Gatherer.g_man, get("Cannot_Open", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE);
788 }
789 }
790 lock_file = null;
791 }
792 }
793 else {
794 JOptionPane.showMessageDialog(Gatherer.g_man, get("CollectionManager.File_Not_Found", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE);
795 }
796 collection_directory = null;
797 }
798 else {
799 JOptionPane.showMessageDialog(Gatherer.g_man, get("Not_Col_File", args2), get("General.Error"), JOptionPane.ERROR_MESSAGE);
800 Gatherer.println("Not a Gatherer Collection.");
801 }
802 args2 = null;
803 return result;
804 }
805
806 public void makeCollection(String description, String email, String name, String title) {
807 // Run the mkcol command.
808 String command[];
809 if(Utility.isWindows()) {
810 if(description == null || email == null || title == null) {
811 command = new String[4];
812 }
813 else {
814 command = new String[10];
815 }
816 command[0] = Gatherer.config.perl_path;
817 command[1] = "-S";
818 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
819 if(description == null || email == null || title == null) {
820 command[3] = name;
821 }
822 else {
823 command[3] = "-title";
824 command[4] = title;
825 command[5] = "-creator";
826 command[6] = email;
827 command[7] = "-about";
828 command[8] = description;
829 command[9] = name;
830 }
831 }
832 else {
833 if(description == null || email == null || title == null) {
834 command = new String[2];
835 command[0] = "mkcol.pl";
836 command[1] = name;
837 }
838 else {
839 command = new String[8];
840 command[0] = "mkcol.pl";
841 command[1] = "-title";
842 command[2] = title;
843 command[3] = "-creator";
844 command[4] = email;
845 command[5] = "-about";
846 command[6] = description;
847 command[7] = name;
848 }
849 }
850 GShell process = new GShell(command, GShell.NEW, Message.COLLECT, this, null, GShell.GSHELL_NEW);
851 process.addGShellListener(this);
852 process.run(); // Don't bother threading this... yet
853 }
854
855 /** 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.
856 * @param event A <strong>GShellEvent</strong> which contains a the message.
857 */
858 public synchronized void message(GShellEvent event) {
859 }
860 /** 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.
861 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
862 * @see org.greenstone.gatherer.collection.Collection
863 */
864 public void metadataChanged(MSMEvent event) {
865 // Again this change means we need to save the collection again.
866 collection.setSaved(false);
867 }
868 /** This call is fired whenever a process within a GShell created by this class begins.
869 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
870 * @see org.greenstone.gatherer.Gatherer
871 * @see org.greenstone.gatherer.gui.GUIManager
872 * @see org.greenstone.gatherer.shell.GShell
873 */
874 public synchronized void processBegun(GShellEvent event) {
875 Gatherer.println("CollectionManager.processBegun(" + event.getType() + ")");
876 ///ystem.err.println("ProcessBegun " + event.getType());
877 // If this is one of the types where we wish to lock user control
878 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
879 }
880 /** This call is fired whenever a process within a GShell created by this class ends.
881 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
882 * @see org.greenstone.gatherer.Gatherer
883 * @see org.greenstone.gatherer.gui.GUIManager
884 * @see org.greenstone.gatherer.shell.GShell
885 */
886 public synchronized void processComplete(GShellEvent event) {
887 Gatherer.println("CollectionManager.processComplete(" + event.getType() + ")");
888 ///ystem.err.println("ProcessComplete " + event.getType());
889 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
890 ///ystem.err.println("Recieved process complete event - " + event);
891 // If we were running an import, now run a build.
892 if(event.getType() == GShell.IMPORT && event.getStatus() != GShell.ERROR) {
893 // Finish import.
894 collection.setImported(true);
895 buildCollection();
896 }
897 // If we were running a build, now is when we move files across.
898 else if(event.getType() == GShell.BUILD && event.getStatus() != GShell.ERROR) {
899 ///ystem.err.println("Installing collection.");
900 installCollection();
901 collection.setBuilt(true);
902 // If we have a local library running (that we know about) then we ask it to add our newly create collection
903 ///ystem.err.println("Check if we should reset local server.");
904 if(Gatherer.config.exec_file != null) {
905 ///ystem.err.println("Local Library Found!");
906 Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.ADD_COMMAND + collection.getName());
907 }
908 //else {
909 ///ystem.err.println("GLI can't recognize a local library.");
910 //}
911 // Signal collection changed.
912 workspace_model = null;
913 Gatherer.g_man.collectionChanged(ready());
914 JOptionPane.showMessageDialog(Gatherer.g_man, get("Preview_Ready"), get("Preview_Ready_Title"), JOptionPane.INFORMATION_MESSAGE);
915 }
916 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.ERROR) {
917 JOptionPane.showMessageDialog(Gatherer.g_man, get("Preview_Ready_Failed"), get("Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
918 }
919 }
920 /** Determine if the manager is ready for actions apon its collection.
921 * @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.
922 */
923
924 public synchronized boolean ready() {
925 if(collection != null) {
926 return true;
927 }
928 else {
929 return false;
930 }
931 }
932
933 public synchronized boolean reallyReady() {
934 // If the closing thread is non-null we should only allow that thread to see the collection as closed.
935 if(closing_thread != null) {
936 // Only the closing thread sees the truth
937 if(Thread.currentThread() == closing_thread) {
938 return (collection == null);
939 }
940 // All other threads are told a lie.
941 else {
942 return true;
943 }
944 }
945 else {
946 if(collection != null) {
947 return true;
948 }
949 else {
950 return false;
951 }
952 }
953 }
954
955 /** Refresh the Greenstone Collections special mapping in the workspace tree to account for collections being added/removed
956 */
957 public void refreshGreenstoneCollections() {
958 FileNode root = (FileNode) workspace_model.getRoot();
959 FileNode greenstone_collections_node = null;
960 int root_child_count = root.getChildCount();
961 String greenstone_collections_str = get("Tree.World");
962 for(int i = 0; greenstone_collections_node == null && i < root_child_count; i++) {
963 TreeNode child = root.getChildAt(i);
964 if(child.toString().equals(greenstone_collections_str)) {
965 greenstone_collections_node = (FileNode) child;
966 }
967 child = null;
968 }
969 greenstone_collections_str = null;
970 root = null;
971 if(greenstone_collections_node != null) {
972 TreePath greenstone_collections_path = new TreePath(greenstone_collections_node.getPath());
973 workspace_model.refresh(greenstone_collections_path);
974 greenstone_collections_path = null;
975 greenstone_collections_node = null;
976 }
977 }
978
979 /** Called to refresh the models upon which the trees are based.
980 * @see org.greenstone.gatherer.collection.Collection
981 */
982 public void refreshTrees() {
983 }
984 /** This method associates the collection build monitor with the build monitor created in CreatePane.
985 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
986 */
987 public void registerBuildMonitor(GShellProgressMonitor monitor) {
988 build_monitor = monitor;
989 }
990 /** This method associates the collection copy monitor with the copy monitor created in CreatePane.
991 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the copy monitor.
992 */
993 public void registerCopyMonitor(GShellProgressMonitor monitor) {
994 copy_monitor = monitor;
995 }
996 /** This method associates the collection import monitor with the import monitor created in CreatePane.
997 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
998 */
999 public void registerImportMonitor(GShellProgressMonitor monitor) {
1000 import_monitor = monitor;
1001 }
1002 /** Remove a previously assigned special directory mapping.
1003 * @param name The symbolic name of the special directory mapping to remove as a <strong>String</strong>.
1004 * @return The <strong>File</strong> of the mapping removed.
1005 */
1006 public File removeDirectoryMapping(FileNode target) {
1007 File file = null;
1008 if(ready()) {
1009 // Remove from collection, remembering file
1010 file = collection.removeDirectoryMapping(target.toString());
1011 // Update tree.
1012 FileSystemModel model = (FileSystemModel) Gatherer.g_man.collection_pane.getWorkspaceTree().getModel();
1013 SynchronizedTreeModelTools.removeNodeFromParent(model, target);
1014 }
1015 return file;
1016 }
1017 /** Used to check whether all open collections have a 'saved' state.
1018 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1019 * @see org.greenstone.gatherer.collection.Collection
1020 */
1021 public boolean saved() {
1022 boolean result = true;
1023 if(collection != null) {
1024 result = collection.getSaved();
1025 }
1026 return result;
1027 }
1028 /** Saves a collection by serializing it to file.
1029 * @param close_after <i>true</i> to cause the Gatherer to close the collection once save is complete, <i>false</i> otherwise.
1030 * @param exit_after <i>true</i> to cause the Gatherer to exit once save is complete, <i>false</i> otherwise.
1031 * @see org.greenstone.gatherer.Gatherer
1032 * @see org.greenstone.gatherer.Message
1033 * @see org.greenstone.gatherer.gui.GUIManager
1034 * @see org.greenstone.gatherer.gui.GConfigPane
1035 * @see org.greenstone.gatherer.collection.Collection
1036 */
1037 public void saveCollection(boolean close_after, boolean exit_after) {
1038 Gatherer.println("Save collection: " + collection.getName());
1039 try {
1040 SaveCollectionTask save_task = new SaveCollectionTask(collection, close_after, exit_after);
1041 save_task.start();
1042 // Run this in the same thread
1043 //save_task.run();
1044 }
1045 catch(Exception error) {
1046 Gatherer.printStackTrace(error);
1047 }
1048 }
1049 /** Saves the current collection to a new filename, then restores the original collection. Finally opens the collection copy.
1050 * @param name The name collection name.
1051 */
1052 public void saveCollectionAs(String name) {
1053 // We need to do this in a separate thread so create a SaveCollectionAsTask
1054 try {
1055 SaveCollectionTask save_task = new SaveCollectionTask(collection, name);
1056 save_task.start();
1057 }
1058 catch(Exception error) {
1059 Gatherer.printStackTrace(error);
1060 }
1061 }
1062
1063 /** 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.
1064 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1065 * @see org.greenstone.gatherer.collection.Collection
1066 */
1067 public void setChanged(MSMEvent event) {
1068 // Invalidate saved
1069 collection.setSaved(false);
1070 }
1071
1072 /** Updates the given workspace tree model to reference the private cache of the currently loaded collection. */
1073 public void updatePrivateWorkspace(DefaultTreeModel model) {
1074 // Add Private workspace if a collection has been loaded.
1075 if(ready() && !Gatherer.config.get("workflow.mirror", true)) {
1076 FileNode root = (FileNode)model.getRoot();
1077 // Remove old private workspace
1078 FileNode old = (FileNode)model.getChild(root, 2);
1079 model.removeNodeFromParent(old);
1080 // Create and insert new.
1081 FileNode private_workspace = new FileNode(new File(getCollectionCache()), get("Tree.Private"));
1082 model.insertNodeInto(private_workspace, root, 2);
1083 }
1084 }
1085 /** 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.
1086 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1087 * @see org.greenstone.gatherer.collection.Collection
1088 */
1089 public void valueChanged(MSMEvent event) {
1090 collection.setSaved(false);
1091 }
1092 /** Used to retrive a value from the dictionary based on the key.
1093 * @param key A <strong>String</strong> indicating what value to retrieve.
1094 * @return A <strong>String</strong> representing the value.
1095 */
1096 private String get(String key) {
1097 return get(key, (String[])null);
1098 }
1099
1100 /** Used to retrive a value from the dictionary based on the key. */
1101 private String get(String key, String arg) {
1102 String[] args = new String[1];
1103 args[0] = arg;
1104 return get(key, args);
1105 }
1106
1107 /** Used to retrive a value from the dictionary based on the key, and an array of arguments.
1108 * @param key A <strong>String</strong> indicating what value to retrieve.
1109 * @param args A <strong>String[]</strong> of arguments to be inserted into the phrase.
1110 * @return A <strong>String</strong> representing the value.
1111 */
1112 private String get(String key, String args[]) {
1113 if(key.indexOf('.') == -1) {
1114 key = "CollectionManager." + key;
1115 }
1116 return Gatherer.dictionary.get(key, args);
1117 }
1118 /** Install collection by moving its files from building to index after a successful build.
1119 * @see org.greenstone.gatherer.Gatherer
1120 * @see org.greenstone.gatherer.util.Utility
1121 */
1122 private void installCollection() {
1123 Gatherer.println("Build complete. Moving files.");
1124
1125
1126 try {
1127 // We have to ensure that the local library
1128 if(Gatherer.config.exec_file != null) {
1129 ///ystem.err.println("Local Library Found!");
1130 Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1131 }
1132
1133 File index_dir = new File(getCollectionIndex(), "temp.txt");
1134 index_dir = index_dir.getParentFile();
1135 Gatherer.println("Index = " + index_dir.getAbsolutePath());
1136
1137 File build_dir = new File(getCollectionBuild(), "temp.txt");
1138 build_dir = build_dir.getParentFile();
1139 Gatherer.println("Build = " + build_dir.getAbsolutePath());
1140
1141 // Get the build mode from the build options
1142 String build_mode = collection.build_options.getBuildValue("mode");
1143
1144 // Special case for build mode "all": replace index dir with building dir
1145 if (build_mode == null || build_mode.equals(get("CreatePane.Mode_All"))) {
1146 // Remove the old index directory
1147 if (index_dir.exists()) {
1148 Utility.delete(index_dir);
1149 // Check the delete worked
1150 if (index_dir.exists()) {
1151 throw new Exception("Index directory cannot be removed.");
1152 }
1153 }
1154
1155 // Move the building directory to become the new index directory
1156 if (build_dir.renameTo(index_dir) == false) {
1157 throw new Exception("Build directory cannot be moved.");
1158 }
1159
1160 // Create a new building directory
1161 File new_build = new File(getCollectionBuild(), "temp.txt");
1162 new_build = new_build.getParentFile();
1163 new_build.mkdir();
1164 }
1165
1166 // Otherwise copy everything in the building dir into the index dir
1167 else {
1168 File[] build_files = build_dir.listFiles();
1169 for (int i = 0; i < build_files.length; i++) {
1170 File build_file = build_files[i];
1171 File index_equivalent = new File(index_dir, build_file.getName());
1172
1173 // Remove the old file in the index directory (if it exists)
1174 if (index_equivalent.exists()) {
1175 Utility.delete(index_equivalent);
1176 // Check the delete worked
1177 if (index_equivalent.exists()) {
1178 throw new Exception("Index file " + index_equivalent + " cannot be removed.");
1179 }
1180 }
1181
1182 // Move the file into the index directory
1183 if (build_file.renameTo(index_equivalent) == false) {
1184 throw new Exception("File " + build_file + " cannot be moved into index directory.");
1185 }
1186 }
1187 }
1188 }
1189 catch (Exception exception) {
1190 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);
1191 }
1192 }
1193 /** Creates and dispatches a message given the initial details.
1194 * @param level An <i>int</i> indicating the message level for this message.
1195 * @param message A <strong>String</strong> which contains the payload of this message.
1196 * @see org.greenstone.gatherer.Log
1197 * @see org.greenstone.gatherer.Message
1198 */
1199 private void message(int level, String message) {
1200 Message msg = new Message(Message.COLLECT, level, message);
1201 if(Gatherer.g_man != null) {
1202 Gatherer.log.add(msg);
1203 } else {
1204 Gatherer.println(msg.toString());
1205 }
1206 }
1207
1208 private boolean searchArchivesForMetadata(File archive_directory) {
1209 /** @todo - ensure GreenstoneArchiveParser works as expected. */
1210 return true;
1211 }
1212
1213 private boolean searchForMetadata(File current_file) {
1214 boolean cancelled = false;
1215 if(current_file.isFile() && current_file.getName().equals(Utility.METADATA_XML)) {
1216 cancelled = collection.msm.searchForMetadata(null, new FileNode(current_file), false, true); // A dummy run only.
1217 }
1218 else {
1219 File[] children_files = current_file.listFiles();
1220 for(int i = 0; !cancelled && children_files != null && i < children_files.length; i++) {
1221 cancelled = searchForMetadata(children_files[i]);
1222 }
1223 }
1224 return cancelled;
1225 }
1226
1227 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title) {
1228 boolean first_name = true;
1229 boolean first_extra = true;
1230 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath();
1231
1232 HashMap mappings = collection.msm.profiler.getActions(collection_path);
1233 if(mappings == null) {
1234 Gatherer.println("Mappings is null, which is odd. Leaving all configuration commands which use metadata as they are.");
1235 }
1236
1237 // 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.
1238 try {
1239 BufferedReader in = new BufferedReader(new FileReader(base_cfg));
1240 BufferedWriter out = new BufferedWriter(new FileWriter(new_cfg, false)); // Overwrite whats there.
1241 String command = null;
1242 while((command = in.readLine()) != null) {
1243 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
1244 while(command.trim().endsWith("\\")) {
1245 command = command.substring(0, command.lastIndexOf("\\"));
1246 String next_line = in.readLine();
1247 if(next_line != null) {
1248 command = command + next_line;
1249 }
1250 }
1251 ///ystem.err.println("Read: " + command);
1252 // Now we've finished parsing a line, determine what to do with it.
1253 String command_lc = command.toLowerCase();
1254 // We replace the creator string with our own.
1255 if(command_lc.startsWith(Utility.CFG_CREATOR)) {
1256 write(out, Utility.CFG_CREATOR + " " + email);
1257 }
1258 else if(command_lc.startsWith(Utility.CFG_MAINTAINER)) {
1259 write(out, Utility.CFG_MAINTAINER + " " + email);
1260 }
1261 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONNAME)) {
1262 if(first_name) {
1263 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONNAME + " \"" + title + "\"");
1264 first_name = false;
1265 }
1266 }
1267 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA)) {
1268 if(first_extra) {
1269 write(out, Utility.CFG_COLLECTIONMETA_COLLECTIONEXTRA + " \"" + description + "\"");
1270 first_extra = false;
1271 }
1272 }
1273 else if(command_lc.startsWith(Utility.CFG_COLLECTIONMETA_ICONCOLLECTION)) {
1274 write(out, Utility.CFG_COLLECTIONMETA_ICONCOLLECTION + " \"\"");
1275 }
1276
1277 // 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.
1278 else if(special_case == SPECIAL_DLS && command_lc.equals("classify hierarchy -hfile azlist.txt -metadata azlist -sort title -buttonname title -hlist_at_top")) {
1279 write(out, "classify AZList -metadata dls.Title -buttonname Title");
1280 }
1281 else if(command_lc.startsWith(Utility.CFG_CLASSIFY)) {
1282 StringTokenizer tokenizer = new StringTokenizer(command);
1283 StringBuffer text = new StringBuffer(tokenizer.nextToken());
1284 // Read in the classifier command watching for hfile, metadata and sort arguments.
1285 String buttonname = null;
1286 String hfile = null;
1287 String new_metadata = null;
1288 String old_metadata = null;
1289 while(tokenizer.hasMoreTokens()) {
1290 String token = tokenizer.nextToken();
1291 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) {
1292 if(tokenizer.hasMoreTokens()) {
1293 text.append(" ");
1294 text.append(token);
1295 token = tokenizer.nextToken();
1296 hfile = token;
1297 }
1298 }
1299 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) {
1300 if(tokenizer.hasMoreTokens()) {
1301 text.append(" ");
1302 text.append(token);
1303 String temp_metadata = tokenizer.nextToken();
1304 String replacement = null;
1305 if(mappings != null) {
1306 replacement = (String) mappings.get(temp_metadata);
1307 }
1308 if(replacement != null) {
1309 token = replacement;
1310 old_metadata = temp_metadata;
1311 new_metadata = replacement;
1312 }
1313 else {
1314 token = temp_metadata;
1315 }
1316 temp_metadata = null;
1317 replacement = null;
1318 }
1319 }
1320 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) {
1321 if(tokenizer.hasMoreTokens()) {
1322 text.append(" ");
1323 text.append(token);
1324 String temp_metadata = tokenizer.nextToken();
1325 String replacement = null;
1326 if(mappings != null) {
1327 replacement = (String) mappings.get(temp_metadata);
1328 }
1329 if(replacement != null) {
1330 token = replacement;
1331 }
1332 else {
1333 token = temp_metadata;
1334 }
1335 temp_metadata = null;
1336 replacement = null;
1337 }
1338 }
1339 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) {
1340 buttonname = token;
1341 }
1342 text.append(' ');
1343 text.append(token);
1344 token = null;
1345 }
1346 tokenizer = null;
1347
1348 // 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)!
1349 if(old_metadata != null && new_metadata != null && buttonname == null) {
1350 text.append(' ');
1351 text.append(Utility.CFG_CLASSIFY_BUTTONNAME);
1352 text.append(' ');
1353 text.append(old_metadata);
1354 }
1355 command = text.toString();
1356 // Replace the hfile if we found it
1357 if(hfile != null && new_metadata != null) {
1358 command = command.replaceAll(hfile, new_metadata + ".txt");
1359 }
1360 buttonname = null;
1361 hfile = null;
1362 new_metadata = null;
1363 old_metadata = null;
1364 write(out, command);
1365 }
1366 else {
1367 // 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.
1368 boolean format_command = command_lc.startsWith(Utility.CFG_FORMAT);
1369 // Replace mapping strings
1370 if(mappings != null) {
1371 for(Iterator keys = mappings.keySet().iterator(); keys.hasNext(); ) {
1372 String target = (String) keys.next();
1373 String replacement = (String) mappings.get(target);
1374 if(format_command) {
1375 target = "\\[" + target + "\\]";
1376 replacement = "{Or}{[" + replacement + "]," + target + "}";
1377 }
1378 command = command.replaceAll(target, replacement);
1379 }
1380 }
1381 write(out, command);
1382 }
1383 }
1384 in.close();
1385 in = null;
1386 out.flush();
1387 out.close();
1388 out = null;
1389 }
1390 catch(Exception error) {
1391 Gatherer.printStackTrace(error);
1392 }
1393 // All done, I hope.
1394 }
1395
1396 private void write(BufferedWriter out, String message)
1397 throws Exception {
1398 ///ystem.err.println("Writing: " + message);
1399 out.write(message, 0, message.length());
1400 out.newLine();
1401 }
1402
1403 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
1404 private class FMTreeModelListener
1405 implements TreeModelListener {
1406 /** 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.
1407 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1408 */
1409 public void treeNodesChanged(TreeModelEvent event) {
1410 if(collection != null) {
1411 collection.setSaved(false);
1412 }
1413 }
1414 /** 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.
1415 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1416 */
1417 public void treeNodesInserted(TreeModelEvent event) {
1418 if(collection != null) {
1419 collection.setSaved(false);
1420 }
1421 }
1422 /** 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.
1423 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1424 */
1425 public void treeNodesRemoved(TreeModelEvent event) {
1426 if(collection != null) {
1427 collection.setSaved(false);
1428 }
1429 }
1430 /** 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.
1431 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1432 */
1433 public void treeStructureChanged(TreeModelEvent event) {
1434 if(collection != null) {
1435 collection.setSaved(false);
1436 }
1437 }
1438 }
1439}
Note: See TracBrowser for help on using the repository browser.