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

Last change on this file since 20447 was 20447, checked in by kjdon, 15 years ago

split CollectionConfiguration into three files: CollectionConfiguration handles all the DOM internal stuff, while CollectCfgReadWrite and CollectionConfigXMLReadWrite handle reading from and writing to collect.cfg and collectionConfig.xml files, respectively. This class was jsut getting far too big and I could never find anything in it...

  • Property svn:keywords set to Author Date Id Revision
File size: 21.7 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
318 /** Retrieve or create the subindexes Element. */
319 public Element getSubIndexes () {
320 return getOrCreateElementByTagName (StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, null, null);
321 }
322
323 /** Retrieve or create the supercollections Element. */
324 public Element getSuperCollection () {
325 return getOrCreateElementByTagName (StaticStrings.SUPERCOLLECTION_ELEMENT, null, null);
326 }
327
328 public boolean ready () {
329 return document != null;
330 }
331
332
333
334
335 /** ************************** Private Methods ***************************/
336
337
338 /** Retrieve or create the indexes Element. */
339 static private Element getOrCreateElementByTagName (String name, String conditional_attribute, String required_value) {
340 Element document_element = document.getDocumentElement ();
341 NodeList elements = document_element.getElementsByTagName (name);
342 int elements_length = elements.getLength ();
343 if(elements_length > 0) {
344 if(conditional_attribute == null) {
345 document_element = null;
346 return (Element) elements.item (0);
347 }
348 else {
349 for(int i = 0; i < elements_length; i++) {
350 Element element = (Element) elements.item (i);
351 if(element.getAttribute (conditional_attribute).equals (required_value)) {
352 document_element = null;
353 return element;
354 }
355 element = null;
356 }
357 }
358 }
359 // Create the element
360 Element element = document.createElement (name);
361 // If there was a property set it
362 if(conditional_attribute != null) {
363 element.setAttribute (conditional_attribute, required_value);
364 }
365 Node target_node = findInsertionPoint (element);
366 if(target_node != null) {
367 document_element.insertBefore (element, target_node);
368 }
369 else {
370 document_element.appendChild (element);
371 }
372 document_element = null;
373 return element;
374 }
375
376
377
378
379
380 /** 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.
381 * @param writer the BufferedWriter to which the str will be written
382 * @param str the String to be written
383 */
384 private void write (BufferedWriter writer, String str)
385 throws IOException {
386 writer.write (str, 0, str.length ());
387 }
388
389
390 public void saveIfNecessary () {
391
392 // Generate a string version of internal document
393 String config_file_string = null;
394 if (Gatherer.GS3) {
395 config_file_string = CollectionConfigXMLReadWrite.generateStringVersion(document);
396 } else {
397 config_file_string = CollectCfgReadWrite.generateStringVersion(document);
398 }
399 // compare to saved version
400 if (saved_config_file_string != null) {
401 if (saved_config_file_string.equals(config_file_string)) {
402 DebugStream.println (collect_config_filename +" file hasn't changed so no save necessary...");
403 return;
404 }
405 }
406
407 // We need to save...
408 DebugStream.println (collect_config_filename +" file has changed, saving now...");
409
410
411 // If we're using the Local Library we must release the collection before writing to the collect.cfg file
412 String collection_name = CollectionManager.getLoadedCollectionName ();
413 boolean collection_released = false;
414 if (Gatherer.c_man.built () && LocalLibraryServer.isRunning () == true) {
415 // Release the collection
416 LocalLibraryServer.releaseCollection (collection_name);
417 collection_released = true;
418 }
419
420 // Make a backup of the existing config file
421 if (collect_config_file.exists ()) {
422 String config_filename;
423 String backup_filename;
424 if (Gatherer.GS3) {
425 config_filename = Utility.COLLECTION_CONFIG_XML;
426 backup_filename = Utility.COLLECTION_CONFIG_BAK;
427 } else {
428 config_filename = StaticStrings.COLLECT_CFG;
429 backup_filename = Utility.COLLECT_BAK;
430 }
431 File original_file = new File (collect_config_file.getParentFile (), config_filename);
432 File backup_file = new File (collect_config_file.getParentFile (), backup_filename);
433 if (backup_file.exists ()) {
434 backup_file.delete ();
435 }
436 if (!original_file.renameTo (backup_file)) {
437 System.err.println ("Warning: can't rename "+config_filename + " to "+ backup_filename);
438 }
439 }
440
441 // now save the file
442 if (Gatherer.GS3) {
443 CollectionConfigXMLReadWrite.save(collect_config_file, document);
444 } else {
445 // we have already converted to string, so save here
446 try {
447 OutputStream ostream = new FileOutputStream (collect_config_file);
448 Writer file_writer = new OutputStreamWriter (ostream, ENCODING);
449 BufferedWriter buffered_writer = new BufferedWriter (file_writer);
450 buffered_writer.write (config_file_string);
451 buffered_writer.close ();
452 }
453 catch (Exception exception) {
454 DebugStream.println ("Error in CollectionConfiguration.save(): " + exception);
455 DebugStream.printStackTrace (exception);
456 }
457
458 }
459
460 // save the string version
461 saved_config_file_string = config_file_string;
462
463 // If we're using a remote Greenstone server, upload the new collect.cfg file
464 if (Gatherer.isGsdlRemote) {
465 Gatherer.remoteGreenstoneServer.uploadCollectionFile (collection_name, collect_config_file);
466 }
467
468
469 // Now re-add the collection to the Local Library server
470 if (collection_released) {
471 LocalLibraryServer.addCollection (collection_name);
472 }
473 }
474
475
476
477}
478
479
Note: See TracBrowser for help on using the repository browser.