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

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

the updateCollectionCFG method now uses a CommandTokenizer to read in the base coll cfg file - allows collectionmeta and format statements to extend over more than one line

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