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

Last change on this file since 5893 was 5893, checked in by kjdon, 20 years ago

instead of configuring the server via the preview pane, now we do it via the Gatherer itself

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