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

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

removed the copy_monitor stuff cos its not used anywhere and its confusing. John added it in to be used for the stage where html links got rewritten as part of the mirroring process. if we ever do this, we can add it back in.

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