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

Last change on this file since 7166 was 7166, checked in by mdewsnip, 20 years ago

Added a null pointer check.

  • Property svn:keywords set to Author Date Id Revision
File size: 66.9 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.collection.BasicCollectionConfiguration;
52import org.greenstone.gatherer.collection.Collection;
53import org.greenstone.gatherer.collection.SaveCollectionTask;
54import org.greenstone.gatherer.file.FileNode;
55import org.greenstone.gatherer.file.FileSystemModel;
56import org.greenstone.gatherer.gui.LockFileDialog;
57import org.greenstone.gatherer.gui.NewCollectionMetadataPrompt;
58import org.greenstone.gatherer.gui.ExternalCollectionPrompt;
59import org.greenstone.gatherer.gui.NewMetaSetPrompt;
60import org.greenstone.gatherer.gui.WarningDialog;
61import org.greenstone.gatherer.gui.tree.WorkspaceTree;
62import org.greenstone.gatherer.msm.ElementWrapper;
63import org.greenstone.gatherer.msm.MetadataXMLFileManager;
64import org.greenstone.gatherer.msm.GreenstoneArchiveParser;
65import org.greenstone.gatherer.msm.LegacyCollectionImporter;
66import org.greenstone.gatherer.msm.MetadataSet;
67import org.greenstone.gatherer.msm.MetadataSetManager;
68import org.greenstone.gatherer.msm.MSMEvent;
69import org.greenstone.gatherer.msm.MSMListener;
70import org.greenstone.gatherer.msm.MSMProfiler;
71import org.greenstone.gatherer.msm.MSMUtils;
72import org.greenstone.gatherer.shell.GShell;
73import org.greenstone.gatherer.shell.GShellEvent;
74import org.greenstone.gatherer.shell.GShellListener;
75import org.greenstone.gatherer.shell.GShellProgressMonitor;
76import org.greenstone.gatherer.undo.UndoManager;
77import org.greenstone.gatherer.util.ArrayTools;
78import org.greenstone.gatherer.util.Codec;
79import org.greenstone.gatherer.util.GSDLSiteConfig;
80import org.greenstone.gatherer.util.StaticStrings;
81import org.greenstone.gatherer.util.SynchronizedTreeModelTools;
82import org.greenstone.gatherer.util.Utility;
83import org.w3c.dom.*;
84
85/** 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.
86 * @author John Thompson
87 * @version 2.3
88 */
89public class CollectionManager
90 implements GShellListener, MSMListener {
91 /** A reference to the metadata set manager. */
92 public MetadataSetManager msm;
93 /** A reference to the undo manager. Although only one instance is shared between all collections, the undo queues are emptied between each. */
94 public UndoManager undo;
95 /** Are we currently in the process of building? */
96 private boolean building = false;
97 /** Are we currently in the process of importing? */
98 private boolean importing = false;
99 /** The collection this manager is managing! */
100 static private Collection collection = null;
101 /** The collection_model. */
102 private FileSystemModel collection_model = null;
103 /** The workspace model. This becomes invalid on a collection change. */
104 // private FileSystemModel workspace_model = null;
105 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
106 private FMTreeModelListener fm_tree_model_listener = null;
107 /** The monitor resposible for parsing the build process. */
108 private GShellProgressMonitor build_monitor = null;
109 /** The monitor resposible for parsing the import process. */
110 private GShellProgressMonitor import_monitor = null;
111
112 /** 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. */
113 private Thread closing_thread = null;
114
115 /** The name of the standard lock file. */
116 static final public String LOCK_FILE = "gli.lck";
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 // Update the information stored in the Gatherer config
137 Gatherer.config.addDirectoryMapping(name, file);
138 // Now update the tree
139 Gatherer.g_man.gather_pane.refreshWorkspaceTree(WorkspaceTree.MAPPED_DIRECTORIES_CHANGED);
140 }
141 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
142 * @see org.greenstone.gatherer.Configuration
143 * @see org.greenstone.gatherer.Gatherer
144 * @see org.greenstone.gatherer.collection.Collection
145 * @see org.greenstone.gatherer.gui.BuildOptions
146 * @see org.greenstone.gatherer.shell.GShell
147 * @see org.greenstone.gatherer.shell.GShellListener
148 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
149 * @see org.greenstone.gatherer.util.Utility
150 */
151 public void buildCollection() {
152 Gatherer.println("CollectionManager.buildCollection()");
153 building = true;
154 String lang = Gatherer.config.getLanguage();
155 String args[];
156 if(Utility.isWindows()) {
157 args = new String[7];
158 args[0] = Gatherer.config.perl_path;
159 args[1] = "-S";
160 args[2] = Gatherer.config.getScriptPath() + "buildcol.pl";
161 args[3] = "-gli";
162 args[4] = "-language";
163 args[5] = lang;
164 args[6] = collection.getName();
165 }
166 else {
167 args = new String[5];
168 args[0] = Gatherer.config.getScriptPath() + "buildcol.pl";
169 args[1] = "-gli";
170 args[2] = "-language";
171 args[3] = lang;
172 args[4] = collection.getName();
173 }
174 args = ArrayTools.add(args, collection.build_options.getBuildValues());
175 GShell shell = new GShell(args, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
176 shell.addGShellListener(Gatherer.g_man.create_pane);
177 shell.start();
178 Gatherer.println("CollectionManager.buildCollection().return");
179 }
180
181 /** Used to determine whether the currently active collection has been built.
182 * @return A boolean indicating the built status of the collection.
183 */
184 public boolean built() {
185 if(collection != null) {
186 // Determine if the collection has been built by looking for the build.cfg file
187 File build_cfg_file = new File(getCollectionIndex() + Utility.BUILD_CFG_FILENAME);
188 return build_cfg_file.exists();
189 }
190 return false;
191 }
192
193 /** 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 */
194 public boolean canDelete(File file) {
195 if (!file.isDirectory()) {
196 return file.canWrite();
197 }
198 File [] file_list = file.listFiles();
199 for (int i=0; i<file_list.length; i++) {
200 if (!canDelete(file_list[i])) {
201 return false;
202 }
203 }
204 return true;
205 }
206 /** Called to close the current collection and remove its lock file.
207 * @see org.greenstone.gatherer.Gatherer
208 * @see org.greenstone.gatherer.collection.Collection
209 * @see org.greenstone.gatherer.util.Utility
210 */
211 public void closeCollection() {
212 Gatherer.println("Close collection: " + collection.getName());
213 // 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.
214 closing_thread = Thread.currentThread();
215 // Remove the lock on this file, then remove the collection.
216 File lock_file = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path) + collection.getName() + File.separator + LOCK_FILE);
217 lock_file.delete();
218 if(lock_file.exists()) {
219 System.err.println("Lockfile was not successfully deleted.");
220 }
221 collection.msm.destroy();
222 collection = null;
223 collection_model = null;
224 // workspace_model = null;
225 undo.clear();
226 Gatherer.config.setCollectionConfiguration(null);
227 if (Gatherer.g_man != null) {
228 Gatherer.g_man.collectionChanged(false);
229 }
230 // All of the consequences of a close should have been processed by now, so others should now see the collection as non-ready.
231 closing_thread = null;
232 }
233
234 /** Method that is called whenever something has changed in the configuration of this collection. */
235 public void configurationChanged() {
236 if(collection != null) {
237 collection.setSaved(false);
238 }
239 }
240
241 /** 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.
242 * @param description a description of the collection as a String
243 * @param email the email address of the author/maintainer as a String
244 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
245 * @param title the longer title of the collection as a String
246 * @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
247 * @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
248 */
249 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets) {
250 boolean cancelled = false;
251
252 try {
253 // Create a progress monitor.
254 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, Dictionary.get("CollectionManager.Creating_New"), "mkcol.pl", 0, 7);
255 // Create the new collection.
256 makeCollection(description, email, name, title);
257 progress.setProgress(1);
258
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.importMetadata();
847 lci.updateClassifiers();
848 lci = null;
849 }
850
851 // Whether the collection is legacy or not, we should now be able to prepare the MetadataXMLFileManager
852 collection.gdm = new MetadataXMLFileManager();
853
854 // Tell everyone that it worked.
855 String[] args = new String[1];
856 args[0] = name;
857 Gatherer.println(Dictionary.get("CollectionManager.Loading_Successful", args));
858 // Now we need to hook up classes that depend on messages from the metadata set manager to keep their content fresh.
859 collection.msm.addMSMListener(this);
860 // We're done. Let everyone know.
861 if(Gatherer.g_man != null) {
862 // workspace_model = null;
863 Gatherer.g_man.collectionChanged(ready());
864 }
865 result = true;
866 ///ystem.err.println("Done loadCollection().");
867 } catch (Exception error) {
868 System.err.println("Exception occurred!");
869 error.printStackTrace();
870 // There is obviously no existing collection present.
871 Gatherer.printStackTrace(error);
872 if(error.getMessage() != null) {
873 String[] args = new String[2];
874 args[0] = args2[0];
875 args[1] = error.getMessage();
876 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
877 }
878 else {
879 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", args2), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
880 }
881 }
882
883 lock_file = null;
884 collection_directory = null;
885
886 args2 = null;
887 return result;
888 }
889
890 /** 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] */
891 private boolean addSomeMetadataSets(File collection_dir) {
892
893
894 ExternalCollectionPrompt external_prompt = new ExternalCollectionPrompt();
895 int meta_choice = external_prompt.getMetadataChoice();
896 boolean cancelled = external_prompt.isCancelled();
897 if (cancelled) {
898 return false;
899 }
900
901 /*if (meta_choice == ExternalCollectionPrompt.NEW_META_SET) {
902 NewMetaSetPrompt nmsp = new NewMetaSetPrompt();
903 if (nmsp.isCancelled()) {
904 return false;
905}
906 String namespace_str = nmsp.getNamespace();
907 String name_str = nmsp.getName();
908 MetadataSet set = Gatherer.c_man.msm.addSet(namespace_str, name_str);
909 } else if (meta_choice == ExternalCollectionPrompt.EXISTING_META_SET) {
910 */
911 // now we reuse the newcoll metadata prompt for the user to select metadata sets
912 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt(true);
913 if (ncm_prompt.isCancelled()) {
914 return false;
915 }
916 ArrayList metadata_sets = ncm_prompt.getSets();
917 // Import default metadata sets if any.
918 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
919 MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i);
920 collection.msm.importMDS(metadata_set.getFile(), false);
921 }
922 /*} else {
923 return false;
924 }*/
925 // Always import the extracted metadata set
926 collection.msm.importMDS(new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION), false);
927
928 return true;
929 }
930
931 private boolean searchForMetadata(TreeModel collection_tree, FileNode current_node) {
932 File source_file = current_node.getFile();
933 String source_file_name = source_file.getName();
934 if (source_file_name.equals(Utility.METADATA_XML) || source_file_name.equals("CVS")) {
935 return true;
936 }
937 if (collection.msm.searchForMetadata(current_node, current_node, false)== false) {
938 return false;
939 }
940 int num_children = collection_tree.getChildCount(current_node);
941 for (int i=0; i<num_children; i++) {
942 FileNode child = (FileNode)collection_tree.getChild(current_node, i);
943 if (searchForMetadata(collection_tree, child)==false) {
944 return false;
945 }
946 }
947 return true;
948 }
949
950 public void makeCollection(String description, String email, String name, String title) {
951 // Encode the description so it is safe to write to shell
952 if(Utility.isWindows()) {
953 description = Codec.transform(description, Codec.TEXT_TO_SHELL_WINDOWS);
954 }
955 else {
956 description = Codec.transform(description, Codec.TEXT_TO_SHELL_UNIX);
957 }
958 // Run the mkcol command.
959 String command[];
960 if(Utility.isWindows()) {
961 if(description == null || title == null) {
962 command = new String[4];
963 command[0] = Gatherer.config.perl_path;
964 command[1] = "-S";
965 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
966 command[3] = name;
967 }
968 // Users are no longer required to supply an email
969 else if(email == null) {
970 command = new String[8];
971 command[0] = Gatherer.config.perl_path;
972 command[1] = "-S";
973 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
974 command[3] = "-title";
975 command[4] = title;
976 command[5] = "-about";
977 command[6] = description;
978 command[7] = name;
979 }
980 else {
981 command = new String[10];
982 command[0] = Gatherer.config.perl_path;
983 command[1] = "-S";
984 command[2] = Gatherer.config.getScriptPath() + "mkcol.pl";
985 command[3] = "-title";
986 command[4] = title;
987 command[5] = "-creator";
988 command[6] = email;
989 command[7] = "-about";
990 command[8] = description;
991 command[9] = name;
992 }
993 }
994 else {
995 if(description == null || title == null) {
996 command = new String[2];
997 command[0] = "mkcol.pl";
998 command[1] = name;
999 }
1000 else if(email == null) {
1001 command = new String[6];
1002 command[0] = "mkcol.pl";
1003 command[1] = "-title";
1004 command[2] = title;
1005 command[3] = "-about";
1006 command[4] = description;
1007 command[5] = name;
1008 }
1009 else {
1010 command = new String[8];
1011 command[0] = "mkcol.pl";
1012 command[1] = "-title";
1013 command[2] = title;
1014 command[3] = "-creator";
1015 command[4] = email;
1016 command[5] = "-about";
1017 command[6] = description;
1018 command[7] = name;
1019 }
1020 }
1021 GShell process = new GShell(command, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1022 process.addGShellListener(this);
1023 process.run(); // Don't bother threading this... yet
1024 }
1025
1026 /** 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.
1027 * @param event A <strong>GShellEvent</strong> which contains a the message.
1028 */
1029 public synchronized void message(GShellEvent event) {
1030 }
1031 /** 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.
1032 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1033 * @see org.greenstone.gatherer.collection.Collection
1034 */
1035 public void metadataChanged(MSMEvent event) {
1036 // Again this change means we need to save the collection again.
1037 collection.setSaved(false);
1038 }
1039 /** This call is fired whenever a process within a GShell created by this class begins.
1040 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1041 * @see org.greenstone.gatherer.Gatherer
1042 * @see org.greenstone.gatherer.gui.GUIManager
1043 * @see org.greenstone.gatherer.shell.GShell
1044 */
1045 public synchronized void processBegun(GShellEvent event) {
1046 Gatherer.println("CollectionManager.processBegun(" + event.getType() + ")");
1047 ///ystem.err.println("ProcessBegun " + event.getType());
1048 // If this is one of the types where we wish to lock user control
1049 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1050 }
1051 /** This call is fired whenever a process within a GShell created by this class ends.
1052 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1053 * @see org.greenstone.gatherer.Gatherer
1054 * @see org.greenstone.gatherer.gui.GUIManager
1055 * @see org.greenstone.gatherer.shell.GShell
1056 */
1057 public synchronized void processComplete(GShellEvent event) {
1058 Gatherer.println("CollectionManager.processComplete(" + event.getType() + ")");
1059 ///ystem.err.println("ProcessComplete " + event.getType());
1060 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1061 ///ystem.err.println("Recieved process complete event - " + event);
1062 // If we were running an import, now run a build.
1063 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1064 // Finish import.
1065 collection.setImported(true);
1066 buildCollection();
1067 }
1068 // If we were running a build, now is when we move files across.
1069 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1070 if(installCollection()) {
1071 // If we have a local library running (that we know about) then we ask it to add our newly create collection
1072 if(Gatherer.config.exec_file != null) {
1073 Gatherer.self.configServer(GSDLSiteConfig.ADD_COMMAND + collection.getName());
1074 }
1075
1076 // Fire a collection changed first to update the preview etc buttons
1077 Gatherer.g_man.collectionChanged(ready());
1078
1079 // Now display a message dialog saying its all built
1080 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", false);
1081 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1082 collection_built_warning_dialog.display();
1083 collection_built_warning_dialog.dispose();
1084 collection_built_warning_dialog = null;
1085 }
1086 else {
1087 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1088 Gatherer.g_man.collectionChanged(ready());
1089 }
1090 }
1091 else if(event.getStatus() == GShell.ERROR || event.getStatus() == GShell.CANCELLED) {
1092 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1093 Gatherer.g_man.collectionChanged(ready());
1094 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.
1095 }
1096 }
1097 /** Determine if the manager is ready for actions apon its collection.
1098 * @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.
1099 */
1100 static public synchronized boolean ready() {
1101 if(collection != null) {
1102 return true;
1103 }
1104 else {
1105 return false;
1106 }
1107 }
1108
1109 public synchronized boolean reallyReady() {
1110 // If the closing thread is non-null we should only allow that thread to see the collection as closed.
1111 if(closing_thread != null) {
1112 // Only the closing thread sees the truth
1113 if(Thread.currentThread() == closing_thread) {
1114 return (collection == null);
1115 }
1116 // All other threads are told a lie.
1117 else {
1118 return true;
1119 }
1120 }
1121 else {
1122 if(collection != null) {
1123 return true;
1124 }
1125 else {
1126 return false;
1127 }
1128 }
1129 }
1130
1131 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1132 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1133 */
1134 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1135 build_monitor = monitor;
1136 }
1137 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1138 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1139 */
1140 public void registerImportMonitor(GShellProgressMonitor monitor) {
1141 import_monitor = monitor;
1142 }
1143 /** Remove a previously assigned special directory mapping.
1144 * @param target The <string>FileNode</strong> representing the special directory mapping to remove as a <strong>String</strong>.
1145 * @return The <strong>File</strong> of the mapping removed.
1146 * @see org.greenstone.gatherer.file.FileNode
1147 */
1148 public File removeDirectoryMapping(FileNode target) {
1149 // Remove from config, remembering file
1150 File file = Gatherer.config.removeDirectoryMapping(target.toString());
1151 // Update tree.
1152 Gatherer.g_man.gather_pane.refreshWorkspaceTree(WorkspaceTree.MAPPED_DIRECTORIES_CHANGED);
1153 return file;
1154 }
1155 /** Used to check whether all open collections have a 'saved' state.
1156 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1157 * @see org.greenstone.gatherer.collection.Collection
1158 */
1159 public boolean saved() {
1160 boolean result = true;
1161 if(collection != null) {
1162 result = collection.getSaved();
1163 }
1164 return result;
1165 }
1166 /** Saves a collection by serializing it to file.
1167 * @param close_after <i>true</i> to cause the Gatherer to close the collection once save is complete, <i>false</i> otherwise.
1168 * @param exit_after <i>true</i> to cause the Gatherer to exit once save is complete, <i>false</i> otherwise.
1169 * @see org.greenstone.gatherer.Gatherer
1170 * @see org.greenstone.gatherer.gui.GUIManager
1171 * @see org.greenstone.gatherer.collection.Collection
1172 */
1173 public void saveCollection(boolean close_after, boolean exit_after) {
1174 Gatherer.println("Save collection: " + collection.getName());
1175 try {
1176 SaveCollectionTask save_task = new SaveCollectionTask(collection, close_after, exit_after);
1177 save_task.start();
1178 // Run this in the same thread
1179 //save_task.run();
1180 }
1181 catch(Exception error) {
1182 Gatherer.printStackTrace(error);
1183 }
1184 }
1185 /** Saves the current collection to a new filename, then restores the original collection. Finally opens the collection copy.
1186 * @param name The name collection name.
1187 */
1188 public void saveCollectionAs(String name) {
1189 // We need to do this in a separate thread so create a SaveCollectionAsTask
1190 try {
1191 SaveCollectionTask save_task = new SaveCollectionTask(collection, name);
1192 save_task.start();
1193 }
1194 catch(Exception error) {
1195 Gatherer.printStackTrace(error);
1196 }
1197 }
1198
1199 /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. We want to mark the collection so that it needs saving again.
1200 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1201 * @see org.greenstone.gatherer.collection.Collection
1202 */
1203 public void setChanged(MSMEvent event) {
1204 // Invalidate saved
1205 collection.setSaved(false);
1206 }
1207
1208 public void setClosingThread(boolean set) {
1209 if(set) {
1210 closing_thread = Thread.currentThread();
1211 }
1212 else {
1213 closing_thread = null;
1214 }
1215 }
1216
1217 /** Updates the given workspace tree model to reference the private cache of the currently loaded collection. */
1218 /* private void updatePrivateWorkspace(DefaultTreeModel model) {
1219 // Add Private workspace if a collection has been loaded.
1220 if(ready() && !Gatherer.config.get("workflow.mirror", true)) {
1221 FileNode root = (FileNode)model.getRoot();
1222 // Remove old private workspace
1223 FileNode old = (FileNode)model.getChild(root, 2);
1224 model.removeNodeFromParent(old);
1225 // Create and insert new.
1226 FileNode private_workspace = new FileNode(new File(getCollectionCache()), Dictionary.get("Tree.Private"));
1227 model.insertNodeInto(private_workspace, root, 2);
1228 }
1229 } */
1230 /** 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.
1231 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
1232 * @see org.greenstone.gatherer.collection.Collection
1233 */
1234 public void valueChanged(MSMEvent event) {
1235 collection.setSaved(false);
1236 }
1237
1238 /** Install collection by moving its files from building to index after a successful build.
1239 * @see org.greenstone.gatherer.Gatherer
1240 * @see org.greenstone.gatherer.util.Utility
1241 */
1242 private boolean installCollection() {
1243 Gatherer.println("Build complete. Moving files.");
1244
1245 try {
1246 // We have to ensure that the local library
1247 if(Gatherer.config.exec_file != null) {
1248 ///ystem.err.println("Local Library Found!");
1249 //Gatherer.g_man./review_pane.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1250 Gatherer.self.configServer(GSDLSiteConfig.RELEASE_COMMAND + collection.getName());
1251 }
1252
1253 File index_dir = new File(getCollectionIndex(), "temp.txt");
1254 index_dir = index_dir.getParentFile();
1255 if(index_dir.exists()) {
1256 Gatherer.println("Index = " + index_dir.getAbsolutePath());
1257 }
1258
1259 File build_dir = new File(getCollectionBuild(), "temp.txt");
1260 build_dir = build_dir.getParentFile();
1261 Gatherer.println("Build = " + build_dir.getAbsolutePath());
1262
1263 // Get the build mode from the build options
1264 String build_mode = collection.build_options.getBuildValue("mode");
1265
1266 // Special case for build mode "all": replace index dir with building dir
1267 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1268 // Remove the old index directory
1269 if (index_dir.exists()) {
1270 Utility.delete(index_dir);
1271 // Check the delete worked
1272 if (index_dir.exists()) {
1273 throw new Exception("Index directory cannot be removed.");
1274 }
1275 }
1276
1277 // Move the building directory to become the new index directory
1278 if (build_dir.renameTo(index_dir) == false) {
1279 throw new Exception("Build directory cannot be moved.");
1280 }
1281
1282 // Create a new building directory
1283 File new_build = new File(getCollectionBuild(), "temp.txt");
1284 new_build = new_build.getParentFile();
1285 new_build.mkdir();
1286 }
1287
1288 // Otherwise copy everything in the building dir into the index dir
1289 else {
1290 File[] build_files = build_dir.listFiles();
1291 for (int i = 0; i < build_files.length; i++) {
1292 File build_file = build_files[i];
1293 File index_equivalent = new File(index_dir, build_file.getName());
1294
1295 // Remove the old file in the index directory (if it exists)
1296 if (index_equivalent.exists()) {
1297 Utility.delete(index_equivalent);
1298 // Check the delete worked
1299 if (index_equivalent.exists()) {
1300 throw new Exception("Index file " + index_equivalent + " cannot be removed.");
1301 }
1302 }
1303
1304 // Move the file into the index directory
1305 if (build_file.renameTo(index_equivalent) == false) {
1306 throw new Exception("File " + build_file + " cannot be moved into index directory.");
1307 }
1308 }
1309 }
1310 }
1311 catch (Exception exception) {
1312 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);
1313 return false;
1314 }
1315 return true;
1316 }
1317
1318 private boolean searchArchivesForMetadata(File archive_directory) {
1319 /** @todo - ensure GreenstoneArchiveParser works as expected. */
1320 return true;
1321 }
1322
1323 /** now this returns true if successful, false if unsuccessful or cancelled */
1324 private boolean searchForMetadata(File current_file) {
1325 boolean success = true;
1326 if(current_file.isFile() && current_file.getName().equals(Utility.METADATA_XML)) {
1327 success = collection.msm.searchForMetadata(null, new FileNode(current_file), false, true); // A dummy run only.
1328 }
1329 else {
1330 File[] children_files = current_file.listFiles();
1331 for(int i = 0; success && children_files != null && i < children_files.length; i++) {
1332 success = searchForMetadata(children_files[i]);
1333 }
1334 }
1335 return success;
1336 }
1337
1338 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title) {
1339 boolean first_name = true;
1340 boolean first_extra = true;
1341 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath();
1342
1343 HashMap mappings = collection.msm.profiler.getActions(collection_path);
1344 if(mappings == null) {
1345 Gatherer.println("Mappings is null, which is odd. Leaving all configuration commands which use metadata as they are.");
1346 }
1347
1348 // 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.
1349 try {
1350 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
1351 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
1352 String command = null;
1353 while((command = in.readLine()) != null) {
1354 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
1355 while(command.trim().endsWith("\\")) {
1356 command = command.substring(0, command.lastIndexOf("\\"));
1357 String next_line = in.readLine();
1358 if(next_line != null) {
1359 command = command + next_line;
1360 }
1361 }
1362 ///ystem.err.println("Read: " + command);
1363 // Now we've finished parsing a line, determine what to do with it.
1364 String command_lc = command.toLowerCase();
1365 if (command_lc.startsWith(StaticStrings.COLLECTIONMETADATA_STR)) {
1366 // we don't want to import collectionname, collectionextra, iconcollection, iconcollectionsmall from the base collection
1367 StringTokenizer tokeniser = new StringTokenizer(command_lc);
1368 tokeniser.nextToken(); // remove the first one
1369 String meta_name = tokeniser.nextToken();
1370 tokeniser = null;
1371 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) ) {
1372 continue;
1373 }
1374 }
1375 // 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.
1376 if(command_lc.startsWith(Utility.CFG_CLASSIFY)) {
1377 StringTokenizer tokenizer = new StringTokenizer(command);
1378 StringBuffer text = new StringBuffer(tokenizer.nextToken());
1379 // Read in the classifier command watching for hfile, metadata and sort arguments.
1380 String buttonname = null;
1381 String hfile = null;
1382 String new_metadata = null;
1383 String old_metadata = null;
1384 while(tokenizer.hasMoreTokens()) {
1385 String token = tokenizer.nextToken();
1386 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) {
1387 if(tokenizer.hasMoreTokens()) {
1388 text.append(" ");
1389 text.append(token);
1390 token = tokenizer.nextToken();
1391 hfile = token;
1392 }
1393 }
1394 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) {
1395 if(tokenizer.hasMoreTokens()) {
1396 text.append(" ");
1397 text.append(token);
1398 String temp_metadata = tokenizer.nextToken();
1399 String replacement = null;
1400 if(mappings != null) {
1401 replacement = (String) mappings.get(temp_metadata);
1402 }
1403 if(replacement != null) {
1404 token = replacement;
1405 old_metadata = temp_metadata;
1406 new_metadata = replacement;
1407 }
1408 else {
1409 token = temp_metadata;
1410 }
1411 temp_metadata = null;
1412 replacement = null;
1413 }
1414 }
1415 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) {
1416 if(tokenizer.hasMoreTokens()) {
1417 text.append(" ");
1418 text.append(token);
1419 String temp_metadata = tokenizer.nextToken();
1420 String replacement = null;
1421 if(mappings != null) {
1422 replacement = (String) mappings.get(temp_metadata);
1423 }
1424 if(replacement != null) {
1425 token = replacement;
1426 }
1427 else {
1428 token = temp_metadata;
1429 }
1430 temp_metadata = null;
1431 replacement = null;
1432 }
1433 }
1434 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) {
1435 buttonname = token;
1436 }
1437 text.append(' ');
1438 text.append(token);
1439 token = null;
1440 }
1441 tokenizer = null;
1442
1443 // 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)!
1444 if(old_metadata != null && new_metadata != null && buttonname == null) {
1445 text.append(' ');
1446 text.append(Utility.CFG_CLASSIFY_BUTTONNAME);
1447 text.append(' ');
1448 text.append(old_metadata);
1449 }
1450 command = text.toString();
1451 // Replace the hfile if we found it
1452 if(hfile != null && new_metadata != null) {
1453 command = command.replaceAll(hfile, new_metadata + ".txt");
1454 }
1455 buttonname = null;
1456 hfile = null;
1457 new_metadata = null;
1458 old_metadata = null;
1459 write(out, command);
1460 }
1461 else {
1462 // 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.
1463 boolean format_command = command_lc.startsWith(Utility.CFG_FORMAT);
1464 // Replace mapping strings
1465 if(mappings != null) {
1466 for(Iterator keys = mappings.keySet().iterator(); keys.hasNext(); ) {
1467 String target = (String) keys.next();
1468 String replacement = (String) mappings.get(target);
1469 if(format_command) {
1470 target = "\\[" + target + "\\]";
1471 replacement = "{Or}{[" + replacement + "]," + target + "}";
1472 }
1473 command = command.replaceAll(target, replacement);
1474 }
1475 }
1476 write(out, command);
1477 }
1478 }
1479 in.close();
1480 in = null;
1481 out.flush();
1482 out.close();
1483 out = null;
1484 }
1485 catch(Exception error) {
1486 Gatherer.printStackTrace(error);
1487 }
1488 // All done, I hope.
1489 }
1490
1491 private void write(BufferedWriter out, String message)
1492 throws Exception {
1493 ///ystem.err.println("Writing: " + message);
1494 out.write(message, 0, message.length());
1495 out.newLine();
1496 }
1497
1498 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
1499 private class FMTreeModelListener
1500 implements TreeModelListener {
1501 /** 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.
1502 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1503 */
1504 public void treeNodesChanged(TreeModelEvent event) {
1505 if(collection != null) {
1506 collection.setSaved(false);
1507 }
1508 }
1509 /** 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.
1510 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1511 */
1512 public void treeNodesInserted(TreeModelEvent event) {
1513 if(collection != null) {
1514 collection.setSaved(false);
1515 }
1516 }
1517 /** 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.
1518 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1519 */
1520 public void treeNodesRemoved(TreeModelEvent event) {
1521 if(collection != null) {
1522 collection.setSaved(false);
1523 }
1524 }
1525 /** 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.
1526 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1527 */
1528 public void treeStructureChanged(TreeModelEvent event) {
1529 if(collection != null) {
1530 collection.setSaved(false);
1531 }
1532 }
1533 }
1534}
Note: See TracBrowser for help on using the repository browser.