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

Last change on this file since 23433 was 23433, checked in by ak19, 13 years ago

GLI now has a gs.FilenameEncoding metadata field which appears like all the others in GLI's EnrichPane, but is unique in that this metadata (once set, changed or removed) must be applied to the affected filenames in the Collection Tree. More importantly, the changes made for this are to allow GLI's java code to interact with the recent changes to Perl where strings were made unicode-aware (for proper regex matching) but which required other changes elsewhere. To still support filenames with different encodings Perl used URL encoded versions of filenames representing characters' code point values in URL encoding. This required that GLI write out URL encoded filenames to the metadata.xml files that are associated with each folder level of a collection, so that Perl can read them. In this way, they can both speak of the same filenames. Only works on unicode 16 (such as latin-1), non-UTF8 systems. The latter is a requirement since Java uses the filesystem encoding from startup. If it is UTF8, non-recognised characters are replaced by the invalid char for UTF8. This process being destructive, we can't get the original filenames' bytecodes back. The changes made to GLI will work on Windows which is UTF-16 (windows codepage 1252), presumably also Macs (some kind of UTF-16) and also works on Native Latin 1 Linux systems. UTF-8 Linux systems need to be reconfigured to Native Latin-1, or if not installed, an administrator can install it easily.

  • Property svn:keywords set to Author Date Id Revision
File size: 88.1 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.collection;
38
39import java.io.*;
40import java.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.cdm.CollectionDesignManager;
50import org.greenstone.gatherer.cdm.CollectionMeta;
51import org.greenstone.gatherer.cdm.CollectionMetaManager;
52import org.greenstone.gatherer.cdm.CommandTokenizer;
53import org.greenstone.gatherer.cdm.BuildTypeManager;
54import org.greenstone.gatherer.cdm.CollectionConfiguration;
55import org.greenstone.gatherer.greenstone.Classifiers;
56import org.greenstone.gatherer.greenstone.LocalGreenstone;
57import org.greenstone.gatherer.greenstone.LocalLibraryServer;
58import org.greenstone.gatherer.greenstone.Plugins;
59import org.greenstone.gatherer.greenstone3.ServletConfiguration;
60import org.greenstone.gatherer.gui.LockFileDialog;
61import org.greenstone.gatherer.gui.ModalProgressPopup;
62import org.greenstone.gatherer.gui.WarningDialog;
63import org.greenstone.gatherer.metadata.DocXMLFileManager;
64import org.greenstone.gatherer.metadata.FilenameEncoding;
65import org.greenstone.gatherer.metadata.MetadataChangedListener;
66import org.greenstone.gatherer.metadata.MetadataSet;
67import org.greenstone.gatherer.metadata.MetadataSetManager;
68import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
69import org.greenstone.gatherer.metadata.ProfileXMLFileManager;
70import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
71import org.greenstone.gatherer.shell.GShell;
72import org.greenstone.gatherer.shell.GShellEvent;
73import org.greenstone.gatherer.shell.GShellListener;
74import org.greenstone.gatherer.shell.GShellProgressMonitor;
75import org.greenstone.gatherer.util.Codec;
76import org.greenstone.gatherer.util.StaticStrings;
77import org.greenstone.gatherer.util.Utility;
78import org.greenstone.gatherer.util.XMLTools;
79import org.w3c.dom.*;
80
81/** 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 responsible for firing appropriate event when significant changes have occured within the collection, and for creating a new metadata set manager as necessary.
82 * @author John Thompson
83 * @version 2.3
84 */
85public class CollectionManager
86 implements GShellListener, MetadataChangedListener
87{
88 /** Are we currently in the process of building? */
89 static private boolean building = false;
90 /** Are we currently in the process of importing? */
91 static private boolean importing = false;
92 /** Are we currently in the process of scheduling? */
93 static private boolean scheduling = false;
94 /** The objects listening for CollectionContentsChanged events. */
95 static private ArrayList collection_contents_changed_listeners = new ArrayList();
96 /** The collection this manager is managing! */
97 static private Collection collection = null;
98 /** The collection tree (used in both Gather and Enrich panes). */
99 static private CollectionTree collection_tree = null;
100 /** The collection tree model. */
101 static private CollectionTreeModel collection_tree_model = null;
102 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
103 static private FMTreeModelListener fm_tree_model_listener = null;
104 /** The monitor responsible for parsing the build process. */
105 static private GShellProgressMonitor build_monitor = null;
106 /** The monitor responsible for parsing the import process. */
107 static private GShellProgressMonitor import_monitor = null;
108 /** The monitor responsible for parsing the scheduler process. */
109 static private GShellProgressMonitor schedule_monitor = null;
110
111 /** The name of the standard lock file. */
112 static final public String LOCK_FILE = "gli.lck";
113
114 /** Used to indicate the source of the message is the file collection methods. */
115 static final public int COLLECT = 3;
116 /** Used to indicate the source of the message is the building methods. */
117 static final public int BUILDING = 5;
118 /** Used to indicate the source of the message is in the scheduling methods...? */
119 static final public int SCHEDULING = 7;
120
121 /** To store the path to the perl scripts. In the case of local Greenstone servers,
122 * this will be the local bin/script folder. */
123 static private String scriptPath = "";
124
125 /** Constructor. */
126 public CollectionManager() {
127 // Initialisation.
128 this.building = false;
129 this.importing = false;
130 this.scheduling = false;
131 this.collection = null;
132
133 MetadataXMLFileManager.addMetadataChangedListener(this);
134
135 // If using a remote Greenstone server, delete the local collect directory because it will be out of date
136 if (Gatherer.isGsdlRemote) {
137 System.err.println("Deleting user's local collect directory...");
138 Utility.delete(new File(Gatherer.getCollectDirectoryPath()));
139 System.err.println("Done.");
140 new File(Gatherer.getCollectDirectoryPath()).mkdirs();
141
142 scriptPath = ""; // remote greenstone: scriptPath will be determined on remote server side
143 }
144 else { // local greenstone case: scripts are inside bin/script
145 scriptPath = LocalGreenstone.getBinScriptDirectoryPath();
146 }
147 }
148
149
150 static public void addCollectionContentsChangedListener(CollectionContentsChangedListener listener)
151 {
152 collection_contents_changed_listeners.add(listener);
153 }
154
155
156 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
157 * @see org.greenstone.gatherer.Configuration
158 * @see org.greenstone.gatherer.Gatherer
159 * @see org.greenstone.gatherer.collection.Collection
160 * @see org.greenstone.gatherer.gui.BuildOptions
161 * @see org.greenstone.gatherer.shell.GShell
162 * @see org.greenstone.gatherer.shell.GShellListener
163 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
164 * @see org.greenstone.gatherer.util.Utility
165 */
166 public void buildCollection()
167 {
168
169 DebugStream.println("In CollectionManager.buildCollection(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
170 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
171 building = true;
172
173 // Generate the buildcol.pl command
174 ArrayList command_parts_list = new ArrayList();
175 if (!Gatherer.isGsdlRemote) {
176 command_parts_list.add(Configuration.perl_path);
177 command_parts_list.add("-S");
178 }
179
180 if (Configuration.fedora_info.isActive()) {
181 command_parts_list.add(scriptPath + "g2f-buildcol.pl");
182
183 command_parts_list.add("-hostname");
184 command_parts_list.add(Configuration.fedora_info.getHostname());
185
186 command_parts_list.add("-port");
187 command_parts_list.add(Configuration.fedora_info.getPort());
188
189 command_parts_list.add("-username");
190 command_parts_list.add(Configuration.fedora_info.getUsername());
191
192 command_parts_list.add("-password");
193 command_parts_list.add(Configuration.fedora_info.getPassword());
194
195 command_parts_list.add("-protocol");
196 command_parts_list.add(Configuration.fedora_info.getProtocol());
197
198 }
199 else {
200
201 if ( !CollectionDesignManager.isCompleteBuild() && CollectionDesignManager.index_manager.isLucene() ) {
202 command_parts_list.add(scriptPath + "incremental-buildcol.pl");
203 CollectionDesignManager.setBuildcolWasFull(false);
204 } else {
205 command_parts_list.add(scriptPath + "full-buildcol.pl");
206 CollectionDesignManager.setBuildcolWasFull(true);
207 }
208 }
209
210 command_parts_list.add("-gli");
211 command_parts_list.add("-language");
212 command_parts_list.add(Configuration.getLanguage());
213 if(!Gatherer.isGsdlRemote) {
214 command_parts_list.add("-collectdir");
215 command_parts_list.add(getCollectDirectory()); // <../collect/>
216 }
217
218 String[] build_options = collection.build_options.getValues();
219 for (int i = 0; i < build_options.length; i++) {
220 command_parts_list.add(build_options[i]);
221 }
222
223 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
224
225 // Run the buildcol.pl and
226 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
227 GShell shell = new GShell(command_parts, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
228 shell.addGShellListener(Gatherer.g_man.create_pane);
229 shell.addGShellListener(Gatherer.g_man.format_pane);
230 shell.start();
231
232 }
233
234 /*probably repeating alot of work, but I want to keep this separate... wendy*/
235 public void scheduleBuild()
236 {
237 DebugStream.println("In CollectionManager.scheduleBuild(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
238 DebugStream.println("Is event dispatch threa: " + SwingUtilities.isEventDispatchThread());
239
240 ArrayList sched_list = new ArrayList();
241 if (!Gatherer.isGsdlRemote) {
242 sched_list.add(Configuration.perl_path);
243 sched_list.add("-S");
244 }
245 sched_list.add(scriptPath + "schedule.pl");
246 sched_list.add("-colname");
247 sched_list.add(collection.getName());
248 sched_list.add("-gli");
249
250 // First, generate the import.pl command, also converting to a string
251 // Generate the import.pl command
252 ArrayList import_list = new ArrayList();
253 if (!Gatherer.isGsdlRemote) {
254 import_list.add(Configuration.perl_path);
255 import_list.add("-S");
256 }
257
258 String cmdPrefix = CollectionDesignManager.isCompleteBuild() ? "full-" : "incremental-";
259 import_list.add(scriptPath + cmdPrefix + "import.pl");
260 import_list.add("-language");
261 import_list.add(Configuration.getLanguage());
262 if(!Gatherer.isGsdlRemote) {
263 import_list.add("-collectdir");
264 import_list.add(getCollectDirectory());
265 }
266
267 String[] import_options = collection.import_options.getValues();
268 int i = 0;
269 for (i = 0; i < import_options.length; i++) {
270 import_list.add(import_options[i]);
271 }
272
273 import_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
274
275 String[] import_parts = (String[]) import_list.toArray(new String[0]);
276 String command = "";
277 i = 0;
278 for (i = 0; i < import_parts.length-1; i++) {
279 command = command + import_parts[i] + " ";
280 }
281 command = command + import_parts[i];
282
283 sched_list.add("-import");
284 sched_list.add("\"" + command + "\"");
285
286 // Generate the buildcol.pl command, also converting to a string
287 ArrayList build_list = new ArrayList();
288
289 // i'm not doing this in schedule.pl right now - should i be?
290 if (!Gatherer.isGsdlRemote) {
291 build_list.add(Configuration.perl_path);
292 build_list.add("-S");
293 }
294
295 String buildType = (new CollectionMeta( CollectionDesignManager.collect_config.getBuildType() )).getValue(CollectionMeta.TEXT);
296 if ( !CollectionDesignManager.isCompleteBuild() && buildType.equals( BuildTypeManager.BUILD_TYPE_LUCENE ) ) {
297 build_list.add(scriptPath + "incremental-buildcol.pl");
298 } else {
299 build_list.add(scriptPath + "full-buildcol.pl");
300 }
301
302 build_list.add("-language");
303 build_list.add(Configuration.getLanguage());
304 if(!Gatherer.isGsdlRemote) {
305 build_list.add("-collectdir");
306 build_list.add(getCollectDirectory());
307 }
308
309 String[] build_options = collection.build_options.getValues();
310 for (i = 0; i < build_options.length; i++) {
311 build_list.add(build_options[i]);
312 }
313
314 build_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
315
316 //build actual string
317 String[] build_parts = (String[]) build_list.toArray(new String[0]);
318 String command2 = "";
319 for(i = 0; i < build_parts.length-1; i++) {
320 command2 = command2 + build_parts[i] + " ";
321 }
322 command2 = command2 + build_parts[i];
323
324 sched_list.add("-build");
325 sched_list.add("\"" + command2 + "\"");
326
327 //next, the scheduling frequency goes here
328 String[] schedule_options = collection.schedule_options.getValues();
329 for(i = 0; i < schedule_options.length; i++) {
330 sched_list.add(schedule_options[i]);
331 }
332
333 //now, hope it will run. ;)
334 String[] sched_parts = (String[]) sched_list.toArray(new String[0]);
335
336 GShell shell = new GShell(sched_parts, GShell.SCHEDULE, SCHEDULING, this, schedule_monitor, GShell.GSHELL_SCHEDULE);
337 shell.addGShellListener(Gatherer.g_man.create_pane);
338 shell.addGShellListener(Gatherer.g_man.format_pane);
339 shell.start();
340 }
341
342 /** Used to determine whether the currently active collection has been built.
343 * @return A boolean indicating the built status of the collection.
344 */
345 public boolean built() {
346 if(collection != null) {
347 // Determine if the collection has been built by looking for the build.cfg (gs2)
348 // buildConfig.xml (gs3) or export.inf (fedora) file
349 String file_name = "";
350
351 if (Configuration.fedora_info != null && Configuration.fedora_info.isActive()) { // FLI case
352 // Fedora build
353 //file_name = getLoadedCollectionArchivesDirectoryPath() + "import.inf";
354 //file_name = getLoadedCollectionExportDirectoryPath() + "export.inf"; // export.pl no longer generates this
355 file_name = getLoadedCollectionExportDirectoryPath() + "archiveinf-doc.gdb";
356 } else {
357 // GLI is running, check if it's greenstone 3 or greenstone 2
358 if (Gatherer.GS3) { // GS3 GLI
359 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CONFIG_XML;
360 }
361 else { // greenstone 2 GLI
362 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CFG;
363 }
364 }
365 File test_file = new File(file_name);
366 return test_file.exists();
367 }
368 return false;
369 }
370
371 /** Used to determine whether the currently active collection has been imported.
372 * @return A boolean indicating the imported status of the collection.
373 */
374 public boolean imported() {
375 if ( collection != null ) {
376 String file_name = getLoadedCollectionDirectoryPath() + "archives";
377 File test_file = new File(file_name);
378 return test_file.exists();
379 }
380 return false;
381 }
382
383 /** 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 */
384 static private boolean canDelete(File file)
385 {
386 if (!file.isDirectory()) {
387 return file.canWrite();
388 }
389 File [] file_list = file.listFiles();
390 for (int i=0; i<file_list.length; i++) {
391 if (!canDelete(file_list[i])) {
392 return false;
393 }
394 }
395 return true;
396 }
397
398
399 /** Called to close the current collection and remove its lock file.
400 * @see org.greenstone.gatherer.Gatherer
401 * @see org.greenstone.gatherer.collection.Collection
402 * @see org.greenstone.gatherer.util.Utility
403 */
404 public void closeCollection() {
405 if (collection == null) {
406 return;
407 }
408 DebugStream.println("Close collection: " + collection.getName());
409
410 // Remove the lock on this file, then remove the collection.
411 File lock_file = new File(getLoadedCollectionDirectoryPath() + LOCK_FILE);
412 lock_file.delete();
413 if (lock_file.exists()) {
414 System.err.println("Warning: Lockfile was not successfully deleted.");
415 }
416
417 // Remove the lock file on the server
418 if (Gatherer.isGsdlRemote) {
419 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), lock_file);
420 }
421
422 // release the current build_log file - else we're unable to delete the last closed collection
423 // until another collection is opened and its opening a new build_log closes this old one
424 Gatherer.g_man.create_pane.options_pane.closeCurrentLogDocument();
425
426 MetadataSetManager.clearMetadataSets();
427 MetadataXMLFileManager.clearMetadataXMLFiles();
428 DocXMLFileManager.clearDocXMLFiles();
429 ProfileXMLFileManager.clearProfileXMLFile();
430
431 collection.destroy();
432 collection = null;
433 collection_tree_model = null;
434 //Configuration.setCollectionConfiguration(null);
435 Gatherer.refresh(Gatherer.COLLECTION_CLOSED);
436 if (Gatherer.g_man != null) {
437 Gatherer.g_man.updateUI(); // !!! Necessary?
438 }
439 }
440
441//This method is no longer used in gs3 since the modification of CollectionConfiguration.java
442// public void convertToGS3Collection() {
443// // Generate the convert_coll_from_gs2.pl command
444// ArrayList command_parts_list = new ArrayList();
445// if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
446// command_parts_list.add(Configuration.perl_path);
447// command_parts_list.add("-S");
448// }
449// command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
450// command_parts_list.add("-collectdir");
451// command_parts_list.add(getCollectDirectory());
452// command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
453//
454// // Run the convert_coll_from_gs2.pl command
455// String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
456// GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
457// process.addGShellListener(this);
458// process.run(); // Don't bother threading this... yet
459//
460// }
461
462 /** When basing a new collection on an existing one, we need to copy
463 * over some extra directories: all except import, archives, building, index
464 * really we just want images, macros, perllib, but there may also be eg style, or other dirs.
465 */
466 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
467 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
468 return false;
469 }
470 DebugStream.println("Copying extra dirs from the base collection");
471
472
473 File subdirs[] = base_coll_dir.listFiles();
474 for (int i = 0; subdirs != null && i < subdirs.length; i++) {
475 File subdir = subdirs[i];
476 if (subdir.isDirectory()) {
477 String dir_name = subdir.getName();
478 // ignore those we don't need, (archives, buildng, index) and
479 // those we are handling in another place (import, etc, metadata)
480 if (dir_name.startsWith ("import") || dir_name.startsWith("archives") || dir_name.startsWith("building") || dir_name.startsWith("index") || dir_name.startsWith("etc") || dir_name.startsWith("metadata") || dir_name.startsWith("log") || dir_name.startsWith("tmp")) {
481 continue;
482 }
483 try {
484 // copy the directory
485 File new_coll_subdir = new File(new_coll_dir, dir_name);
486 new_coll_subdir.mkdirs();
487 Gatherer.f_man.getQueue().copyDirectoryContents(subdir, new_coll_subdir);
488 }
489 catch (Exception e) {
490 DebugStream.println("Couldn't copy over the" + subdir+" dir from the base collection: "+e.toString());
491 }
492 }
493 }
494
495 return true;
496 }
497
498 /** 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 collection 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.
499 * @param description a description of the collection as a String
500 * @param email the email address of the author/maintainer as a String
501 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
502 * @param title the longer title of the collection as a String
503 * @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
504 * @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
505 */
506 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
507 {
508 // Display a modal progress popup to indicate that the collection is being loaded
509 ModalProgressPopup create_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Creating_Collection"), Dictionary.get("CollectionManager.Creating_Collection_Please_Wait"));
510 create_collection_progress_popup.display();
511
512 // Create the collection on a separate thread so the progress bar updates correctly
513 (new CreateCollectionTask(description, email, name, title, base_collection_directory, metadata_sets, create_collection_progress_popup)).start();
514 }
515
516
517 private class CreateCollectionTask
518 extends Thread
519 {
520 private String description = null;
521 private String email = null;
522 private String name = null;
523 private String title = null;
524 private File base_collection_directory = null;
525 private ArrayList metadata_sets = null;
526 private ModalProgressPopup create_collection_progress_popup = null;
527
528 public CreateCollectionTask(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets, ModalProgressPopup create_collection_progress_popup)
529 {
530 this.description = description;
531 this.email = email;
532 this.name = name;
533 this.title = title;
534 this.base_collection_directory = base_collection_directory;
535 this.metadata_sets = metadata_sets;
536 this.create_collection_progress_popup = create_collection_progress_popup;
537 }
538
539 public void run()
540 {
541 createCollectionInternal(description, email, name, title, base_collection_directory, metadata_sets);
542 create_collection_progress_popup.close();
543 }
544 }
545
546
547 private void createCollectionInternal(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
548 {
549 try {
550 // first make sure that the collect directory exists
551 File collect_dir = new File(getDefaultCollectDirectory());
552 if (!collect_dir.exists()) {
553 collect_dir.mkdirs();
554 }
555
556 // Create the new collection
557 makeCollection(name, email);
558
559 // Check that the collection has been created successfully
560 String collection_directory_path = getCollectionDirectoryPath(name);
561 if (!new File(collection_directory_path).exists()) {
562 // If there is no collection directory then the creation was unsuccessful, or cancelled
563
564 return;
565 }
566
567 // Check for the existence of the collection configuration file
568 String file_name = ((Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG);
569 File collect_cfg_file = new File(collection_directory_path + "etc" + File.separator + file_name);
570
571 if (!collect_cfg_file.exists()) {
572 System.err.println("Error: no " + file_name + " file has been created!");
573 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", Dictionary.get("CollectionManager.No_Config_File")), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
574 return;
575 }
576
577 // ACTIVE_DIR/log/
578 File log_dir = new File(collection_directory_path + "log");
579 log_dir.mkdirs();
580
581 // Make sure an import folder exists
582 File collection_import_directory = new File(collection_directory_path + "import");
583 if (!collection_import_directory.exists()) {
584 collection_import_directory.mkdirs();
585 if (Gatherer.isGsdlRemote) {
586 Gatherer.remoteGreenstoneServer.newCollectionDirectory(name, collection_import_directory);
587 }
588 }
589
590 // Now create the collection object around the directory.
591 collection = new Collection(new File(collection_directory_path, "gli.col"));
592
593 // for remote case, scheduling causes an Exception on creating a new collection that
594 // can't be recovered from. For GS3, it doesn't work since it it trying to access etc/main.cfg
595 if (canDoScheduling()) {
596 scheduling();
597 }
598
599 MetadataSetManager.clearMetadataSets();
600 MetadataXMLFileManager.clearMetadataXMLFiles();
601 DocXMLFileManager.clearDocXMLFiles();
602
603 // Import default metadata sets, if any
604 // for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
605 // importMetadataSet((MetadataSet) metadata_sets.get(i));
606 // }
607
608 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_directory_path + "metadata"));
609
610 // Before creating the CollectionDesignManager check if we are basing it upon some other collection
611 if (base_collection_directory != null) {
612 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
613
614 // If we're using a remote Greenstone server, download the collection shell to get the files needed
615 if (Gatherer.isGsdlRemote) {
616 String base_collection_name = base_collection_directory.getName();
617 Gatherer.remoteGreenstoneServer.downloadCollection(base_collection_name);
618 }
619
620 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
621 // copy over other needed directories
622 copyExtraBaseCollStuff(new File(collection_directory_path), base_collection_directory);
623
624 // Try to import any existing metadata sets for this collection
625 // Look in base_collection_directory/metadata and import any metadata sets found.
626 File base_metadata_directory = new File(base_collection_directory, "metadata");
627 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
628 if (base_metadata_sets != null) {
629 for (int i = 0; i < base_metadata_sets.size(); i++) {
630 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
631 }
632 }
633 else {
634 DebugStream.println("This base collection has no metadata directory.");
635 }
636
637 // Now we update our collect.cfg
638 DebugStream.println("Copy and update " + file_name + " from base collection.");
639
640 if (Gatherer.GS3 == true) {
641 updateCollectionConfigXML(new File(base_collection_directory, Utility.CONFIG_GS3_FILE),
642 new File(collection_directory_path, Utility.CONFIG_GS3_FILE));
643 } else {
644 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE),
645 new File(collection_directory_path, Utility.CONFIG_FILE),
646 description, email, title);
647 }
648 }
649 else {
650 // only load metadata sets here if we have not based the collection on any other.
651 // Load the default metadata sets
652 addDefaultMetadataSets();
653
654 // Make sure we always have the extracted metadata set
655 addRequiredMetadataSets();
656 }
657
658 collection.cdm = new CollectionDesignManager(new File(getLoadedCollectionCfgFilePath()));
659
660 // We always set title and description here rather than calling mkcol.pl with Unicode arguments
661 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
662 collection_name_collectionmeta.setValue(title);
663 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
664 collection_extra_collectionmeta.setValue(description);
665
666 // 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. This update is done to the internal xml structure which may be saved into collect.cfg or collectionConfig.xml accordingly.
667 if (base_collection_directory != null) {
668 // Update the creator and maintainer
669 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
670 creator_collectionmeta.setValue(email);
671 creator_collectionmeta = null;
672 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
673 maintainer_collectionmeta.setValue(email);
674 maintainer_collectionmeta = null;
675
676 // All collections based on others are automatically public
677 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
678 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
679 public_collectionmeta = null;
680
681 // Finally reset the icons
682 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
683 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
684 icon_collection_collectionmeta = null;
685 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
686 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
687 icon_collection_small_collectionmeta = null;
688 }
689
690 saveCollection();
691
692 // Create a lock file
693 createLockFile(new File(collection_directory_path, LOCK_FILE));
694
695 // We're done. Let everyone know.
696 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
697 }
698 catch (Exception error) {
699 DebugStream.printStackTrace(error);
700 }
701 }
702
703 private void scheduling()
704 throws Exception
705 {
706 //try to obtain email address of collection owner if it exists...
707 String stmp = Configuration.getEmail();
708 if(stmp != null) {
709 collection.schedule_options.setValue("toaddr", false, Configuration.getEmail());
710 }
711
712 //The next few items deal with updating the SMTP server, and the to: and from: addresses
713 //from main.cfg and the collection configuration. if no changes are made, or the
714 //values are result to NULL, any existing values are kept.
715
716 //try to obtain email address of Greenstone installation webmaster for - used to indicate "sender".
717 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
718 BufferedReader maincfg = new BufferedReader(new FileReader(mcfg));
719 stmp = "";
720 String fromaddr = "";
721 while((stmp = maincfg.readLine()) != null) {
722 if(stmp.startsWith("maintainer")) {
723 fromaddr = stmp.substring(10); //length of MailServer
724 fromaddr = fromaddr.trim();
725 break;
726 }
727 }
728 maincfg.close();
729 if(!fromaddr.equals("NULL") && !fromaddr.equals("null")) {
730 collection.schedule_options.setValue("fromaddr", false, fromaddr);
731 }
732
733 //try to obtain an smtp server address from main.cfg. If that fails,
734 //try mail.server if an email address exists. If that fails,
735 //maybe a message to set attribute in main.cfg?
736 //i'm pretty sure there exists functionality to do this, but
737 //i'll finish this faster if I just wrote it
738
739
740 maincfg = new BufferedReader(new FileReader(mcfg));
741 String smtptmp = "NULL";
742 while((stmp = maincfg.readLine()) != null) {
743 if(stmp.startsWith("MailServer")) {
744 smtptmp = stmp.substring(10); //length of MailServer
745 smtptmp = smtptmp.trim();
746 break;
747 }
748 }
749 maincfg.close();
750
751 //try if lookup fails
752 if(smtptmp.equals("NULL") || smtptmp.equals("null")) {
753 String email2=fromaddr;
754 if(!email2.equals("NULL") && !email2.equals("null")) {
755 int loc = email2.indexOf('@');
756 email2 = email2.substring(loc+1);
757 smtptmp = "mail."+email2;
758 }
759 }
760 if(!smtptmp.equals("NULL") && !smtptmp.equals("null")) {
761 collection.schedule_options.setValue("smtp", false, smtptmp);
762 }
763
764 }
765
766
767 private void createLockFile(File lock_file)
768 {
769 try {
770 Document default_lockfile = XMLTools.parseXMLFile("xml/" + LOCK_FILE, true);
771 String user_name = System.getProperty("user.name");
772 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
773 person_element.appendChild(default_lockfile.createTextNode(user_name));
774 person_element = null;
775 user_name = null;
776 String machine_name = Utility.getMachineName();
777 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
778 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
779 machine_element = null;
780 machine_name = null;
781 String date_time = Utility.getDateString();
782 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
783 date_element.appendChild(default_lockfile.createTextNode(date_time));
784 date_element = null;
785 date_time = null;
786 XMLTools.writeXMLFile(lock_file, default_lockfile);
787 }
788 catch (Exception exception) {
789 DebugStream.printStackTrace(exception);
790 }
791 }
792
793
794 public boolean deleteCollection(String collection_name)
795 {
796 // First we must release the collection from the local library, if it's running
797 if (LocalLibraryServer.isRunning() == true) {
798 LocalLibraryServer.releaseCollection(collection_name);
799 }
800
801 // Delete the collection on the server if we're using a remote Greenstone
802 if (Gatherer.isGsdlRemote) {
803 Gatherer.remoteGreenstoneServer.deleteCollection(collection_name);
804 }
805
806 // if Greenstone3, need to deactivate the collection on the server
807 if (Gatherer.GS3) {
808 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection_name);
809 }
810
811 // Now delete the collection directory
812 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
813 }
814
815
816 public void fireFileAddedToCollection(File file)
817 {
818 // Send the event off to all the CollectionContentsChangedListeners
819 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
820 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
821 }
822 }
823
824
825 /** Retrieve the current collection.
826 * @return The <strong>Collection</strong> itself.
827 */
828 public Collection getCollection() {
829 return collection;
830 }
831
832
833 /** Returns the absolute filename of the specified collection's directory.
834 */
835 static public String getCollectionDirectoryPath(String collection_name)
836 {
837 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
838 }
839
840
841 /** Returns the absolute filename of the loaded collection's archives directory.
842 */
843 static public String getLoadedCollectionArchivesDirectoryPath()
844 {
845 return getLoadedCollectionDirectoryPath() + "archives" + File.separator;
846 }
847
848 /** Returns the absolute filename of the loaded collection's export directory.
849 */
850 static public String getLoadedCollectionExportDirectoryPath()
851 {
852 return getLoadedCollectionDirectoryPath() + "export" + File.separator;
853 }
854
855
856
857 /** Returns the absolute filename of the loaded collection's building directory.
858 */
859 static public String getLoadedCollectionBuildingDirectoryPath()
860 {
861 return getLoadedCollectionDirectoryPath() + "building" + File.separator;
862 }
863
864
865 /** Returns the absolute filename of the loaded collection's collect.cfg file.
866 */
867 static public String getLoadedCollectionCfgFilePath()
868 {
869 String path = (Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG;
870 return getLoadedCollectionEtcDirectoryPath() + path;
871 }
872
873
874 /** Returns the absolute filename of the loaded collection's directory.
875 */
876 static public String getLoadedCollectionDirectoryPath()
877 {
878 return collection.getCollectionDirectory().getPath() + File.separator;
879 }
880
881
882 /** Returns the absolute filename of the loaded collection's etc directory.
883 */
884 static public String getLoadedCollectionEtcDirectoryPath()
885 {
886 return getLoadedCollectionDirectoryPath() + "etc" + File.separator;
887 }
888
889
890 /** Returns the absolute filename of the loaded collection's .col file.
891 */
892 static public String getLoadedCollectionColFilePath()
893 {
894 return getLoadedCollectionDirectoryPath() + "gli.col";
895 }
896
897
898 /** Returns the absolute filename of the loaded collection's images directory.
899 */
900 static public String getLoadedCollectionImagesDirectoryPath()
901 {
902 return getLoadedCollectionDirectoryPath() + "images" + File.separator;
903 }
904
905
906 /** Returns the absolute filename of the loaded collection's import directory.
907 */
908 static public String getLoadedCollectionImportDirectoryPath()
909 {
910 return getLoadedCollectionDirectoryPath() + "import" + File.separator;
911 }
912
913
914 /** Returns the absolute filename of the loaded collection's index directory.
915 */
916 static public String getLoadedCollectionIndexDirectoryPath()
917 {
918 return getLoadedCollectionDirectoryPath() + "index" + File.separator;
919 }
920
921
922 /** Returns the absolute filename of the loaded collection's log directory.
923 */
924 static public String getLoadedCollectionLogDirectoryPath()
925 {
926 return getLoadedCollectionDirectoryPath() + "log" + File.separator;
927 }
928
929
930 /** Returns the absolute filename of the loaded collection's macros directory.
931 */
932 static public String getLoadedCollectionMacrosDirectoryPath()
933 {
934 return getLoadedCollectionDirectoryPath() + "macros" + File.separator;
935 }
936
937
938 /** Returns the absolute filename of the loaded collection's metadata directory.
939 */
940 static public String getLoadedCollectionMetadataDirectoryPath()
941 {
942 return getLoadedCollectionDirectoryPath() + "metadata" + File.separator;
943 }
944
945
946 /** Returns the (group-qualified) name of the loaded collection with
947 * OS-dependent file separator.
948 */
949 static public String getLoadedCollectionName()
950 {
951 return CollectionManager.getLoadedCollectionName(false);
952 }
953
954 /** Returns the (group-qualified) name of the loaded collection with
955 * OS-dependent space separator.
956 * @url true if url-type forward slashes, false if OS-dependent filesystem slashes.
957 */
958 static public String getLoadedCollectionName(boolean url)
959 {
960 if (collection != null) {
961 //return collection.getName();
962 return collection.getGroupQualifiedName(url);
963 }
964
965 return null;
966 }
967
968 /** @return the subname of any collection (stripped of any collection-group). */
969 static public String getLoadedCollectionTailName()
970 {
971 if (collection != null) {
972 return collection.getCollectionTailName();
973 }
974
975 return null;
976 }
977
978 /** Returns the "collectionGroupName/collectionName" or just the collectionName
979 * depending on whether the collection is part of a collection group or not.
980 * If url = true, then returns the sub-path as a URL (containing / only),
981 * and if url = false, then the sub-path is returned in filepath form
982 * (\ or /, depending on the OS).
983 */
984 static public String getLoadedGroupQualifiedCollectionName(boolean url)
985 {
986 if (collection != null) {
987 return collection.getGroupQualifiedName(url);
988 }
989
990 return null;
991 }
992
993 public CollectionTree getCollectionTree()
994 {
995 if (collection_tree == null) {
996 collection_tree = new CollectionTree(collection_tree_model, true);
997 }
998
999 return collection_tree;
1000 }
1001
1002
1003 /** Retrieve the tree model associated with the current collection. */
1004 public CollectionTreeModel getCollectionTreeModel()
1005 {
1006 if (collection_tree_model == null && collection != null) {
1007 // Use the import directory to generate a new CollectionTreeModel
1008 collection_tree_model = new CollectionTreeModel(new CollectionTreeNode(new File(getLoadedCollectionImportDirectoryPath())));
1009 // Ensure that the manager is a change listener for the tree.
1010 if (fm_tree_model_listener == null) {
1011 fm_tree_model_listener = new FMTreeModelListener();
1012 }
1013 collection_tree_model.addTreeModelListener(fm_tree_model_listener);
1014 }
1015 return collection_tree_model;
1016 }
1017
1018
1019 /** This method when called, creates a new GShell in order to run the import.pl script.
1020 * @see org.greenstone.gatherer.Configuration
1021 * @see org.greenstone.gatherer.Gatherer
1022 * @see org.greenstone.gatherer.gui.BuildOptions
1023 * @see org.greenstone.gatherer.shell.GShell
1024 * @see org.greenstone.gatherer.shell.GShellListener
1025 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
1026 * @see org.greenstone.gatherer.util.Utility
1027 */
1028 public void importCollection() {
1029 importing = true;
1030
1031 if (!saved()) {
1032 DebugStream.println("CollectionManager.importCollection().forcesave");
1033 import_monitor.saving();
1034 saveCollection();
1035 }
1036
1037 DebugStream.println("CollectionManager.importCollection()");
1038 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
1039 //check that we can remove the old index before starting import
1040 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1041 if (index_dir.exists()) {
1042 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
1043 if (!canDelete(index_dir)) {
1044 // tell the user
1045 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1046 // tell the gui manager
1047 // a message for the building log
1048 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
1049 Gatherer.g_man.create_pane.message(event);
1050 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
1051 Gatherer.g_man.create_pane.processComplete(event);
1052 importing = false;
1053 return;
1054 }
1055 }
1056
1057 // Generate the import.pl command
1058 ArrayList command_parts_list = new ArrayList();
1059 if (!Gatherer.isGsdlRemote) {
1060 command_parts_list.add(Configuration.perl_path);
1061 command_parts_list.add("-S");
1062 }
1063
1064 if (Configuration.fedora_info.isActive()) {
1065 command_parts_list.add(scriptPath + "g2f-import.pl");
1066
1067 command_parts_list.add("-hostname");
1068 command_parts_list.add(Configuration.fedora_info.getHostname());
1069
1070 command_parts_list.add("-port");
1071 command_parts_list.add(Configuration.fedora_info.getPort());
1072
1073 command_parts_list.add("-username");
1074 command_parts_list.add(Configuration.fedora_info.getUsername());
1075
1076 command_parts_list.add("-password");
1077 command_parts_list.add(Configuration.fedora_info.getPassword());
1078
1079 command_parts_list.add("-protocol");
1080 command_parts_list.add(Configuration.fedora_info.getProtocol());
1081 }
1082 else {
1083 String cmdPrefix = null;
1084 if ( CollectionDesignManager.isCompleteBuild() ) {
1085 cmdPrefix = "full-";
1086 CollectionDesignManager.setImportWasFull( true );
1087 } else {
1088 cmdPrefix = "incremental-";
1089 CollectionDesignManager.setImportWasFull( false );
1090 }
1091 command_parts_list.add(scriptPath + cmdPrefix + "import.pl"); // scriptPath already set according to local or remote case
1092 }
1093
1094 command_parts_list.add("-gli");
1095 command_parts_list.add("-language");
1096 command_parts_list.add(Configuration.getLanguage());
1097 if(!Gatherer.isGsdlRemote) {
1098 command_parts_list.add("-collectdir");
1099 command_parts_list.add(getCollectDirectory());
1100 }
1101
1102 String[] import_options = collection.import_options.getValues();
1103 for (int i = 0; i < import_options.length; i++) {
1104 System.err.println( "Tacking on option: " + import_options[i] );
1105 command_parts_list.add(import_options[i]);
1106 }
1107
1108 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
1109
1110 // Run the import.pl command
1111 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1112 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
1113 //shell.setEventProperty("is_incremental", Boolean.toString(is_incremental));
1114 shell.addGShellListener(Gatherer.g_man.create_pane);
1115 shell.addGShellListener(Gatherer.g_man.format_pane);
1116 shell.start();
1117 DebugStream.println("CollectionManager.importCollection().return");
1118
1119 importing = false;
1120 }
1121
1122
1123 public void importMetadataSet(MetadataSet external_metadata_set)
1124 {
1125 // Copy the .mds file into the collection's "metadata" folder...
1126 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
1127
1128 // ...but not if it is the redundant "hidden.mds" file
1129 if (external_metadata_set_file.getName().equals("hidden.mds")) {
1130 return;
1131 }
1132
1133 // ...and only if it doesn't already exist
1134 File metadata_set_file = new File(getLoadedCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
1135 if (!metadata_set_file.exists()) {
1136 try {
1137 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, false);
1138
1139 // If we're using a remote Greenstone server, upload the metadata file
1140 if (Gatherer.isGsdlRemote) {
1141 Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1142 }
1143 }
1144 catch (Exception exception) {
1145 DebugStream.printStackTrace(exception);
1146 }
1147
1148 // Load it into the MetadataSetManager
1149 MetadataSetManager.loadMetadataSet(metadata_set_file);
1150 }
1151 }
1152
1153
1154 /** 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.
1155 * @return true if the gli is currently importing
1156 */
1157 public boolean isImporting() {
1158 return importing;
1159 }
1160
1161
1162 public void loadCollection(String collection_file_path)
1163 {
1164 // Display a modal progress popup to indicate that the collection is being loaded
1165 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
1166 load_collection_progress_popup.display();
1167
1168 // Load the collection on a separate thread so the progress bar updates correctly
1169 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
1170 }
1171
1172
1173 private class LoadCollectionTask
1174 extends Thread
1175 {
1176 private String collection_file_path = null;
1177 private ModalProgressPopup load_collection_progress_popup = null;
1178
1179 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
1180 {
1181 this.collection_file_path = collection_file_path;
1182 this.load_collection_progress_popup = load_collection_progress_popup;
1183 }
1184
1185 public void run()
1186 {
1187 loadCollectionInternal(collection_file_path);
1188 load_collection_progress_popup.close();
1189 Gatherer.setMenuBarEnabled(true);
1190 }
1191 }
1192
1193
1194 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
1195 * @param location The path to the collection as a <strong>String</strong>.
1196 * @see org.greenstone.gatherer.Configuration
1197 * @see org.greenstone.gatherer.Gatherer
1198 * @see org.greenstone.gatherer.collection.Collection
1199 * @see org.greenstone.gatherer.util.Utility
1200 */
1201 private void loadCollectionInternal(String location)
1202 {
1203 DebugStream.println("Loading collection " + location + "...");
1204
1205
1206 // Check we have actually been given a .col file.
1207 if (!location.endsWith(".col")) {
1208 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1209 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
1210 return;
1211 }
1212
1213 // Check that the collection configuration file is available
1214 File collection_file = new File(location);
1215
1216 //String collection_name = collection_directory.getName();
1217 String collection_name = "";
1218 File collection_directory = collection_file.getParentFile();
1219
1220 // To get colname = (colgroup/)coltailname, subtract Gatherer.getCollectDirectoryPath() from collection_directory:
1221 int index = collection_directory.getAbsolutePath().indexOf(Gatherer.getCollectDirectoryPath());
1222 if(index == -1) {
1223 System.err.println("*** ERROR: collection directory " + collection_directory + " is not located in collect folder: " + Gatherer.getCollectDirectoryPath());
1224 } else {
1225 index += Gatherer.getCollectDirectoryPath().length();
1226 collection_name = collection_directory.getAbsolutePath().substring(index);
1227 }
1228
1229 if (Gatherer.isGsdlRemote) {
1230 if (Gatherer.remoteGreenstoneServer.downloadCollection(collection_name).equals("")) {
1231 return;
1232 }
1233 }
1234
1235 // Ensure that the collection directory exists
1236 if (collection_directory == null || !collection_directory.exists()) {
1237 // We can't open this
1238 System.err.println("CollectionManager.loadCollection: No collection directory.");
1239 return;
1240 }
1241
1242 String file_str = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
1243 File collection_config_file = new File(collection_directory, file_str);
1244 if (!collection_config_file.exists()) {
1245 System.err.println("CollectionManager.loadCollection: No config file.");
1246 collection_directory = null;
1247 collection_config_file = null;
1248 return;
1249 }
1250
1251 // Ensure that an import directory exists for this collection
1252 File collection_import_directory = new File(collection_directory, "import");
1253 if (!collection_import_directory.exists()) {
1254 collection_import_directory.mkdir();
1255 }
1256
1257 // Special case of a user trying to open an old greenstone collection.
1258 boolean non_gli_collection = false;
1259 File collection_metadata_directory = new File(collection_directory, "metadata");
1260 if (!collection_metadata_directory.exists()) {
1261 DebugStream.println("Loading non-gatherer collection...");
1262 // Show a warning message in case user wants to quit now
1263 non_gli_collection = true;
1264 WarningDialog legacy_dialog = new WarningDialog("warning.LegacyCollection", Dictionary.get("LegacyCollection.Title"), Dictionary.get("LegacyCollection.Message"), null, true);
1265 if (legacy_dialog.display()==JOptionPane.CANCEL_OPTION) {
1266 legacy_dialog.dispose();
1267 collection_directory = null;
1268 collection_config_file = null;
1269 return;
1270 }
1271 legacy_dialog.dispose();
1272
1273 }
1274
1275 // Now determine if a lock already exists on this collection.
1276 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
1277 if (lock_file.exists()) {
1278 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, collection_name, lock_file);
1279 int choice = dialog.getChoice();
1280 dialog.dispose();
1281 dialog = null;
1282
1283 if (choice != LockFileDialog.YES_OPTION) {
1284 // user has cancelled
1285 lock_file = null;
1286 collection_directory = null;
1287 collection_config_file = null;
1288 return;
1289 }
1290
1291 lock_file.delete();
1292 }
1293
1294 // now we are using gli.col - old colls may have used the collection name
1295 if (!collection_file.exists()) {
1296 File old_coll_file = new File(collection_directory, collection_name+".col");
1297 if (old_coll_file.exists()) {
1298 try {
1299 old_coll_file.renameTo(collection_file);
1300 } catch (Exception e) {
1301 DebugStream.println("Couldn't rename "+old_coll_file.getName()+" to gli.col. Will just carry on with default gli.col");
1302 // but just carry on.
1303 }
1304 }
1305 }
1306
1307 try {
1308 // Create a lock file.
1309 createLockFile(lock_file);
1310 // This lock file may not have been created so check
1311 if(!lock_file.canWrite()) {
1312 // The lock file cannot be written to. Most likely cause incorrect file permissions.
1313 System.err.println("Cannot write lock file!");
1314 String args[] = new String[2];
1315 args[0] = location;
1316 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{lock_file.getAbsolutePath()});
1317 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS")!=-1){
1318 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1319 args[1] += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1320 //}
1321 }
1322 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1323 args = null;
1324 return;
1325 }
1326
1327 // need to fix this up as currently it craps out if the .col file is not there, which is may not always be.
1328 if (canDoScheduling() && collection_file.exists()) {
1329 //THIS LOOKS LIKE THE BEST PLACE TO TRY AND UPDATE .col FILES FOR EXISTING COLLECTIONS...Wendy
1330 // Don't need to update anything if collection_file doesn't exist yet.
1331 //First, see if "Schedule" exists in the XMl File...
1332 BufferedReader bir = new BufferedReader(new FileReader(collection_file));
1333 boolean flag = false;
1334 try {
1335 String stmp = new String();
1336
1337 while((stmp = bir.readLine()) != null) {
1338 stmp = stmp.trim();
1339 if(stmp.equals("<Schedule>") || stmp.equals("<Schedule/>")) {
1340 flag = true;
1341 break;
1342 }
1343 }
1344 bir.close();
1345
1346 } catch (IOException ioe) {
1347 DebugStream.printStackTrace(ioe);
1348 }
1349
1350 //modify if old .col (i.e. no Schedule exists in XML file)
1351 if(!flag) {
1352 File new_collection_file = new File(collection_directory.getAbsolutePath() + "/tmp.col");
1353
1354
1355 BufferedWriter bor = new BufferedWriter(new FileWriter(new_collection_file));
1356 bir = new BufferedReader(new FileReader(collection_file));
1357
1358 try {
1359 String stmp = new String();
1360 while((stmp = bir.readLine()) != null) {
1361 String stmp2 = stmp.trim();
1362 if(stmp2.startsWith("<!ELEMENT Argument")) {
1363 bor.write(" <!ELEMENT Schedule (Arguments*)>\n");
1364 }
1365 else if(stmp2.equals("</BuildConfig>")) {
1366 bor.write(" <Schedule/>\n");
1367 }
1368
1369 bor.write(stmp + "\n");
1370
1371 }
1372 bir.close();
1373 bor.close();
1374 } catch (IOException ioe) {
1375 DebugStream.printStackTrace(ioe);
1376 }
1377
1378 //copy over tmp.col to replace
1379 try {
1380 collection_file.delete();
1381 new_collection_file.renameTo(collection_file);
1382 } catch (Exception e) {
1383 DebugStream.printStackTrace(e);
1384 }
1385 }
1386 }
1387
1388 // Open the collection file
1389 this.collection = new Collection(collection_file);
1390 if (collection.error) {
1391 collection = null;
1392 // Remove lock file
1393 if (lock_file.exists()) {
1394 lock_file.delete();
1395 }
1396 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
1397 }
1398
1399 if (canDoScheduling()) {
1400 scheduling();
1401 }
1402
1403 // These may have been set in the past, but are no longer used
1404 // by GLI
1405 collection.import_options.removeValue("removeold");
1406 collection.import_options.removeValue("keepold");
1407
1408 MetadataSetManager.clearMetadataSets();
1409 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
1410
1411 // Make sure we always have the extracted metadata set
1412 addRequiredMetadataSets();
1413
1414 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
1415
1416 // If this is a non-GLI (legacy) collection, load the default metadata sets
1417 if (non_gli_collection) {
1418 addDefaultMetadataSets();
1419
1420 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
1421 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
1422 }
1423
1424 // Read through the metadata.xml files in the import directory, building up the metadata value trees
1425 MetadataXMLFileManager.clearMetadataXMLFiles();
1426 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory,collection.toSkimFile());
1427
1428
1429 // get rid of the previous scan through docxml files
1430 DocXMLFileManager.clearDocXMLFiles();
1431
1432 if (Configuration.fedora_info.isActive()) { // FLI case
1433 // Read through the docmets.xml files in the export directory
1434 File collection_export_directory = new File(getLoadedCollectionExportDirectoryPath());
1435 DocXMLFileManager.loadDocXMLFiles(collection_export_directory,"docmets.xml");
1436 }
1437 else {
1438 // Read through the doc.xml files in the archives directory
1439 File collection_archives_directory = new File(getLoadedCollectionArchivesDirectoryPath());
1440 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory,"doc.xml");
1441 }
1442
1443
1444 // Get a list of the collection specific classifiers and plugins
1445 Classifiers.loadClassifiersList(collection_name);
1446 Plugins.loadPluginsList(collection_name);
1447
1448 collection.cdm = new CollectionDesignManager(collection_config_file);
1449 if (non_gli_collection) {
1450 // Change the classifiers to use the namespaced element names
1451 LegacyCollectionImporter.updateClassifiers(collection.cdm);
1452 }
1453
1454 // We're done. Let everyone know.
1455 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", collection_name));
1456 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
1457 }
1458 catch (Exception error) {
1459 // There is obviously no existing collection present.
1460 DebugStream.printStackTrace(error);
1461 error.printStackTrace();
1462 if(error.getMessage() != null) {
1463 String[] args = new String[2];
1464 args[0] = location;
1465 args[1] = error.getMessage();
1466 //args[1] = "The Librarian Interface does not have permission to write to... Please check file permissions.";
1467 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1468 }
1469 else {
1470 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1471 }
1472 }
1473
1474 lock_file = null;
1475 collection_directory = null;
1476 collection_config_file = null;
1477 }
1478
1479 /** At present, scheduling only works for GS2, only when GS2 is local and only when GLI runs from
1480 * within a GS2 installation. This method can be adjusted as scheduling becomes available for more
1481 * more situations. */
1482 public static boolean canDoScheduling() {
1483 // Would be nice to support more of these, rather than returning false
1484 if(Gatherer.isGsdlRemote) {
1485 return false;
1486 }
1487 if(Gatherer.GS3) {
1488 return false;
1489 }
1490 if (Configuration.fedora_info.isActive()) {
1491 return false;
1492 }
1493
1494 // GS2's etc/main.cfg is necessary for scheduling, but scheduling looks for it locally:
1495 // it assumes GLI is inside a GS2 installation
1496 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
1497 if(!mcfg.exists()) {
1498 System.out.println("Cannot do scheduling, since there is no file: " + mcfg.getAbsolutePath()
1499 + ".\nScheduling presently depends on GLI running from inside a GS2.");
1500 return false;
1501 }
1502
1503 return true;
1504 }
1505
1506 private void makeCollection(String name, String email)
1507 {
1508 // Generate the mkcol.pl command
1509 ArrayList command_parts_list = new ArrayList();
1510 if (!Gatherer.isGsdlRemote) {
1511 command_parts_list.add(Configuration.perl_path);
1512 command_parts_list.add("-S");
1513 }
1514 command_parts_list.add(scriptPath + "mkcol.pl");
1515 if(Gatherer.GS3) {
1516 command_parts_list.add(Utility.GS3MODE_ARGUMENT); // add '-gs3mode'
1517 }
1518 if(!Gatherer.isGsdlRemote) {
1519 command_parts_list.add("-collectdir");
1520 command_parts_list.add(getDefaultCollectDirectory());
1521 }
1522 command_parts_list.add("-win31compat");
1523 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
1524
1525 if (email != null && !email.equals("")) {
1526 command_parts_list.add("-creator");
1527 command_parts_list.add(email);
1528 }
1529
1530 command_parts_list.add(name);
1531
1532 // Run the mkcol.pl command
1533 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1534 //for(int i = 0; i < command_parts.length; i++) {
1535 ///ystem.err.println("\""+command_parts[i]+"\"");
1536 //}
1537
1538 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1539 process.run(); // Don't bother threading this... yet
1540 }
1541
1542
1543 /** 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.
1544 * @param event A <strong>GShellEvent</strong> which contains a the message.
1545 */
1546 public synchronized void message(GShellEvent event) {
1547
1548 }
1549
1550
1551 public void metadataChanged(CollectionTreeNode[] file_nodes)
1552 {
1553 if (collection != null) {
1554 collection.setMetadataChanged(true);
1555
1556 // we're only going to refresh the tree's already visible nodes (and reselect them) IFF
1557 // gs.FilenameEncoding meta was set on any files/folders and the filenames of the
1558 // affected CollectionTreeNodes need to be recalculated
1559 if(FilenameEncoding.isRefreshRequired()) {
1560
1561 // refreshes the relevant part of the tree
1562 TreePath[] paths = collection_tree.getSelectionPaths();
1563 if(paths != null) {
1564 for(int i = 0; i < paths.length; i++) {
1565 collection_tree_model.refresh(paths[i]);
1566 collection_tree.setSelectionPath(paths[i]);
1567 }
1568 }
1569 // refreshed the tree, so turn off the requirement to refresh
1570 FilenameEncoding.setRefreshRequired(false);
1571 }
1572 }
1573 }
1574
1575
1576 public void openCollectionFromLastTime() {
1577 // If there was an open collection last session, reopen it.
1578 // (this method doesn't get called if there was no open collection or only a collect folder
1579 // instead of any previously-opened collection)
1580
1581 // Load the collection now
1582 loadCollection(Gatherer.open_collection_file_path);
1583 }
1584
1585
1586 /** This call is fired whenever a process within a GShell created by this class begins.
1587 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1588 * @see org.greenstone.gatherer.Gatherer
1589 * @see org.greenstone.gatherer.gui.GUIManager
1590 * @see org.greenstone.gatherer.shell.GShell
1591 */
1592 public synchronized void processBegun(GShellEvent event) {
1593 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1594 ///ystem.err.println("ProcessBegun " + event.getType());
1595 // If this is one of the types where we wish to lock user control
1596 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1597 }
1598 /** This call is fired whenever a process within a GShell created by this class ends.
1599 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1600 * @see org.greenstone.gatherer.Gatherer
1601 * @see org.greenstone.gatherer.gui.GUIManager
1602 * @see org.greenstone.gatherer.shell.GShell
1603 */
1604 public synchronized void processComplete(GShellEvent event) {
1605 //ystem.err.println("CollectionManager.processComplete(" + event.getType() + ")");
1606 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1607 ///ystem.err.println("Received process complete event - " + event);
1608 // If we were running an import, now run a build.
1609 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1610 // Finish import.
1611 collection.setImported(true);
1612 collection.setFilesChanged(false);
1613 collection.setMetadataChanged(false);
1614 buildCollection();
1615 }
1616 else if(event.getType() == GShell.SCHEDULE && event.getStatus() == GShell.OK ) {
1617
1618 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.ScheduleBuilt", Dictionary.get("ScheduleBuilt.Title"), Dictionary.get("ScheduleBuilt.Message"), null, false);
1619 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1620 collection_built_warning_dialog.display();
1621 collection_built_warning_dialog.dispose();
1622 collection_built_warning_dialog = null;
1623 }
1624 // If we were running a build, now is when we move files across.
1625 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1626
1627 if ( CollectionDesignManager.buildcolWasFull() ) {
1628 if(installCollection()) {
1629 // If we have a local library running then ask it to add our newly create collection
1630 if (LocalLibraryServer.isRunning() == true) {
1631 LocalLibraryServer.addCollection(collection.getName());
1632 }
1633 else if (Gatherer.GS3) {
1634 //xiao comment out this: convertToGS3Collection();
1635 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1636 }
1637
1638 // Fire a collection changed first to update the preview etc buttons
1639 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1640
1641 // Now display a message dialog saying its all built
1642 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", Dictionary.get("CollectionBuilt.Title"), Dictionary.get("CollectionBuilt.Message"), null, false);
1643 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1644 collection_built_warning_dialog.display();
1645 collection_built_warning_dialog.dispose();
1646 collection_built_warning_dialog = null;
1647
1648 //Set nothing as needing rebuilding, as a build has just finished :-)
1649 CollectionDesignManager.resetRebuildTypeRequired();
1650 }
1651 else {
1652 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1653 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1654 DebugStream.println("Status is ok but !installCollection()");
1655 }
1656 }
1657 }
1658 else if (event.getStatus() == GShell.CANCELLED) {
1659 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1660 Gatherer.g_man.repaint();
1661 }
1662 else if (event.getStatus() == GShell.ERROR) {
1663 if (event.getType() == GShell.NEW) {
1664 String name = event.getMessage();
1665 String collectDir = getCollectionDirectoryPath(name);
1666 String errMsg = "";
1667 if (!new File(getCollectionDirectoryPath(name)).exists() || !new File(getCollectionDirectoryPath(name)).canWrite()) {
1668 String reason = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{collectDir});
1669 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", new String[]{reason});
1670 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS") != -1){
1671 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1672 errMsg += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1673 //}
1674 }
1675 } else {
1676 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection");
1677 }
1678 JOptionPane.showMessageDialog(Gatherer.g_man, errMsg, Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1679 }
1680 else if(event.getType() == GShell.SCHEDULE) {
1681 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Schedule_Failed"), Dictionary.get("CollectionManager.Schedule_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1682 }
1683 else {
1684 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1685 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1686 }
1687
1688 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.
1689 }
1690 }
1691
1692
1693 /** Determine if the manager is ready for actions apon its collection.
1694 * @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.
1695 */
1696 static public synchronized boolean ready() {
1697 if(collection != null) {
1698 return true;
1699 }
1700 else {
1701 return false;
1702 }
1703 }
1704
1705
1706 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1707 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1708 */
1709 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1710 build_monitor = monitor;
1711 }
1712 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1713 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1714 */
1715 public void registerImportMonitor(GShellProgressMonitor monitor) {
1716 import_monitor = monitor;
1717 }
1718
1719 public void registerScheduleMonitor(GShellProgressMonitor monitor) {
1720 schedule_monitor = monitor;
1721 }
1722
1723
1724 static public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1725 {
1726 collection_contents_changed_listeners.remove(listener);
1727 }
1728
1729
1730 public void removeMetadataSet(MetadataSet metadata_set)
1731 {
1732 DebugStream.println("Removing metadata set...");
1733
1734 // Delete the .mds file from the collection's "metadata" folder...
1735 File metadata_set_file = metadata_set.getMetadataSetFile();
1736
1737 // ...but not if it is the "ex.mds" file
1738 if (metadata_set_file.getName().equals("ex.mds")) {
1739 return;
1740 }
1741
1742 // ...and only if it exists
1743 if (metadata_set_file.exists()) {
1744 metadata_set_file.delete();
1745
1746 // Unload it from the MetadataSetManager
1747 MetadataSetManager.unloadMetadataSet(metadata_set);
1748
1749 // If we're using a remote Greenstone server, delete the metadata file on the server
1750 if (Gatherer.isGsdlRemote) {
1751 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1752 }
1753 }
1754 }
1755
1756
1757 /** Used to check whether all open collections have a 'saved' state.
1758 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1759 * @see org.greenstone.gatherer.collection.Collection
1760 */
1761 public boolean saved() {
1762 boolean result = true;
1763 if(collection != null) {
1764 result = collection.getSaved();
1765 }
1766 return result;
1767 }
1768
1769
1770 /** Saves the currently loaded collection. */
1771 public void saveCollection()
1772 {
1773
1774 if (collection == null) return;
1775
1776 DebugStream.println("Saving collection " + collection.getName() + "...");
1777
1778 // Change cursor to hourglass
1779 Gatherer.g_man.wait(true);
1780
1781 // Create a backup of the collection file, just in case anything goes wrong
1782 File collection_file = new File(getLoadedCollectionColFilePath());
1783 if (collection_file.exists()) {
1784 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1785 if (!collection_file.renameTo(collection_file_backup)) {
1786 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1787 }
1788 collection_file_backup.deleteOnExit();
1789 }
1790
1791 // Write out the collection file
1792 collection.save();
1793
1794 // Write out the collection configuration file
1795 collection.cdm.save();
1796
1797 // Change cursor back to normal
1798 Gatherer.g_man.wait(false);
1799 }
1800
1801
1802 /** 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] */
1803 // now add in greenstone metadata set too.
1804 private void addDefaultMetadataSets()
1805 {
1806 // Add dublin core which is the default metadata set. The user
1807 // can change this later
1808 File dc_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"dublin.mds");
1809 if (dc_file.exists()) {
1810 importMetadataSet(new MetadataSet(dc_file));
1811 }
1812 File gs_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"greenstone.mds");
1813 if (gs_file.exists()) {
1814 importMetadataSet(new MetadataSet(gs_file));
1815 }
1816 }
1817
1818
1819 private void addRequiredMetadataSets()
1820 {
1821 // Always import the extracted metadata set
1822 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1823 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1824 }
1825
1826 private String getDefaultCollectDirectory() {
1827 String collect_dir = Gatherer.getCollectDirectoryPath();
1828 // Remove erroneous file windows file separator as it causes problems when running import.pl
1829 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1830 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1831 }
1832 return collect_dir;
1833 }
1834
1835 // used as arg in the perl scripts
1836 private String getCollectDirectory() {
1837 String collect_dir = Gatherer.getCollectDirectoryPath();
1838 return collect_dir.substring(0, collect_dir.length()-1); // remove trailing slash
1839
1840 // the following will stick any colgroup at the end of the collect directory, making it no longer
1841 // possible to get the real collect dir in a general manner if this were located outside greenstone
1842 //String collect_dir = collection.getCollectionDirectory().getParentFile().getPath();
1843 //return collect_dir;
1844 }
1845
1846
1847 /** Install collection by moving its files from building to index after a successful build.
1848 * @see org.greenstone.gatherer.Gatherer
1849 * @see org.greenstone.gatherer.util.Utility
1850 */
1851 private boolean installCollection()
1852 {
1853 if (Configuration.fedora_info.isActive()) {
1854 DebugStream.println("Fedora build complete. No need to move files.");
1855 return true;
1856 }
1857
1858
1859 DebugStream.println("Build complete. Moving files.");
1860
1861 try {
1862 // Ensure that the local library has released this collection so we can delete the index directory
1863 if (LocalLibraryServer.isRunning() == true) {
1864 LocalLibraryServer.releaseCollection(getLoadedCollectionName(true)); // URL style slash //collection.getName());
1865 }
1866 // deactivate it in tomcat so that windows will release the index files
1867 if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
1868 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection.getName());
1869 }
1870 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1871 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1872
1873 File building_dir = new File(getLoadedCollectionBuildingDirectoryPath());
1874 DebugStream.println("Building = " + building_dir.getAbsolutePath());
1875
1876 // Get the build mode from the build options
1877 String build_mode = collection.build_options.getValue("mode");
1878
1879 // Special case for build mode "all": replace index dir with building dir
1880 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1881 // Remove the old index directory
1882 if (index_dir.exists()) {
1883 Utility.delete(index_dir);
1884
1885 // Wait for a couple of seconds, just for luck
1886 wait(2000);
1887
1888 // Check the delete worked
1889 if (index_dir.exists()) {
1890 throw new Exception(Dictionary.get("CollectionManager.Index_Not_Deleted"));
1891 }
1892 }
1893
1894 if (Gatherer.isGsdlRemote) {
1895 Gatherer.remoteGreenstoneServer.deleteCollectionFile(
1896 collection.getGroupQualifiedName(false), new File(getLoadedCollectionIndexDirectoryPath()));
1897 Gatherer.remoteGreenstoneServer.moveCollectionFile(collection.getGroupQualifiedName(false),
1898 new File(getLoadedCollectionBuildingDirectoryPath()), new File(getLoadedCollectionIndexDirectoryPath()));
1899 }
1900
1901 // Move the building directory to become the new index directory
1902 if (building_dir.renameTo(index_dir) == false) {
1903 throw new Exception(Dictionary.get("CollectionManager.Build_Not_Moved"));
1904 }
1905 }
1906
1907 // Otherwise copy everything in the building dir into the index dir
1908 else {
1909 moveContentsInto(building_dir, index_dir);
1910 }
1911 }
1912 catch (Exception exception) {
1913 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Install_Exception", exception.getMessage()), "Error", JOptionPane.ERROR_MESSAGE);
1914 return false;
1915 }
1916 return true;
1917 }
1918
1919
1920 /** Moves all the files in one directory into another, overwriting existing files */
1921 private void moveContentsInto(File source_directory, File target_directory)
1922 {
1923 File[] source_files = source_directory.listFiles();
1924 for (int i = 0; i < source_files.length; i++) {
1925 File source_file = source_files[i];
1926 File target_file = new File(target_directory, source_file.getName());
1927
1928 if (source_file.isDirectory()) {
1929 moveContentsInto(source_file, target_file);
1930 source_file.delete();
1931 }
1932 else {
1933 if (target_file.exists()) {
1934 target_file.delete();
1935 }
1936
1937 source_file.renameTo(target_file);
1938 }
1939 }
1940 }
1941
1942 private void updateCollectionConfigXML(File base_cfg, File new_cfg) {
1943 //In this method, the files base_cfg and new_cfg are all xml files.
1944
1945 Document base_cfg_doc = XMLTools.parseXMLFile(base_cfg);
1946 XMLTools.writeXMLFile(new_cfg, base_cfg_doc);
1947 Document new_cfg_doc = XMLTools.parseXMLFile(new_cfg);
1948 Element collection_config = new_cfg_doc.getDocumentElement();
1949
1950 Node browseNode = XMLTools.getChildByTagNameIndexed(collection_config, StaticStrings.BROWSE_STR, 0);
1951 NodeList classifier_children = ((Element)browseNode).getElementsByTagName(StaticStrings.CLASSIFIER_STR);
1952 int num_nodes = classifier_children.getLength();
1953
1954 if (num_nodes < 1) {
1955 return;
1956 }
1957
1958 // Read in the classifier command watching for hfile, metadata and sort arguments.
1959 String buttonname = null;
1960 String hfile = null;
1961 String metadata = null;
1962 String sort = null;
1963
1964 for (int i=0; i<num_nodes; i++) {
1965 Element classifier_element = (Element)classifier_children.item(i);
1966 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
1967 for (int j=0; j<option_children.getLength(); j++) {
1968 Element option_element = (Element)option_children.item(j);
1969 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
1970 String value_str = option_element.getAttribute(StaticStrings.VALUE_ATTRIBUTE);
1971
1972 if (name_str == null || name_str.equals("")) {
1973 continue;
1974 }
1975 if (name_str != null && value_str == null ) {
1976 value_str = "";
1977 }
1978 if (name_str.equals("hfile")) {
1979 hfile = value_str;
1980 }
1981 else if (name_str.equals("metadata") && value_str != null) {
1982 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1983 if (replacement != null && !replacement.equals("")) {
1984 metadata = replacement;
1985 }
1986 }
1987 else if (name_str.equals("sort") && value_str != null) {
1988 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1989 if (replacement != null && !replacement.equals("")) {
1990 sort = replacement;
1991 }
1992 }
1993 else if(name_str.equals("buttonname") && value_str != null) {
1994 buttonname = value_str;
1995 }
1996 }
1997 }
1998 for (int i=0; i<num_nodes; i++) {
1999 Element classifier_element = (Element)classifier_children.item(i);
2000 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
2001 for (int j=0; j<option_children.getLength(); j++) {
2002 Element option_element = (Element)option_children.item(j);
2003 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
2004
2005 if (name_str.equals("metadata") && metadata != null) {
2006 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
2007 }
2008 else if (name_str.equals("hfile") && hfile != null) {
2009 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata + ".txt");
2010 }
2011 else if (name_str.equals("sort") && sort != null) {
2012 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, sort);
2013 }
2014 else if(name_str.equals("buttonname") && (buttonname == "" || buttonname == null)) {
2015 // No buttonname has been specified. Lets create one using the metadata as its value
2016 Element option = new_cfg_doc.createElement(StaticStrings.OPTION_STR);
2017 option.setAttribute(StaticStrings.NAME_ATTRIBUTE, "buttonname");
2018 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
2019 classifier_element.appendChild(option);
2020 }
2021 }
2022 }
2023 }
2024
2025 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
2026 {
2027 boolean first_name = true;
2028 boolean first_extra = true;
2029
2030 // 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.
2031 try {
2032 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
2033 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
2034 String command = null;
2035 while((command = in.readLine()) != null) {
2036 if (command.length()==0) {
2037 // output a new line
2038 out.newLine();
2039 continue;
2040 }
2041 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
2042 while(command.trim().endsWith("\\")) {
2043 command = command.substring(0, command.lastIndexOf("\\"));
2044 String next_line = in.readLine();
2045 if(next_line != null) {
2046 command = command + next_line;
2047 }
2048 }
2049 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
2050 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
2051 String command_type_str = tokenizer.nextToken().toLowerCase();
2052
2053 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
2054 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
2055 StringBuffer new_command = new StringBuffer(command_type_str);
2056 String meta_name = tokenizer.nextToken();
2057 new_command.append(' ');
2058 new_command.append(meta_name);
2059 while (tokenizer.hasMoreTokens()) {
2060 new_command.append(' ');
2061 new_command.append(tokenizer.nextToken());
2062 }
2063 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)) {
2064 // dont save
2065 } else {
2066 write(out, new_command.toString());
2067 }
2068 new_command = null;
2069 continue;
2070 } // if collectionmeta
2071
2072 if (command_type_str.equals("classify")) {
2073 StringBuffer text = new StringBuffer(command_type_str);
2074 // Read in the classifier command watching for hfile, metadata and sort arguments.
2075 String buttonname = null;
2076 String hfile = null;
2077 String new_metadata = null;
2078 String old_metadata = null;
2079
2080 while(tokenizer.hasMoreTokens()) {
2081 String token = tokenizer.nextToken();
2082 if (token.equals("-hfile")) {
2083 if(tokenizer.hasMoreTokens()) {
2084 text.append(" ");
2085 text.append(token);
2086 token = tokenizer.nextToken();
2087 hfile = token;
2088 }
2089 }
2090 else if (token.equals("-metadata")) {
2091 if(tokenizer.hasMoreTokens()) {
2092 text.append(" ");
2093 text.append(token);
2094 String temp_metadata = tokenizer.nextToken();
2095 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2096 if (replacement != null && !replacement.equals("")) {
2097 token = replacement;
2098 old_metadata = temp_metadata;
2099 new_metadata = replacement;
2100 }
2101 else {
2102 token = temp_metadata;
2103 }
2104 temp_metadata = null;
2105 replacement = null;
2106 }
2107 }
2108 else if (token.equals("-sort")) {
2109 if(tokenizer.hasMoreTokens()) {
2110 text.append(" ");
2111 text.append(token);
2112 String temp_metadata = tokenizer.nextToken();
2113 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2114 if (replacement != null && !replacement.equals("")) {
2115 token = replacement;
2116 }
2117 else {
2118 token = temp_metadata;
2119 }
2120 temp_metadata = null;
2121 replacement = null;
2122 }
2123 }
2124 else if(token.equals("-buttonname")) {
2125 buttonname = token;
2126 }
2127 text.append(' ');
2128 text.append(token);
2129 token = null;
2130 }
2131
2132 // 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)!
2133 if(old_metadata != null && new_metadata != null && buttonname == null) {
2134 text.append(' ');
2135 text.append("-buttonname");
2136 text.append(' ');
2137 text.append(old_metadata);
2138 }
2139 command = text.toString();
2140 // Replace the hfile if we found it
2141 if(hfile != null && new_metadata != null) {
2142 command = command.replaceAll(hfile, new_metadata + ".txt");
2143 }
2144
2145 buttonname = null;
2146 hfile = null;
2147 new_metadata = null;
2148 old_metadata = null;
2149 write(out, command);
2150 } else {
2151 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
2152 StringBuffer new_command = new StringBuffer(command_type_str);
2153 while (tokenizer.hasMoreTokens()) {
2154 new_command.append(' ');
2155 new_command.append(tokenizer.nextToken());
2156 }
2157
2158 command = new_command.toString();
2159
2160 // 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.
2161 // we really want to build up the whole command here
2162 boolean format_command = command_type_str.equals("format");
2163 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
2164 if (metadata_mapping != null) {
2165 Iterator keys = metadata_mapping.keySet().iterator();
2166 while (keys.hasNext()) {
2167 String target = (String) keys.next();
2168 String replacement = (String) metadata_mapping.get(target);
2169 if (replacement != null && !replacement.equals("")) {
2170 if (format_command) {
2171 target = "\\[" + target + "\\]";
2172 replacement = "{Or}{[" + replacement + "]," + target + "}";
2173 }
2174 command = command.replaceAll(target, replacement);
2175 }
2176 }
2177 }
2178
2179 write(out, command);
2180 }
2181 tokenizer = null;
2182 }
2183 in.close();
2184 in = null;
2185 out.flush();
2186 out.close();
2187 out = null;
2188 }
2189 catch(Exception error) {
2190 DebugStream.printStackTrace(error);
2191 }
2192 // All done, I hope.
2193 }
2194
2195 private void write(BufferedWriter out, String message)
2196 throws Exception {
2197 out.write(message, 0, message.length());
2198 out.newLine();
2199 }
2200
2201
2202 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
2203 private class FMTreeModelListener
2204 implements TreeModelListener {
2205 /** 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.
2206 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2207 */
2208 public void treeNodesChanged(TreeModelEvent event) {
2209 if(collection != null) {
2210 collection.setSaved(false);
2211 collection.setFilesChanged(true);
2212 }
2213 }
2214 /** 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.
2215 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2216 */
2217 public void treeNodesInserted(TreeModelEvent event) {
2218 if(collection != null) {
2219 collection.setSaved(false);
2220 collection.setFilesChanged(true);
2221 }
2222 }
2223 /** 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.
2224 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2225 */
2226 public void treeNodesRemoved(TreeModelEvent event) {
2227 if(collection != null) {
2228 collection.setSaved(false);
2229 collection.setFilesChanged(true);
2230
2231 }
2232 }
2233 /** 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.
2234 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2235 */
2236 public void treeStructureChanged(TreeModelEvent event) {
2237 if(collection != null) {
2238 collection.setSaved(false);
2239 }
2240 }
2241 }
2242}
Note: See TracBrowser for help on using the repository browser.