source: main/trunk/gli/src/org/greenstone/gatherer/cdm/CollectionConfiguration.java@ 22970

Last change on this file since 22970 was 22970, checked in by sjm84, 14 years ago

Added the ability to change the database type between GDBM, JDBM and SQLite

  • Property svn:keywords set to Author Date Id Revision
File size: 22.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 * Author: John Thompson, Greenstone Digital Library, University of Waikato
9 *
10 * Copyright (C) 1999 New Zealand Digital Library Project
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *########################################################################
26 */
27package org.greenstone.gatherer.cdm;
28
29import java.awt.*;
30import java.awt.event.*;
31import java.io.*;
32import java.util.*;
33import javax.swing.*;
34import org.greenstone.gatherer.DebugStream;
35import org.greenstone.gatherer.Gatherer;
36import org.greenstone.gatherer.collection.CollectionManager;
37import org.greenstone.gatherer.greenstone.LocalLibraryServer;
38import org.greenstone.gatherer.gui.GLIButton;
39import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
40import org.greenstone.gatherer.util.DOMTree;
41import org.greenstone.gatherer.util.StaticStrings;
42import org.greenstone.gatherer.util.XMLTools;
43import org.greenstone.gatherer.util.Utility;
44import org.w3c.dom.*;
45
46/** This class provides access to an xml-type view of the collect.cfg file. This is useful as it allows the manipulation and free form editing of a collect.cfg file while still allowing the various CDM data managers to base themselves directly on this model (whereas they used to be independant ListModels which clobbered the ordering of unparsed commands).
47 * @author John Thompson, Greenstone Digital Library, University of Waikato
48 */
49public class CollectionConfiguration {
50 static final public String ENCODING = "UTF-8";
51 static final public String NEWLINE_ELEMENT = "NewLine";
52
53 static private Document document;
54 static private String saved_config_file_string = null;
55
56 // may be collec.cfg (GS2) or collectionConfig.xml (GS3)
57 private File collect_config_file;
58 private String collect_config_filename;
59
60 // This method is initilised in CollectionDesignManager.java constructor
61 public CollectionConfiguration (File collect_config_file) {
62 this.collect_config_file = collect_config_file;
63 this.collect_config_filename = collect_config_file.getName();
64 // parse the XML template
65 document = XMLTools.parseXMLFile ("xml/CollectionConfig.xml", true);
66 String filename = collect_config_filename.toLowerCase ();
67
68 if (filename.endsWith (".cfg")) {
69 saved_config_file_string = CollectCfgReadWrite.parse (collect_config_file, document);
70 }
71 else if (filename.endsWith (".xml")) {
72 CollectionConfigXMLReadWrite.parse (collect_config_file, document);
73 }
74
75 //XMLTools.printXMLNode(document.getDocumentElement());
76 }
77
78 static public Element createElement (String element_name) {
79 return document.createElement (element_name);
80 }
81
82 /** Gives the preferred ordering of commands */
83 static final public String[] COMMAND_ORDER =
84 {StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, StaticStrings.BUILDTYPE_ELEMENT, StaticStrings.PLUGIN_ELEMENT, StaticStrings.INDEXES_ELEMENT, StaticStrings.INDEX_DEFAULT_ELEMENT, StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.LANGUAGES_ELEMENT, StaticStrings.LANGUAGE_DEFAULT_ELEMENT, StaticStrings.LANGUAGE_METADATA_ELEMENT, StaticStrings.SUBCOLLECTION_ELEMENT, StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT, StaticStrings.SUPERCOLLECTION_ELEMENT, StaticStrings.CLASSIFY_ELEMENT, StaticStrings.FORMAT_ELEMENT, StaticStrings.COLLECTIONMETADATA_ELEMENT};
85
86 /** Find the best insertion position for the given DOM Element. This should try to match command tag, and if found should then try to group by name or type (eg CollectionMeta), or append to end is no such grouping exists (eg Plugins). Failing a command match it will check against the command order for the best insertion location.
87 * @param target_element the command Element to be inserted
88 * @return the Element which the given command should be inserted before, or null to append to end of list
89 */
90 static public Node findInsertionPoint (Element target_element) {
91 ///ystem.err.println("Find insertion point: " + target_element.getNodeName());
92 String target_element_name = target_element.getNodeName ();
93 Element document_element = document.getDocumentElement ();
94 // Try to find commands with the same tag.
95 NodeList matching_elements = document_element.getElementsByTagName (target_element_name);
96 // If we found matching elements, then we have our most likely insertion location, so check within for groupings
97 if(matching_elements.getLength () != 0) {
98 ///ystem.err.println("Found matching elements.");
99 // Only CollectionMeta are grouped.
100 if(target_element_name.equals (StaticStrings.COLLECTIONMETADATA_ELEMENT)) {
101 ///ystem.err.println("Dealing with collection metadata");
102 // Special case: CollectionMeta can be added at either the start or end of a collection configuration file. However the start position is reserved for special metadata, so if no non-special metadata can be found we must append to the end.
103 // So if the command to be added is special add it immediately after any other special command
104 if(target_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) {
105 int index = 0;
106 Element matched_element = (Element) matching_elements.item (index);
107 Element sibling_element = (Element) matched_element.getNextSibling ();
108 while(sibling_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) {
109 index++;
110 matched_element = (Element) matching_elements.item (index);
111 sibling_element = (Element) matched_element.getNextSibling ();
112 }
113 if(sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) {
114 Element newline_element = document.createElement (NEWLINE_ELEMENT);
115 document_element.insertBefore (newline_element, sibling_element);
116 }
117 return sibling_element;
118 }
119 // Otherwise try to find a matching 'name' and add after the last one in that group.
120 else {
121 int index = 0;
122 target_element_name = target_element.getAttribute (StaticStrings.NAME_ATTRIBUTE);
123 boolean found = false;
124 // Skip all of the special metadata
125 Element matched_element = (Element) matching_elements.item (index);
126 while(matched_element.getAttribute (StaticStrings.SPECIAL_ATTRIBUTE).equals (StaticStrings.TRUE_STR)) {
127 index++;
128 matched_element = (Element) matching_elements.item (index);
129 }
130 // Begin search
131 while(!found && matched_element != null) {
132 if(matched_element.getAttribute (StaticStrings.NAME_ATTRIBUTE).equals (target_element_name)) {
133 found = true;
134 }
135 else {
136 index++;
137 matched_element = (Element) matching_elements.item (index);
138 }
139 }
140 // If we found a match, we need to continue checking until we find the last name match.
141 if(found) {
142 index++;
143 Element previous_sibling = matched_element;
144 Element sibling_element = (Element) matching_elements.item (index);
145 while(sibling_element != null && sibling_element.getAttribute (StaticStrings.NAME_ATTRIBUTE).equals (target_element_name)) {
146 previous_sibling = sibling_element;
147 index++;
148 sibling_element = (Element) matching_elements.item (index);
149 }
150 // Previous sibling now holds the command immediately before where we want to add, so find its next sibling and add to that. In this one case we can ignore new lines!
151 return previous_sibling.getNextSibling ();
152 }
153 // If not found we just add after last metadata element
154 else {
155 Element last_element = (Element) matching_elements.item (matching_elements.getLength () - 1);
156 return last_element.getNextSibling ();
157 }
158 }
159
160 }
161 else {
162 ///ystem.err.println("Not dealing with collection meta.");
163 Element matched_element = (Element) matching_elements.item (matching_elements.getLength () - 1);
164 // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines)
165 Node sibling_element = matched_element.getNextSibling ();
166 if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) {
167 Element newline_element = document.createElement (NEWLINE_ELEMENT);
168 document_element.insertBefore (newline_element, sibling_element);
169 }
170 return sibling_element; // Note that this may be null
171 }
172 }
173 ///ystem.err.println("No matching elements found.");
174 // Locate where this command is in the ordering
175 int command_index = -1;
176 for(int i = 0; command_index == -1 && i < COMMAND_ORDER.length; i++) {
177 if(COMMAND_ORDER[i].equals (target_element_name)) {
178 command_index = i;
179 }
180 }
181 ///ystem.err.println("Command index is: " + command_index);
182 // Now move forward, checking for existing elements in each of the preceeding command orders.
183 int preceeding_index = command_index - 1;
184 ///ystem.err.println("Searching before the target command.");
185 while(preceeding_index >= 0) {
186 matching_elements = document_element.getElementsByTagName (COMMAND_ORDER[preceeding_index]);
187 // If we've found a match
188 if(matching_elements.getLength () > 0) {
189 // We add after the last element
190 Element matched_element = (Element) matching_elements.item (matching_elements.getLength () - 1);
191 // One final quick test. If the matched element is immediately followed by a NewLine command, then we insert another NewLine after the matched command, then return the NewLine instead (thus the about to be inserted command will be placed between the two NewLines)
192 Node sibling_element = matched_element.getNextSibling ();
193 if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) {
194 Element newline_element = document.createElement (NEWLINE_ELEMENT);
195 document_element.insertBefore (newline_element, sibling_element);
196 }
197 return sibling_element; // Note that this may be null
198 }
199 preceeding_index--;
200 }
201 // If all that fails, we now move backwards through the commands
202 int susceeding_index = command_index + 1;
203 ///ystem.err.println("Searching after the target command.");
204 while(susceeding_index < COMMAND_ORDER.length) {
205 matching_elements = document_element.getElementsByTagName (COMMAND_ORDER[susceeding_index]);
206 // If we've found a match
207 if(matching_elements.getLength () > 0) {
208 // We add before the first element
209 Element matched_element = (Element) matching_elements.item (0);
210 // One final quick test. If the matched element is immediately preceeded by a NewLine command, then we insert another NewLine before the matched command, then return this new NewLine instead (thus the about to be inserted command will be placed between the two NewLines)
211 Node sibling_element = matched_element.getPreviousSibling ();
212 if(sibling_element != null && sibling_element.getNodeName ().equals (NEWLINE_ELEMENT)) {
213 Element newline_element = document.createElement (NEWLINE_ELEMENT);
214 document_element.insertBefore (newline_element, sibling_element);
215 }
216 return sibling_element; // Note that this may be null
217 }
218 susceeding_index++;
219 }
220 // Well. Apparently there are no other commands in this collection configuration. So append away...
221 return null;
222 }
223
224
225 static public NodeList getElementsByTagName (String element_name) {
226 return document.getDocumentElement ().getElementsByTagName (element_name);
227 }
228
229 public Element getDocumentElement () {
230 return document.getDocumentElement ();
231 }
232
233 /** This debug facility shows the currently loaded collect.cfg or CollectConfig.xml file as a DOM tree. */
234 public void display () {
235 JDialog dialog = new JDialog (Gatherer.g_man, "Collection Configuration", false);
236 dialog.setSize (400,400);
237 JPanel content_pane = (JPanel) dialog.getContentPane ();
238 final DOMTree tree = new DOMTree (document);
239 JButton refresh_button = new GLIButton ("Refresh Tree");
240 refresh_button.addActionListener (new ActionListener () {
241 public void actionPerformed (ActionEvent event) {
242 tree.setDocument (document);
243 }
244 });
245 content_pane.setBorder (BorderFactory.createEmptyBorder (5,5,5,5));
246 content_pane.setLayout (new BorderLayout ());
247 content_pane.add (new JScrollPane (tree), BorderLayout.CENTER);
248 content_pane.add (refresh_button, BorderLayout.SOUTH);
249 dialog.setVisible (true);
250 }
251
252
253
254 public File getFile () {
255 return collect_config_file;
256 }
257
258 public Element getCreator () {
259 Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, null, null);
260 element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_CREATOR_STR);
261 element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
262 return element;
263 }
264
265 public Element getMaintainer () {
266 Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, null, null);
267 element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_MAINTAINER_STR);
268 element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
269 return element;
270 }
271
272 /** Retrieve or create the languages Element. */
273 public Element getLanguages () {
274 return getOrCreateElementByTagName (StaticStrings.LANGUAGES_ELEMENT, null, null);
275 }
276
277 public Element getLanguageMetadata () {
278 return getOrCreateElementByTagName (StaticStrings.LANGUAGE_METADATA_ELEMENT, null, null);
279 }
280
281 public Element getLevels () {
282 return getOrCreateElementByTagName (StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVELS_STR);
283 }
284
285 public Element getLevelDefault () {
286 return getOrCreateElementByTagName (StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVEL_DEFAULT_STR);
287 }
288
289 public Element getIndexOptions () {
290 return getOrCreateElementByTagName (StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.INDEXOPTIONS_STR);
291 }
292
293
294 /** Retrieve or create the indexes Element. Note that this method behaves differently from the other getBlah methods, in that it also has to keep in mind that indexes come in two flavours, MG and MGPP. */
295 public Element getMGIndexes () {
296 return getOrCreateElementByTagName (StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.FALSE_STR);
297 }
298
299 public Element getMGPPIndexes () {
300 return getOrCreateElementByTagName (StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.TRUE_STR);
301 }
302
303 public Element getPublic () {
304 Element element = getOrCreateElementByTagName (StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, null, null);
305 element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_PUBLIC_STR);
306 element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
307 return element;
308 }
309
310 public Element getBuildType () {
311 Element element = getOrCreateElementByTagName (StaticStrings.BUILDTYPE_ELEMENT, null, null);
312 element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.BUILDTYPE_STR);
313 element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
314 return element;
315
316 }
317
318public Element getDatabaseType () {
319 Element element = getOrCreateElementByTagName (StaticStrings.DATABASETYPE_ELEMENT, null, null);
320 element.setAttribute (StaticStrings.NAME_ATTRIBUTE, StaticStrings.DATABASETYPE_STR);
321 element.setAttribute (StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
322 return element;
323
324 }
325
326 /** Retrieve or create the subindexes Element. */
327 public Element getSubIndexes () {
328 return getOrCreateElementByTagName (StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, null, null);
329 }
330
331 /** Retrieve or create the supercollections Element. */
332 public Element getSuperCollection () {
333 return getOrCreateElementByTagName (StaticStrings.SUPERCOLLECTION_ELEMENT, null, null);
334 }
335
336 public boolean ready () {
337 return document != null;
338 }
339
340
341
342
343 /** ************************** Private Methods ***************************/
344
345
346 /** Retrieve or create the indexes Element. */
347 static private Element getOrCreateElementByTagName (String name, String conditional_attribute, String required_value) {
348 Element document_element = document.getDocumentElement ();
349 NodeList elements = document_element.getElementsByTagName (name);
350 int elements_length = elements.getLength ();
351 if(elements_length > 0) {
352 if(conditional_attribute == null) {
353 document_element = null;
354 return (Element) elements.item (0);
355 }
356 else {
357 for(int i = 0; i < elements_length; i++) {
358 Element element = (Element) elements.item (i);
359 if(element.getAttribute (conditional_attribute).equals (required_value)) {
360 document_element = null;
361 return element;
362 }
363 element = null;
364 }
365 }
366 }
367 // Create the element
368 Element element = document.createElement (name);
369 // If there was a property set it
370 if(conditional_attribute != null) {
371 element.setAttribute (conditional_attribute, required_value);
372 }
373 Node target_node = findInsertionPoint (element);
374 if(target_node != null) {
375 document_element.insertBefore (element, target_node);
376 }
377 else {
378 document_element.appendChild (element);
379 }
380 document_element = null;
381 return element;
382 }
383
384
385
386
387
388 /** Write the text to the buffer. This is used so we don't have to worry about storing intermediate String values just so we can calaulate length and offset.
389 * @param writer the BufferedWriter to which the str will be written
390 * @param str the String to be written
391 */
392 private void write (BufferedWriter writer, String str)
393 throws IOException {
394 writer.write (str, 0, str.length ());
395 }
396
397
398 public void saveIfNecessary () {
399
400 // Generate a string version of internal document
401 String config_file_string = null;
402 if (Gatherer.GS3) {
403 config_file_string = CollectionConfigXMLReadWrite.generateStringVersion(document);
404 } else {
405 config_file_string = CollectCfgReadWrite.generateStringVersion(document);
406 }
407 // compare to saved version
408 if (saved_config_file_string != null) {
409 if (saved_config_file_string.equals(config_file_string)) {
410 DebugStream.println (collect_config_filename +" file hasn't changed so no save necessary...");
411 return;
412 }
413 }
414
415 // We need to save...
416 DebugStream.println (collect_config_filename +" file has changed, saving now...");
417
418
419 // If we're using the Local Library we must release the collection before writing to the collect.cfg file
420 String collection_name = CollectionManager.getLoadedCollectionName (true); // url style slash
421 boolean collection_released = false;
422 if (Gatherer.c_man.built () && LocalLibraryServer.isRunning () == true) {
423 // Release the collection
424 LocalLibraryServer.releaseCollection (collection_name);
425 collection_released = true;
426 }
427
428 // Make a backup of the existing config file
429 if (collect_config_file.exists ()) {
430 String config_filename;
431 String backup_filename;
432 if (Gatherer.GS3) {
433 config_filename = Utility.COLLECTION_CONFIG_XML;
434 backup_filename = Utility.COLLECTION_CONFIG_BAK;
435 } else {
436 config_filename = StaticStrings.COLLECT_CFG;
437 backup_filename = Utility.COLLECT_BAK;
438 }
439 File original_file = new File (collect_config_file.getParentFile (), config_filename);
440 File backup_file = new File (collect_config_file.getParentFile (), backup_filename);
441 if (backup_file.exists ()) {
442 backup_file.delete ();
443 }
444 if (!original_file.renameTo (backup_file)) {
445 System.err.println ("Warning: can't rename "+config_filename + " to "+ backup_filename);
446 }
447 }
448
449 // now save the file
450 if (Gatherer.GS3) {
451 CollectionConfigXMLReadWrite.save(collect_config_file, document);
452 } else {
453 // we have already converted to string, so save here
454 try {
455 OutputStream ostream = new FileOutputStream (collect_config_file);
456 Writer file_writer = new OutputStreamWriter (ostream, ENCODING);
457 BufferedWriter buffered_writer = new BufferedWriter (file_writer);
458 buffered_writer.write (config_file_string);
459 buffered_writer.close ();
460 }
461 catch (Exception exception) {
462 DebugStream.println ("Error in CollectionConfiguration.save(): " + exception);
463 DebugStream.printStackTrace (exception);
464 }
465
466 }
467
468 // save the string version
469 saved_config_file_string = config_file_string;
470
471 // If we're using a remote Greenstone server, upload the new collect.cfg file
472 if (Gatherer.isGsdlRemote) {
473 Gatherer.remoteGreenstoneServer.uploadCollectionFile (collection_name, collect_config_file);
474 }
475
476
477 // Now re-add the collection to the Local Library server
478 if (collection_released) {
479 LocalLibraryServer.addCollection (collection_name);
480 }
481 }
482
483
484
485}
486
487
Note: See TracBrowser for help on using the repository browser.