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

Last change on this file since 10009 was 10006, checked in by mdewsnip, 19 years ago

Moved Utility.parse to XMLTools.parseXMLFile, as part of tidying up the Utility class.

  • Property svn:keywords set to Author Date Id Revision
File size: 59.5 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.util.*;
41import javax.swing.*;
42import javax.swing.event.*;
43import javax.swing.filechooser.FileSystemView;
44import javax.swing.tree.*;
45import org.greenstone.gatherer.Configuration;
46import org.greenstone.gatherer.DebugStream;
47import org.greenstone.gatherer.Dictionary;
48import org.greenstone.gatherer.Gatherer;
49import org.greenstone.gatherer.LocalLibraryServer;
50import org.greenstone.gatherer.ServletConfiguration;
51import org.greenstone.gatherer.cdm.CollectionDesignManager;
52import org.greenstone.gatherer.cdm.CollectionMeta;
53import org.greenstone.gatherer.cdm.CollectionMetaManager;
54import org.greenstone.gatherer.cdm.CommandTokenizer;
55import org.greenstone.gatherer.gui.ExternalCollectionPrompt;
56import org.greenstone.gatherer.gui.LockFileDialog;
57import org.greenstone.gatherer.gui.ModalProgressPopup;
58import org.greenstone.gatherer.gui.NewCollectionMetadataPrompt;
59import org.greenstone.gatherer.gui.WarningDialog;
60import org.greenstone.gatherer.metadata.DocXMLFileManager;
61import org.greenstone.gatherer.metadata.MetadataChangedListener;
62import org.greenstone.gatherer.metadata.MetadataSet;
63import org.greenstone.gatherer.metadata.MetadataSetManager;
64import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
65import org.greenstone.gatherer.metadata.ProfileXMLFileManager;
66import org.greenstone.gatherer.shell.GShell;
67import org.greenstone.gatherer.shell.GShellEvent;
68import org.greenstone.gatherer.shell.GShellListener;
69import org.greenstone.gatherer.shell.GShellProgressMonitor;
70import org.greenstone.gatherer.util.Codec;
71import org.greenstone.gatherer.util.StaticStrings;
72import org.greenstone.gatherer.util.Utility;
73import org.greenstone.gatherer.util.XMLTools;
74import org.w3c.dom.*;
75
76/** This class manages many aspects of the collection, from its creation via scripts, data access via methods and its importing and building into the final collection. It is also resposible for firing appropriate event when significant changes have occured within the collection, and for creating a new metadata set manager as necessary.
77 * @author John Thompson
78 * @version 2.3
79 */
80public class CollectionManager
81 implements GShellListener, MetadataChangedListener
82{
83 /** Are we currently in the process of building? */
84 private boolean building = false;
85 /** Are we currently in the process of importing? */
86 private boolean importing = false;
87 /** The objects listening for CollectionContentsChanged events. */
88 private ArrayList collection_contents_changed_listeners = new ArrayList();
89 /** The collection this manager is managing! */
90 static private Collection collection = null;
91 /** The collection_model. */
92 private CollectionTreeModel collection_model = null;
93 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
94 private FMTreeModelListener fm_tree_model_listener = null;
95 /** The monitor resposible for parsing the build process. */
96 private GShellProgressMonitor build_monitor = null;
97 /** The monitor resposible for parsing the import process. */
98 private GShellProgressMonitor import_monitor = null;
99
100 /** The name of the standard lock file. */
101 static final public String LOCK_FILE = "gli.lck";
102
103 /** Used to indicate the source of the message is the file collection methods. */
104 static final public int COLLECT = 3;
105 /** Used to indicate the source of the message is the building methods. */
106 static final public int BUILDING = 5;
107
108 /** Constructor. */
109 public CollectionManager() {
110 // Initialisation.
111 this.building = false;
112 this.importing = false;
113 this.collection = null;
114
115 MetadataXMLFileManager.addMetadataChangedListener(this);
116 }
117
118
119 public void addCollectionContentsChangedListener(CollectionContentsChangedListener listener)
120 {
121 collection_contents_changed_listeners.add(listener);
122 }
123
124
125 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
126 * @see org.greenstone.gatherer.Configuration
127 * @see org.greenstone.gatherer.Gatherer
128 * @see org.greenstone.gatherer.collection.Collection
129 * @see org.greenstone.gatherer.gui.BuildOptions
130 * @see org.greenstone.gatherer.shell.GShell
131 * @see org.greenstone.gatherer.shell.GShellListener
132 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
133 * @see org.greenstone.gatherer.util.Utility
134 */
135 private void buildCollection() {
136 DebugStream.println("CollectionManager.buildCollection()");
137 building = true;
138
139 // Generate the buildcol.pl command
140 ArrayList command_parts_list = new ArrayList();
141 if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
142 command_parts_list.add(Configuration.perl_path);
143 command_parts_list.add("-S");
144 }
145 command_parts_list.add(Configuration.getScriptPath() + "buildcol.pl");
146 command_parts_list.add("-gli");
147 command_parts_list.add("-language");
148 command_parts_list.add(Configuration.getLanguage());
149 command_parts_list.add("-collectdir");
150 command_parts_list.add(getCollectDirectory());
151
152 String[] build_options = collection.build_options.getValues();
153 for (int i = 0; i < build_options.length; i++) {
154 command_parts_list.add(build_options[i]);
155 }
156
157 command_parts_list.add(collection.getName());
158
159 // Run the buildcol.pl command
160 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
161 GShell shell = new GShell(command_parts, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
162 shell.addGShellListener(Gatherer.g_man.create_pane);
163 shell.start();
164 DebugStream.println("CollectionManager.buildCollection().return");
165 }
166
167
168 /** Used to determine whether the currently active collection has been built.
169 * @return A boolean indicating the built status of the collection.
170 */
171 public boolean built() {
172 if(collection != null) {
173 // Determine if the collection has been built by looking for the build.cfg file
174 File build_cfg_file = new File(getCollectionIndexDirectoryPath() + Utility.BUILD_CFG_FILENAME);
175 return build_cfg_file.exists();
176 }
177 return false;
178 }
179
180 /** 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 */
181 public boolean canDelete(File file) {
182 if (!file.isDirectory()) {
183 return file.canWrite();
184 }
185 File [] file_list = file.listFiles();
186 for (int i=0; i<file_list.length; i++) {
187 if (!canDelete(file_list[i])) {
188 return false;
189 }
190 }
191 return true;
192 }
193 /** Called to close the current collection and remove its lock file.
194 * @see org.greenstone.gatherer.Gatherer
195 * @see org.greenstone.gatherer.collection.Collection
196 * @see org.greenstone.gatherer.util.Utility
197 */
198 public void closeCollection() {
199 DebugStream.println("Close collection: " + collection.getName());
200
201 // Remove the lock on this file, then remove the collection.
202 String collection_directory_path = getCollectionDirectoryPath(collection.getName());
203 File lock_file = new File(collection_directory_path + LOCK_FILE);
204 lock_file.delete();
205 if (lock_file.exists()) {
206 System.err.println("Warning: Lockfile was not successfully deleted.");
207 }
208
209 MetadataSetManager.clearMetadataSets();
210 MetadataXMLFileManager.clearMetadataXMLFiles();
211 DocXMLFileManager.clearDocXMLFiles();
212 ProfileXMLFileManager.clearProfileXMLFile();
213
214 collection = null;
215 collection_model = null;
216 Configuration.setCollectionConfiguration(null);
217 Gatherer.refresh(Gatherer.COLLECTION_CLOSED);
218 if (Gatherer.g_man != null) {
219 Gatherer.g_man.updateUI(); // !!! Necessary?
220 }
221 }
222
223 /** Method that is called whenever something has changed in the configuration of this collection. */
224 public void configurationChanged() {
225 if(collection != null) {
226 collection.setSaved(false);
227 }
228 }
229
230 public void convertToGS3Collection() {
231 // Generate the convert_coll_from_gs2.pl command
232 ArrayList command_parts_list = new ArrayList();
233 if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
234 command_parts_list.add(Configuration.perl_path);
235 command_parts_list.add("-S");
236 }
237 command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
238 command_parts_list.add("-collectdir");
239 command_parts_list.add(getCollectDirectory());
240 command_parts_list.add(collection.getName());
241
242 // Run the convert_coll_from_gs2.pl command
243 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
244 GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
245 process.addGShellListener(this);
246 process.run(); // Don't bother threading this... yet
247
248 }
249
250 /** When basing a new collection on an existing one, we need to copy
251 * over some extra directories: images and macros
252 */
253 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
254 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
255 return false;
256 }
257 DebugStream.println("Copying images and macros dirs from the base collection");
258 try {
259 // do the images dir
260 File base_coll_images = new File(base_coll_dir, Utility.IMAGES_DIR);
261 if (base_coll_images.isDirectory()) {
262 // copy all the images over
263 File new_coll_images = new File(new_coll_dir, Utility.IMAGES_DIR);
264 new_coll_images.mkdirs();
265
266 // copy the contents over
267 Gatherer.f_man.getQueue().copyDirectoryContents(base_coll_images, new_coll_images, null);
268 }
269 }
270 catch (Exception e) {
271 DebugStream.println("Couldn't copy over the images dir from the base collection: "+e.toString());
272 }
273 try {
274 // do the macros dir
275 File base_coll_macros = new File(base_coll_dir, Utility.MACROS_DIR);
276 if (base_coll_macros.isDirectory()) {
277 // copy all the macros over
278 File new_coll_macros = new File(new_coll_dir, Utility.MACROS_DIR);
279 new_coll_macros.mkdirs();
280
281 // copy the contents over
282 Gatherer.f_man.getQueue().copyDirectoryContents(base_coll_macros, new_coll_macros, null);
283 }
284 }
285 catch (Exception e) {
286 DebugStream.println("Couldn't copy over the macros dir from the base collection: "+e.toString());
287 }
288 return true;
289 }
290
291 /** 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.
292 * @param description a description of the collection as a String
293 * @param email the email address of the author/maintainer as a String
294 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
295 * @param title the longer title of the collection as a String
296 * @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
297 * @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
298 */
299 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
300 {
301 try {
302 // first make sure that the collect directory exists
303 File collect_dir = new File(getCollectDirectory());
304 if (!collect_dir.exists()) {
305 collect_dir.mkdirs();
306 }
307
308 // Create a progress monitor
309 ProgressMonitor progress = new ProgressMonitor(Gatherer.g_man, Dictionary.get("CollectionManager.Creating_New"), "mkcol.pl", 0, 4);
310
311 // Create the new collection.
312 makeCollection(description, email, name, title);
313 progress.setProgress(1);
314
315 // *******************
316 //check that this creation has worked - simply by checking for the existence of the collect.cfg file
317 String collection_directory_path = getCollectionDirectoryPath(name);
318 File config_file = new File(Utility.getConfigFile(collection_directory_path));
319 if (!config_file.exists()) {
320 // no point continuing
321 DebugStream.println("The collection could not be created");
322 progress.close();
323 return;
324 }
325
326 // ACTIVE_DIR/log/
327 File log_dir_temp = new File(Utility.getLogDir(collection_directory_path)+"temp.dat");
328 File log_dir = log_dir_temp.getParentFile();
329 log_dir.mkdirs();
330 progress.setNote(Dictionary.get("CollectionManager.Log_Created"));
331
332 // Make sure an import folder exists
333 File import_directory = new File(Utility.getImportDir(collection_directory_path));
334 if (!import_directory.exists()) {
335 import_directory.mkdirs();
336 }
337
338 progress.setProgress(2);
339
340 // Now create the collection object around the directory.
341 collection = new Collection(new File(collection_directory_path, name + ".col"));
342
343 MetadataSetManager.clearMetadataSets();
344 MetadataXMLFileManager.clearMetadataXMLFiles();
345 DocXMLFileManager.clearDocXMLFiles();
346
347 // Import default metadata sets, if any
348 for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
349 importMetadataSet((MetadataSet) metadata_sets.get(i));
350 }
351
352 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_directory_path, Utility.META_DIR));
353
354 // Before creating the CollectionDesignManager check if we are basing it upon some other collection
355 if (base_collection_directory != null) {
356 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
357 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
358 // copy over other needed directories
359 copyExtraBaseCollStuff(new File(collection_directory_path), base_collection_directory);
360 // Try to import any existing metadata sets for this collection
361 // Look in base_collection_directory/metadata and import any metadata sets found.
362 File base_metadata_directory = new File(base_collection_directory, Utility.META_DIR);
363 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
364 if (base_metadata_sets != null) {
365 for (int i = 0; i < base_metadata_sets.size(); i++) {
366 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
367 }
368 }
369 else {
370 DebugStream.println("This base collection has no metadata directory.");
371 }
372
373 // If no sets were imported...
374 if (MetadataSetManager.getMetadataSets().size() == 0) {
375 // Prompt the user so that they can choose at least one initial metadata set. We're sneaky here and just create a ncm_prompt
376 DebugStream.println("This collection has no metadata sets. Present the user with the metadata set selection prompt.");
377 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt();
378 // If cancelled then they really do mean to start a collection with no metadata sets.
379 if (!ncm_prompt.isCancelled()) {
380 ArrayList initial_sets = ncm_prompt.getSets();
381 for (int i = 0; initial_sets != null && i < initial_sets.size(); i++) {
382 importMetadataSet((MetadataSet) initial_sets.get(i));
383 }
384 }
385 ncm_prompt.dispose();
386 ncm_prompt = null;
387 }
388
389 // Now we update our collect.cfg
390 DebugStream.println("Copy and update collect.cfg from base collection.");
391 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE), new File(collection_directory_path, Utility.CONFIG_FILE), description, email, title);
392 }
393
394 // Always import the extracted metadata set if we didn't already
395 if (MetadataSetManager.getMetadataSet(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE) == null) {
396 File extracted_metadata_set_file = new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
397 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
398 }
399
400 collection.cdm = new CollectionDesignManager(new File(getCollectionConfigFilePath()));
401
402 // 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
403 if (base_collection_directory != null) {
404 // Update the creator and maintainer
405 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
406 creator_collectionmeta.setValue(email);
407 creator_collectionmeta = null;
408 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
409 maintainer_collectionmeta.setValue(email);
410 maintainer_collectionmeta = null;
411
412 // Update the collection title
413 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
414 collection_name_collectionmeta.setValue(title);
415 collection_name_collectionmeta = null;
416
417 // And now the description
418 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
419 collection_extra_collectionmeta.setValue(description);
420 collection_extra_collectionmeta = null;
421
422 // All collections based on others are automatically public
423 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
424 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
425 public_collectionmeta = null;
426
427 // Finally reset the icons
428 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
429 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
430 icon_collection_collectionmeta = null;
431 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
432 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
433 icon_collection_small_collectionmeta = null;
434 }
435
436 progress.setProgress(3);
437
438 // Create a lock file
439 createLockFile(new File(collection_directory_path, LOCK_FILE));
440
441 progress.setProgress(4);
442 progress.setNote(Dictionary.get("CollectionManager.Session_Ready", name));
443 progress.close();
444 }
445 catch (Exception error) {
446 DebugStream.printStackTrace(error);
447 }
448 }
449
450
451 private void createLockFile(File lock_file)
452 {
453 try {
454 Document default_lockfile = XMLTools.parseXMLFile("xml/" + LOCK_FILE, true);
455 String user_name = System.getProperty("user.name");
456 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
457 person_element.appendChild(default_lockfile.createTextNode(user_name));
458 person_element = null;
459 user_name = null;
460 String machine_name = Utility.getMachineName();
461 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
462 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
463 machine_element = null;
464 machine_name = null;
465 String date_time = Utility.getDateString();
466 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
467 date_element.appendChild(default_lockfile.createTextNode(date_time));
468 date_element = null;
469 date_time = null;
470 XMLTools.writeXMLFile(lock_file, default_lockfile);
471 }
472 catch (Exception exception) {
473 DebugStream.printStackTrace(exception);
474 }
475 }
476
477
478 public boolean deleteCollection(String collection_name)
479 {
480 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
481 }
482
483
484 public void fireFileAddedToCollection(File file)
485 {
486 // Send the event off to all the CollectionContentsChangedListeners
487 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
488 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
489 }
490 }
491
492
493 /** Retrieve the current collection.
494 * @return The <strong>Collection</strong> itself.
495 */
496 public Collection getCollection() {
497 return collection;
498 }
499
500
501 /** Constructs the absolute filename of the collection's directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;"
502 * @return A <strong>String</strong> containing the directory name.
503 */
504 static public String getCollectionDirectoryPath(String collection_name)
505 {
506 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
507 }
508
509
510 /** Constructs the absolute filename of the collection's .col file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/&lt;col_name&gt;.col"
511 * @return A <strong>String</strong> containing the filename.
512 */
513 public String getCollectionFilePath()
514 {
515 return getCollectionDirectoryPath(collection.getName()) + collection.getName() + ".col";
516 }
517
518
519 /** Constructs the absolute filename of the collection's archives directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/archives/"
520 * @return A <strong>String</strong> containing the filename.
521 */
522 public String getCollectionArchivesDirectoryPath()
523 {
524 return getCollectionDirectoryPath(collection.getName()) + "archives" + File.separator;
525 }
526
527
528 /** Constructs the absolute filename of the collection's building directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/building/"
529 * @return A <strong>String</strong> containing the filename.
530 */
531 public String getCollectionBuildingDirectoryPath()
532 {
533 return getCollectionDirectoryPath(collection.getName()) + "building" + File.separator;
534 }
535
536
537 /** Constructs the absolute filename of the collection's collect.cfg file, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/collect.cfg"
538 * @return A <strong>String</strong> containing the filename.
539 */
540 public String getCollectionConfigFilePath()
541 {
542 return getCollectionEtcDirectoryPath() + "collect.cfg";
543 }
544
545
546 /** Constructs the absolute filename of the collection's etc directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/etc/"
547 * @return A <strong>String</strong> containing the filename.
548 */
549 public String getCollectionEtcDirectoryPath()
550 {
551 return getCollectionDirectoryPath(collection.getName()) + "etc" + File.separator;
552 }
553
554
555 /** Constructs the absolute filename of the collection's images directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/images/"
556 * @return A <strong>String</strong> containing the filename.
557 */
558 public String getCollectionImagesDirectoryPath()
559 {
560 return getCollectionDirectoryPath(collection.getName()) + "images" + File.separator;
561 }
562
563
564 /** Constructs the absolute filename of the collection's import directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/import/"
565 * @return A <strong>String</strong> containing the filename.
566 */
567 public String getCollectionImportDirectoryPath()
568 {
569 return getCollectionDirectoryPath(collection.getName()) + "import" + File.separator;
570 }
571
572
573 /** Constructs the absolute filename of the collection's index directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/index/"
574 * @return A <strong>String</strong> containing the filename.
575 */
576 public String getCollectionIndexDirectoryPath()
577 {
578 return getCollectionDirectoryPath(collection.getName()) + "index" + File.separator;
579 }
580
581
582 /** Constructs the absolute filename of the collection's log directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/log/"
583 * @return A <strong>String</strong> containing the filename.
584 */
585 public String getCollectionLogDirectoryPath()
586 {
587 return getCollectionDirectoryPath(collection.getName()) + "log" + File.separator;
588 }
589
590
591 /** Constructs the absolute filename of the collection's metadata directory, which should resemble "$GSDLHOME/collect/&lt;col_name&gt;/metadata/"
592 * @return A <strong>String</strong> containing the filename.
593 */
594 public String getCollectionMetadataDirectoryPath()
595 {
596 return getCollectionDirectoryPath(collection.getName()) + "metadata" + File.separator;
597 }
598
599
600 /** Retrieve the tree model associated with the current collection. */
601 public CollectionTreeModel getCollectionTreeModel()
602 {
603 if (collection_model == null && collection != null) {
604 // Use the import directory to generate a new CollectionTreeModel
605 collection_model = new CollectionTreeModel(new CollectionTreeNode(new File(getCollectionImportDirectoryPath())));
606 // Ensure that the manager is a change listener for the tree.
607 if (fm_tree_model_listener == null) {
608 fm_tree_model_listener = new FMTreeModelListener();
609 }
610 collection_model.addTreeModelListener(fm_tree_model_listener);
611 }
612 return collection_model;
613 }
614
615
616 /** This method when called, creates a new GShell in order to run the import.pl script.
617 * @see org.greenstone.gatherer.Configuration
618 * @see org.greenstone.gatherer.Gatherer
619 * @see org.greenstone.gatherer.gui.BuildOptions
620 * @see org.greenstone.gatherer.shell.GShell
621 * @see org.greenstone.gatherer.shell.GShellListener
622 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
623 * @see org.greenstone.gatherer.util.Utility
624 */
625 public void importCollection() {
626 importing = true;
627
628 if (!saved()) {
629 DebugStream.println("CollectionManager.importCollection().forcesave");
630 import_monitor.saving();
631 saveCollection();
632 }
633
634 DebugStream.println("CollectionManager.importCollection()");
635 //check that we can remove the old index before starting import
636 File index_dir = new File(getCollectionIndexDirectoryPath());
637 if (index_dir.exists()) {
638 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
639 if (!canDelete(index_dir)) {
640 // tell the user
641 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
642 // tell the gui manager
643 // a message for the building log
644 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
645 Gatherer.g_man.create_pane.message(event);
646 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
647 Gatherer.g_man.create_pane.processComplete(event);
648 importing = false;
649 return;
650 }
651 }
652
653 // Generate the import.pl command
654 ArrayList command_parts_list = new ArrayList();
655 if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
656 command_parts_list.add(Configuration.perl_path);
657 command_parts_list.add("-S");
658 }
659 command_parts_list.add(Configuration.getScriptPath() + "import.pl");
660 command_parts_list.add("-gli");
661 command_parts_list.add("-language");
662 command_parts_list.add(Configuration.getLanguage());
663 command_parts_list.add("-collectdir");
664 command_parts_list.add(getCollectDirectory());
665
666 String[] import_options = collection.import_options.getValues();
667 for (int i = 0; i < import_options.length; i++) {
668 command_parts_list.add(import_options[i]);
669 }
670
671 command_parts_list.add(collection.getName());
672
673 // Run the buildcol.pl command
674 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
675 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
676 shell.addGShellListener(Gatherer.g_man.create_pane);
677 shell.start();
678 DebugStream.println("CollectionManager.importCollection().return");
679
680 importing = false;
681 }
682
683
684 public void importMetadataSet(MetadataSet external_metadata_set)
685 {
686 // Copy the .mds file into the collection's "metadata" folder...
687 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
688
689 // ...but not if it is the redundant "hidden.mds" file
690 if (external_metadata_set_file.getName().equals("hidden.mds")) {
691 return;
692 }
693
694 // ...and only if it doesn't already exist
695 File metadata_set_file = new File(getCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
696 if (!metadata_set_file.exists()) {
697 try {
698 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, null);
699 }
700 catch (Exception ex) {
701 ex.printStackTrace();
702 }
703
704 // Load it into the MetadataSetManager
705 MetadataSetManager.loadMetadataSet(metadata_set_file);
706 }
707 }
708
709
710 /** 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.
711 * @return true if the gli is currently importing
712 */
713 public boolean isImporting() {
714 return importing;
715 }
716
717
718 public void loadCollection(String collection_file_path)
719 {
720 // Display a modal progress popup to indicate that the collection is being loaded
721 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
722 load_collection_progress_popup.display();
723
724 // Load the collection on a separate thread so the progress bar updates correctly
725 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
726 }
727
728
729 private class LoadCollectionTask
730 extends Thread
731 {
732 private String collection_file_path = null;
733 private ModalProgressPopup load_collection_progress_popup = null;
734
735 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
736 {
737 this.collection_file_path = collection_file_path;
738 this.load_collection_progress_popup = load_collection_progress_popup;
739 }
740
741 public void run()
742 {
743 loadCollectionInternal(collection_file_path);
744 load_collection_progress_popup.close();
745 }
746 }
747
748
749 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
750 * @param location The path to the collection as a <strong>String</strong>.
751 * @see org.greenstone.gatherer.Configuration
752 * @see org.greenstone.gatherer.Gatherer
753 * @see org.greenstone.gatherer.collection.Collection
754 * @see org.greenstone.gatherer.util.Utility
755 */
756 private void loadCollectionInternal(String location)
757 {
758 DebugStream.println("Loading collection " + location + "...");
759 boolean non_gli_collection = false;
760
761 // Check we have actually been given a .col file.
762 if (!location.endsWith(".col")) {
763 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
764 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
765 return;
766 }
767
768 // Check that the collection configuration file is available
769 File collection_file = new File(location);
770
771 // Ensure that the directory exists
772 File collection_directory = collection_file.getParentFile();
773 if (collection_directory == null || !collection_directory.exists()) {
774 // We can't open this
775 System.err.println("CollectionManager.loadCollection: No collection directory.");
776 return;
777 }
778
779 File collection_config_file = new File(collection_directory, Utility.CONFIG_FILE);
780 if (!collection_config_file.exists()) {
781 System.err.println("CollectionManager.loadCollection: No config file.");
782 collection_directory = null;
783 collection_config_file = null;
784 return;
785 }
786
787 // Ensure that an import directory exists for this collection
788 File collection_import_directory = new File(collection_directory, Utility.IMPORT_DIR);
789 if (!collection_import_directory.exists()) {
790 collection_import_directory.mkdir();
791 }
792
793 // Special case of a user trying to open an old greenstone collection.
794 File collection_metadata_directory = new File(collection_directory, Utility.META_DIR);
795 if (!collection_metadata_directory.exists()) {
796 DebugStream.println("Loading non-gatherer collection...");
797 non_gli_collection = true;
798 }
799
800 // Now determine if a lock already exists on this collection.
801 String name = collection_directory.getName();
802 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
803 if (lock_file.exists()) {
804 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, name, lock_file);
805 int choice = dialog.getChoice();
806 dialog.dispose();
807 dialog = null;
808
809 if (choice != LockFileDialog.YES_OPTION) {
810 // user has cancelled
811 lock_file = null;
812 collection_directory = null;
813 collection_config_file = null;
814 return;
815 }
816
817 lock_file.delete();
818 }
819
820 try {
821 // Create a lock file.
822 createLockFile(lock_file);
823 // This lock file may not have been created so check
824 if(!lock_file.canWrite()) {
825 // The lock file cannot be written to. Most likely cause incorrect file permissions.
826 System.err.println("Cannot write lock file!");
827 String args[] = new String[2];
828 args[0] = location;
829 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Title");
830 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
831 args = null;
832 return;
833 }
834
835 // Open the collection file
836 this.collection = new Collection(collection_file);
837 if (collection.error) {
838 collection = null;
839 // Remove lock file
840 if (lock_file.exists()) {
841 lock_file.delete();
842 }
843 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
844 }
845
846 MetadataSetManager.clearMetadataSets();
847 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
848
849 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
850
851 // If this is a non-GLI (legacy) collection, ask the user to choose some metadata sets
852 if (non_gli_collection) {
853 if (!addSomeMetadataSets(collection_directory)) {
854 lock_file = null;
855 collection_directory = null;
856 closeCollection();
857 return;
858 }
859
860 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
861 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
862 }
863
864 // Read through the metadata.xml files in the import directory, building up the metadata value trees
865 MetadataXMLFileManager.clearMetadataXMLFiles();
866 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory);
867
868 // Read through the doc.xml files in the archives directory
869 File collection_archives_directory = new File(getCollectionArchivesDirectoryPath());
870 DocXMLFileManager.clearDocXMLFiles();
871 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory);
872
873 collection.cdm = new CollectionDesignManager(collection_config_file);
874 if (non_gli_collection) {
875 // Change the classifiers to use the namespaced element names
876 LegacyCollectionImporter.updateClassifiers(collection.cdm);
877 }
878
879 // We're done. Let everyone know.
880 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", name));
881 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
882 }
883 catch (Exception error) {
884 // There is obviously no existing collection present.
885 DebugStream.printStackTrace(error);
886 if(error.getMessage() != null) {
887 String[] args = new String[2];
888 args[0] = location;
889 args[1] = error.getMessage();
890 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
891 }
892 else {
893 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
894 }
895 }
896
897 lock_file = null;
898 collection_directory = null;
899 collection_config_file = null;
900 }
901
902
903 private void makeCollection(String description, String email, String name, String title) {
904 // Encode the description so it is safe to write to shell
905 if(Utility.isWindows()) {
906 description = Codec.transform(description, Codec.TEXT_TO_SHELL_WINDOWS);
907 }
908 else {
909 description = Codec.transform(description, Codec.TEXT_TO_SHELL_UNIX);
910 }
911
912 // Generate the mkcol.pl command
913 ArrayList command_parts_list = new ArrayList();
914 if (Utility.isWindows() && (!Gatherer.isGsdlRemote)) {
915 command_parts_list.add(Configuration.perl_path);
916 command_parts_list.add("-S");
917 }
918 command_parts_list.add(Configuration.getScriptPath() + "mkcol.pl");
919
920 command_parts_list.add("-collectdir");
921 command_parts_list.add(getCollectDirectory());
922 command_parts_list.add("-win31compat");
923 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
924
925 if (title != null && !title.equals("")) {
926 command_parts_list.add("-title");
927 command_parts_list.add(title);
928 }
929 if (email != null && !email.equals("")) {
930 command_parts_list.add("-creator");
931 command_parts_list.add(email);
932 }
933 if (description != null && !description.equals("")) {
934 command_parts_list.add("-about");
935 command_parts_list.add(description);
936 }
937
938 command_parts_list.add(name);
939
940 // Run the mkcol.pl command
941 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
942 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
943 process.run(); // Don't bother threading this... yet
944 }
945
946
947 /** 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.
948 * @param event A <strong>GShellEvent</strong> which contains a the message.
949 */
950 public synchronized void message(GShellEvent event) {
951 }
952
953
954 public void metadataChanged(CollectionTreeNode[] file_nodes)
955 {
956 if (collection != null) {
957 collection.setMetadataChanged(true);
958 }
959 }
960
961
962 /** This call is fired whenever a process within a GShell created by this class begins.
963 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
964 * @see org.greenstone.gatherer.Gatherer
965 * @see org.greenstone.gatherer.gui.GUIManager
966 * @see org.greenstone.gatherer.shell.GShell
967 */
968 public synchronized void processBegun(GShellEvent event) {
969 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
970 ///ystem.err.println("ProcessBegun " + event.getType());
971 // If this is one of the types where we wish to lock user control
972 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
973 }
974 /** This call is fired whenever a process within a GShell created by this class ends.
975 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
976 * @see org.greenstone.gatherer.Gatherer
977 * @see org.greenstone.gatherer.gui.GUIManager
978 * @see org.greenstone.gatherer.shell.GShell
979 */
980 public synchronized void processComplete(GShellEvent event) {
981 DebugStream.println("CollectionManager.processComplete(" + event.getType() + ")");
982 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
983 ///ystem.err.println("Recieved process complete event - " + event);
984 // If we were running an import, now run a build.
985 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
986 // Finish import.
987 collection.setImported(true);
988 buildCollection();
989 }
990 // If we were running a build, now is when we move files across.
991 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
992 if(installCollection()) {
993 // If we have a local library running then ask it to add our newly create collection
994 if (LocalLibraryServer.isRunning() == true) {
995 LocalLibraryServer.addCollection(collection.getName());
996 }
997 else if (Gatherer.GS3) {
998 convertToGS3Collection();
999 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1000 }
1001
1002 // Fire a collection changed first to update the preview etc buttons
1003 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1004
1005 // Now display a message dialog saying its all built
1006 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", "CollectionBuilt.Title", Dictionary.get("CollectionBuilt.Message"), null, false);
1007 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1008 collection_built_warning_dialog.display();
1009 collection_built_warning_dialog.dispose();
1010 collection_built_warning_dialog = null;
1011 }
1012 else {
1013 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1014 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1015 DebugStream.println("Status is ok but !installCollection()");
1016 }
1017 }
1018 else if (event.getStatus() == GShell.CANCELLED) {
1019 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1020 Gatherer.g_man.repaint();
1021 }
1022 else if (event.getStatus() == GShell.ERROR) {
1023 DebugStream.println("There was an error in the gshell:"+ event.getMessage());
1024 if (event.getType() == GShell.NEW) {
1025 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1026 } else {
1027 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1028 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1029 }
1030
1031 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.
1032 }
1033 }
1034
1035
1036 /** Determine if the manager is ready for actions apon its collection.
1037 * @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.
1038 */
1039 static public synchronized boolean ready() {
1040 if(collection != null) {
1041 return true;
1042 }
1043 else {
1044 return false;
1045 }
1046 }
1047
1048
1049 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1050 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1051 */
1052 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1053 build_monitor = monitor;
1054 }
1055 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1056 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1057 */
1058 public void registerImportMonitor(GShellProgressMonitor monitor) {
1059 import_monitor = monitor;
1060 }
1061
1062
1063 public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1064 {
1065 collection_contents_changed_listeners.remove(listener);
1066 }
1067
1068
1069 public void removeMetadataSet(MetadataSet metadata_set)
1070 {
1071 System.err.println("Removing metadata set...");
1072
1073 // Delete the .mds file from the collection's "metadata" folder...
1074 File metadata_set_file = metadata_set.getMetadataSetFile();
1075
1076 // ...but not if it is the "ex.mds" file
1077 if (metadata_set_file.getName().equals("ex.mds")) {
1078 return;
1079 }
1080
1081 // ...and only if it exists
1082 if (metadata_set_file.exists()) {
1083 metadata_set_file.delete();
1084
1085 // Unload it from the MetadataSetManager
1086 MetadataSetManager.unloadMetadataSet(metadata_set);
1087 }
1088 }
1089
1090
1091 /** Used to check whether all open collections have a 'saved' state.
1092 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1093 * @see org.greenstone.gatherer.collection.Collection
1094 */
1095 public boolean saved() {
1096 boolean result = true;
1097 if(collection != null) {
1098 result = collection.getSaved();
1099 }
1100 return result;
1101 }
1102
1103
1104 /** Saves the currently loaded collection. */
1105 public void saveCollection()
1106 {
1107 DebugStream.println("Saving collection " + collection.getName() + "...");
1108
1109 // Change cursor to hourglass
1110 Gatherer.g_man.wait(true);
1111
1112 // Create a backup of the collection file, just in case anything goes wrong
1113 File collection_file = new File(getCollectionFilePath());
1114 if (collection_file.exists()) {
1115 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1116 if (!collection_file.renameTo(collection_file_backup)) {
1117 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1118 }
1119 collection_file_backup.deleteOnExit();
1120 }
1121
1122 // Write out the collection file
1123 collection.save();
1124
1125 // Write out the collection configuration file
1126 Gatherer.g_man.design_pane.saveConfiguration();
1127
1128 // Change cursor back to normal
1129 Gatherer.g_man.wait(false);
1130 }
1131
1132
1133 /** 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 should be added back in and finished. [kjdon] */
1134 private boolean addSomeMetadataSets(File collection_dir) {
1135
1136
1137 ExternalCollectionPrompt external_prompt = new ExternalCollectionPrompt();
1138 int meta_choice = external_prompt.getMetadataChoice();
1139 boolean cancelled = external_prompt.isCancelled();
1140 if (cancelled) {
1141 return false;
1142 }
1143
1144 // now we reuse the newcoll metadata prompt for the user to select metadata sets
1145 NewCollectionMetadataPrompt ncm_prompt = new NewCollectionMetadataPrompt(true);
1146 if (ncm_prompt.isCancelled()) {
1147 return false;
1148 }
1149 ArrayList metadata_sets = ncm_prompt.getSets();
1150 // Import default metadata sets if any.
1151 for(int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
1152 importMetadataSet((MetadataSet) metadata_sets.get(i));
1153 }
1154
1155 // Always import the extracted metadata set
1156 File extracted_metadata_set_file = new File(Utility.METADATA_DIR + Utility.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1157 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1158
1159 return true;
1160 }
1161
1162
1163 // used as arg in the perl scripts
1164 private String getCollectDirectory() {
1165 String collect_dir = Gatherer.getCollectDirectoryPath();
1166
1167 // Remove erroneous file windows file separator as it causes problems when running import.pl
1168 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1169 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1170 }
1171
1172 return collect_dir;
1173 }
1174
1175
1176 /** Install collection by moving its files from building to index after a successful build.
1177 * @see org.greenstone.gatherer.Gatherer
1178 * @see org.greenstone.gatherer.util.Utility
1179 */
1180 private boolean installCollection()
1181 {
1182 DebugStream.println("Build complete. Moving files.");
1183
1184 try {
1185 // Ensure that the local library has released this collection so we can delete the index directory
1186 if (LocalLibraryServer.isRunning() == true) {
1187 LocalLibraryServer.releaseCollection(collection.getName());
1188 }
1189 // deactivate it in tomcat so that windows will release the index files
1190 if (Gatherer.GS3) {
1191 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection.getName());
1192 }
1193 File index_dir = new File(getCollectionIndexDirectoryPath());
1194 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1195
1196 File building_dir = new File(getCollectionBuildingDirectoryPath());
1197 DebugStream.println("Building = " + building_dir.getAbsolutePath());
1198
1199 // Get the build mode from the build options
1200 String build_mode = collection.build_options.getValue("mode");
1201
1202 // Special case for build mode "all": replace index dir with building dir
1203 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1204 // Remove the old index directory
1205 if (index_dir.exists()) {
1206 Utility.delete(index_dir);
1207
1208 // Wait for a couple of seconds, just for luck
1209 wait(2000);
1210
1211 // Check the delete worked
1212 if (index_dir.exists()) {
1213 throw new Exception("Index directory could not be removed.");
1214 }
1215 }
1216
1217 // Move the building directory to become the new index directory
1218 if (building_dir.renameTo(index_dir) == false) {
1219 throw new Exception("Build directory could not be moved.");
1220 }
1221 }
1222
1223 // Otherwise copy everything in the building dir into the index dir
1224 else {
1225 moveContentsInto(building_dir, index_dir);
1226 }
1227 }
1228 catch (Exception exception) {
1229 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);
1230 return false;
1231 }
1232 return true;
1233 }
1234
1235
1236 /** Moves all the files in one directory into another, overwriting existing files */
1237 private void moveContentsInto(File source_directory, File target_directory)
1238 {
1239 File[] source_files = source_directory.listFiles();
1240 for (int i = 0; i < source_files.length; i++) {
1241 File source_file = source_files[i];
1242 File target_file = new File(target_directory, source_file.getName());
1243
1244 if (source_file.isDirectory()) {
1245 moveContentsInto(source_file, target_file);
1246 source_file.delete();
1247 }
1248 else {
1249 if (target_file.exists()) {
1250 target_file.delete();
1251 }
1252
1253 source_file.renameTo(target_file);
1254 }
1255 }
1256 }
1257
1258
1259 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
1260 {
1261 boolean first_name = true;
1262 boolean first_extra = true;
1263 String collection_path = (base_cfg.getParentFile().getParentFile()).getAbsolutePath();
1264
1265 // 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.
1266 try {
1267 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
1268 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
1269 String command = null;
1270 while((command = in.readLine()) != null) {
1271 if (command.length()==0) {
1272 // output a new line
1273 out.newLine();
1274 continue;
1275 }
1276 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
1277 while(command.trim().endsWith("\\")) {
1278 command = command.substring(0, command.lastIndexOf("\\"));
1279 String next_line = in.readLine();
1280 if(next_line != null) {
1281 command = command + next_line;
1282 }
1283 }
1284 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
1285 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
1286 String command_type_str = tokenizer.nextToken().toLowerCase();
1287
1288 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
1289 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
1290 StringBuffer new_command = new StringBuffer(command_type_str);
1291 String meta_name = tokenizer.nextToken();
1292 new_command.append(' ');
1293 new_command.append(meta_name);
1294 while (tokenizer.hasMoreTokens()) {
1295 new_command.append(' ');
1296 new_command.append(tokenizer.nextToken());
1297 }
1298 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)) {
1299 // dont save
1300 } else {
1301 write(out, new_command.toString());
1302 }
1303 new_command = null;
1304 continue;
1305 } // if collectionmeta
1306
1307 if(command_type_str.equals(Utility.CFG_CLASSIFY)) {
1308 StringBuffer text = new StringBuffer(command_type_str);
1309 // Read in the classifier command watching for hfile, metadata and sort arguments.
1310 String buttonname = null;
1311 String hfile = null;
1312 String new_metadata = null;
1313 String old_metadata = null;
1314
1315 while(tokenizer.hasMoreTokens()) {
1316 String token = tokenizer.nextToken();
1317 if(token.equals(Utility.CFG_CLASSIFY_HFILE)) {
1318 if(tokenizer.hasMoreTokens()) {
1319 text.append(" ");
1320 text.append(token);
1321 token = tokenizer.nextToken();
1322 hfile = token;
1323 }
1324 }
1325 else if(token.equals(Utility.CFG_CLASSIFY_METADATA)) {
1326 if(tokenizer.hasMoreTokens()) {
1327 text.append(" ");
1328 text.append(token);
1329 String temp_metadata = tokenizer.nextToken();
1330 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
1331 if (replacement != null && !replacement.equals("")) {
1332 token = replacement;
1333 old_metadata = temp_metadata;
1334 new_metadata = replacement;
1335 }
1336 else {
1337 token = temp_metadata;
1338 }
1339 temp_metadata = null;
1340 replacement = null;
1341 }
1342 }
1343 else if(token.equals(Utility.CFG_CLASSIFY_SORT)) {
1344 if(tokenizer.hasMoreTokens()) {
1345 text.append(" ");
1346 text.append(token);
1347 String temp_metadata = tokenizer.nextToken();
1348 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
1349 if (replacement != null && !replacement.equals("")) {
1350 token = replacement;
1351 }
1352 else {
1353 token = temp_metadata;
1354 }
1355 temp_metadata = null;
1356 replacement = null;
1357 }
1358 }
1359 else if(token.equals(Utility.CFG_CLASSIFY_BUTTONNAME)) {
1360 buttonname = token;
1361 }
1362 text.append(' ');
1363 text.append(token);
1364 token = null;
1365 }
1366
1367 // 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)!
1368 if(old_metadata != null && new_metadata != null && buttonname == null) {
1369 text.append(' ');
1370 text.append(Utility.CFG_CLASSIFY_BUTTONNAME);
1371 text.append(' ');
1372 text.append(old_metadata);
1373 }
1374 command = text.toString();
1375 // Replace the hfile if we found it
1376 if(hfile != null && new_metadata != null) {
1377 command = command.replaceAll(hfile, new_metadata + ".txt");
1378 }
1379
1380 buttonname = null;
1381 hfile = null;
1382 new_metadata = null;
1383 old_metadata = null;
1384 write(out, command);
1385 } else {
1386 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
1387 StringBuffer new_command = new StringBuffer(command_type_str);
1388 while (tokenizer.hasMoreTokens()) {
1389 new_command.append(' ');
1390 new_command.append(tokenizer.nextToken());
1391 }
1392
1393 command = new_command.toString();
1394
1395 // 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.
1396 // we really want to build up the whole command here
1397 boolean format_command = command_type_str.equals(Utility.CFG_FORMAT);
1398 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
1399 if (metadata_mapping != null) {
1400 Iterator keys = metadata_mapping.keySet().iterator();
1401 while (keys.hasNext()) {
1402 String target = (String) keys.next();
1403 String replacement = (String) metadata_mapping.get(target);
1404 if (replacement != null && !replacement.equals("")) {
1405 if (format_command) {
1406 target = "\\[" + target + "\\]";
1407 replacement = "{Or}{[" + replacement + "]," + target + "}";
1408 }
1409 command = command.replaceAll(target, replacement);
1410 }
1411 }
1412 }
1413
1414 write(out, command);
1415 }
1416 tokenizer = null;
1417 }
1418 in.close();
1419 in = null;
1420 out.flush();
1421 out.close();
1422 out = null;
1423 }
1424 catch(Exception error) {
1425 DebugStream.printStackTrace(error);
1426 }
1427 // All done, I hope.
1428 }
1429
1430 private void write(BufferedWriter out, String message)
1431 throws Exception {
1432 out.write(message, 0, message.length());
1433 out.newLine();
1434 }
1435
1436
1437 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
1438 private class FMTreeModelListener
1439 implements TreeModelListener {
1440 /** 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.
1441 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1442 */
1443 public void treeNodesChanged(TreeModelEvent event) {
1444 if(collection != null) {
1445 collection.setSaved(false);
1446 collection.setFilesChanged(true);
1447 }
1448 }
1449 /** 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.
1450 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1451 */
1452 public void treeNodesInserted(TreeModelEvent event) {
1453 if(collection != null) {
1454 collection.setSaved(false);
1455 collection.setFilesChanged(true);
1456 }
1457 }
1458 /** 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.
1459 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1460 */
1461 public void treeNodesRemoved(TreeModelEvent event) {
1462 if(collection != null) {
1463 collection.setSaved(false);
1464 collection.setFilesChanged(true);
1465
1466 }
1467 }
1468 /** 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.
1469 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
1470 */
1471 public void treeStructureChanged(TreeModelEvent event) {
1472 if(collection != null) {
1473 collection.setSaved(false);
1474 }
1475 }
1476 }
1477}
Note: See TracBrowser for help on using the repository browser.