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

Last change on this file since 36155 was 36155, checked in by kjdon, 2 years ago

added methods for getting the Sorts and Facets elements

  • Property svn:keywords set to Author Date Id Revision
File size: 22.4 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/**
47 * This class provides access to an xml-type view of the collect.cfg file. This
48 * is useful as it allows the manipulation and free form editing of a
49 * collect.cfg file while still allowing the various CDM data managers to base
50 * themselves directly on this model (whereas they used to be independant
51 * ListModels which clobbered the ordering of unparsed commands).
52 *
53 * @author John Thompson, Greenstone Digital Library, University of Waikato
54 */
55public class CollectionConfiguration
56{
57 static final public String ENCODING = "UTF-8";
58 static final public String NEWLINE_ELEMENT = "NewLine";
59
60 static private Document document;
61 static private String saved_config_file_string = null;
62
63 // may be collec.cfg (GS2) or collectionConfig.xml (GS3)
64 private File collect_config_file;
65 private String collect_config_filename;
66
67 // This method is initilised in CollectionDesignManager.java constructor
68 public CollectionConfiguration(File collect_config_file)
69 {
70 load(collect_config_file);
71 }
72
73 private void load(File collect_config_file) {
74 this.collect_config_file = collect_config_file;
75 this.collect_config_filename = collect_config_file.getName();
76 // parse the XML template
77 document = XMLTools.parseXMLFile("xml/CollectionConfig.xml", true);
78 String filename = collect_config_filename.toLowerCase();
79
80 if (filename.endsWith(".cfg"))
81 {
82 saved_config_file_string = CollectCfgReadWrite.parse(collect_config_file, document);
83 }
84 else if (filename.endsWith(".xml"))
85 {
86 CollectionConfigXMLReadWrite.parse(collect_config_file, document);
87 }
88
89 //XMLTools.printXMLNode(document.getDocumentElement());
90 }
91
92
93 public void reload() {
94 load(this.collect_config_file);
95 }
96
97 static public Element createElement(String element_name)
98 {
99 return document.createElement(element_name);
100 }
101
102 /** Gives the preferred ordering of commands */
103 static final public String[] COMMAND_ORDER = { 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.SORTS_ELEMENT, StaticStrings.SORT_DEFAULT_ELEMENT, StaticStrings.FACETS_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 };
104
105 /**
106 * Find the best insertion position for the given DOM Element. This should
107 * try to match command tag, and if found should then try to group by name
108 * or type (eg CollectionMeta), or append to end is no such grouping exists
109 * (eg Plugins). Failing a command match it will check against the command
110 * order for the best insertion location.
111 *
112 * @param target_element
113 * the command Element to be inserted
114 * @return the Element which the given command should be inserted before, or
115 * null to append to end of list
116 */
117 static public Node findInsertionPoint(Element target_element)
118 {
119 ///ystem.err.println("Find insertion point: " + target_element.getNodeName());
120 String target_element_name = target_element.getNodeName();
121 Element document_element = document.getDocumentElement();
122 // Try to find commands with the same tag.
123 NodeList matching_elements = document_element.getElementsByTagName(target_element_name);
124 // If we found matching elements, then we have our most likely insertion location, so check within for groupings
125 if (matching_elements.getLength() != 0)
126 {
127 ///ystem.err.println("Found matching elements.");
128 // Only CollectionMeta are grouped.
129 if (target_element_name.equals(StaticStrings.COLLECTIONMETADATA_ELEMENT))
130 {
131 ///ystem.err.println("Dealing with collection metadata");
132 // 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.
133 // So if the command to be added is special add it immediately after any other special command
134 if (target_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR))
135 {
136 int index = 0;
137 Element matched_element = (Element) matching_elements.item(index);
138 Element sibling_element = (Element) matched_element.getNextSibling();
139 while (sibling_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR))
140 {
141 index++;
142 matched_element = (Element) matching_elements.item(index);
143 sibling_element = (Element) matched_element.getNextSibling();
144 }
145 if (sibling_element.getNodeName().equals(NEWLINE_ELEMENT))
146 {
147 Element newline_element = document.createElement(NEWLINE_ELEMENT);
148 document_element.insertBefore(newline_element, sibling_element);
149 }
150 return sibling_element;
151 }
152 // Otherwise try to find a matching 'name' and add after the last one in that group.
153 else
154 {
155 int index = 0;
156 target_element_name = target_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
157 boolean found = false;
158 // Skip all of the special metadata
159 Element matched_element = (Element) matching_elements.item(index);
160 while (matched_element.getAttribute(StaticStrings.SPECIAL_ATTRIBUTE).equals(StaticStrings.TRUE_STR))
161 {
162 index++;
163 matched_element = (Element) matching_elements.item(index);
164 }
165 // Begin search
166 while (!found && matched_element != null)
167 {
168 if (matched_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(target_element_name))
169 {
170 found = true;
171 }
172 else
173 {
174 index++;
175 matched_element = (Element) matching_elements.item(index);
176 }
177 }
178 // If we found a match, we need to continue checking until we find the last name match.
179 if (found)
180 {
181 index++;
182 Element previous_sibling = matched_element;
183 Element sibling_element = (Element) matching_elements.item(index);
184 while (sibling_element != null && sibling_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(target_element_name))
185 {
186 previous_sibling = sibling_element;
187 index++;
188 sibling_element = (Element) matching_elements.item(index);
189 }
190 // 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!
191 return previous_sibling.getNextSibling();
192 }
193 // If not found we just add after last metadata element
194 else
195 {
196 Element last_element = (Element) matching_elements.item(matching_elements.getLength() - 1);
197 return last_element.getNextSibling();
198 }
199 }
200
201 }
202 else
203 {
204 ///ystem.err.println("Not dealing with collection meta.");
205 Element matched_element = (Element) matching_elements.item(matching_elements.getLength() - 1);
206 // 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)
207 Node sibling_element = matched_element.getNextSibling();
208 if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT))
209 {
210 Element newline_element = document.createElement(NEWLINE_ELEMENT);
211 document_element.insertBefore(newline_element, sibling_element);
212 }
213 return sibling_element; // Note that this may be null
214 }
215 }
216 ///ystem.err.println("No matching elements found.");
217 // Locate where this command is in the ordering
218 int command_index = -1;
219 for (int i = 0; command_index == -1 && i < COMMAND_ORDER.length; i++)
220 {
221 if (COMMAND_ORDER[i].equals(target_element_name))
222 {
223 command_index = i;
224 }
225 }
226 ///ystem.err.println("Command index is: " + command_index);
227 // Now move forward, checking for existing elements in each of the preceeding command orders.
228 int preceeding_index = command_index - 1;
229 ///ystem.err.println("Searching before the target command.");
230 while (preceeding_index >= 0)
231 {
232 matching_elements = document_element.getElementsByTagName(COMMAND_ORDER[preceeding_index]);
233 // If we've found a match
234 if (matching_elements.getLength() > 0)
235 {
236 // We add after the last element
237 Element matched_element = (Element) matching_elements.item(matching_elements.getLength() - 1);
238 // 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)
239 Node sibling_element = matched_element.getNextSibling();
240 if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT))
241 {
242 Element newline_element = document.createElement(NEWLINE_ELEMENT);
243 document_element.insertBefore(newline_element, sibling_element);
244 }
245 return sibling_element; // Note that this may be null
246 }
247 preceeding_index--;
248 }
249 // If all that fails, we now move backwards through the commands
250 int susceeding_index = command_index + 1;
251 ///ystem.err.println("Searching after the target command.");
252 while (susceeding_index < COMMAND_ORDER.length)
253 {
254 matching_elements = document_element.getElementsByTagName(COMMAND_ORDER[susceeding_index]);
255 // If we've found a match
256 if (matching_elements.getLength() > 0)
257 {
258 // We add before the first element
259 Element matched_element = (Element) matching_elements.item(0);
260 // 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)
261 Node sibling_element = matched_element.getPreviousSibling();
262 if (sibling_element != null && sibling_element.getNodeName().equals(NEWLINE_ELEMENT))
263 {
264 Element newline_element = document.createElement(NEWLINE_ELEMENT);
265 document_element.insertBefore(newline_element, sibling_element);
266 }
267 return sibling_element; // Note that this may be null
268 }
269 susceeding_index++;
270 }
271 // Well. Apparently there are no other commands in this collection configuration. So append away...
272 return null;
273 }
274
275 static public NodeList getElementsByTagName(String element_name)
276 {
277 return document.getDocumentElement().getElementsByTagName(element_name);
278 }
279
280 public Element getDocumentElement()
281 {
282 return document.getDocumentElement();
283 }
284
285 /**
286 * This debug facility shows the currently loaded collect.cfg or
287 * CollectConfig.xml file as a DOM tree.
288 */
289 public void display()
290 {
291 JDialog dialog = new JDialog(Gatherer.g_man, "Collection Configuration", false);
292 dialog.setSize(400, 400);
293 JPanel content_pane = (JPanel) dialog.getContentPane();
294 final DOMTree tree = new DOMTree(document);
295 JButton refresh_button = new GLIButton("Refresh Tree");
296 refresh_button.addActionListener(new ActionListener()
297 {
298 public void actionPerformed(ActionEvent event)
299 {
300 tree.setDocument(document);
301 }
302 });
303 content_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
304 content_pane.setLayout(new BorderLayout());
305 content_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
306 content_pane.add(refresh_button, BorderLayout.SOUTH);
307 dialog.setVisible(true);
308 }
309
310 public File getFile()
311 {
312 return collect_config_file;
313 }
314
315 public Element getCreator()
316 {
317 Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_CREATOR_ELEMENT, null, null);
318 element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_CREATOR_STR);
319 element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
320 return element;
321 }
322
323 public Element getMaintainer()
324 {
325 Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_MAINTAINER_ELEMENT, null, null);
326 element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_MAINTAINER_STR);
327 element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
328 return element;
329 }
330
331 /** Retrieve or create the languages Element. */
332 public Element getLanguages()
333 {
334 return getOrCreateElementByTagName(StaticStrings.LANGUAGES_ELEMENT, null, null);
335 }
336
337 public Element getLanguageMetadata()
338 {
339 return getOrCreateElementByTagName(StaticStrings.LANGUAGE_METADATA_ELEMENT, null, null);
340 }
341
342 public Element getLevels()
343 {
344 return getOrCreateElementByTagName(StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVELS_STR);
345 }
346
347 public Element getLevelDefault()
348 {
349 return getOrCreateElementByTagName(StaticStrings.INDEXOPTION_DEFAULT_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.LEVEL_DEFAULT_STR);
350 }
351
352 public Element getIndexOptions()
353 {
354 return getOrCreateElementByTagName(StaticStrings.INDEXOPTIONS_ELEMENT, StaticStrings.NAME_ATTRIBUTE, StaticStrings.INDEXOPTIONS_STR);
355 }
356
357 /**
358 * Retrieve or create the indexes Element. Note that this method behaves
359 * differently from the other getBlah methods, in that it also has to keep
360 * in mind that indexes come in two flavours, MG and MGPP.
361 */
362 public Element getMGIndexes()
363 {
364 return getOrCreateElementByTagName(StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.FALSE_STR);
365 }
366
367 public Element getMGPPIndexes()
368 {
369 return getOrCreateElementByTagName(StaticStrings.INDEXES_ELEMENT, StaticStrings.MGPP_ATTRIBUTE, StaticStrings.TRUE_STR);
370 }
371
372 public Element getSorts()
373 {
374 return getOrCreateElementByTagName(StaticStrings.SORTS_ELEMENT, null, null);
375 }
376 public Element getFacets()
377 {
378 return getOrCreateElementByTagName(StaticStrings.FACETS_ELEMENT, null, null);
379 }
380 public Element getPublic()
381 {
382 Element element = getOrCreateElementByTagName(StaticStrings.COLLECTIONMETADATA_PUBLIC_ELEMENT, null, null);
383 element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.COLLECTIONMETADATA_PUBLIC_STR);
384 element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
385 return element;
386 }
387
388 public Element getBuildType()
389 {
390 Element element = getOrCreateElementByTagName(StaticStrings.BUILDTYPE_ELEMENT, null, null);
391 element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.BUILDTYPE_STR);
392 element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
393 return element;
394
395 }
396
397 public Element getDatabaseType()
398 {
399 Element element = getOrCreateElementByTagName(StaticStrings.DATABASETYPE_ELEMENT, null, null);
400 element.setAttribute(StaticStrings.NAME_ATTRIBUTE, StaticStrings.DATABASETYPE_STR);
401 element.setAttribute(StaticStrings.SPECIAL_ATTRIBUTE, StaticStrings.TRUE_STR);
402 return element;
403
404 }
405
406 /** Retrieve or create the subindexes Element. */
407 public Element getSubIndexes()
408 {
409 return getOrCreateElementByTagName(StaticStrings.SUBCOLLECTION_INDEXES_ELEMENT, null, null);
410 }
411
412 /** Retrieve or create the supercollections Element. */
413 public Element getSuperCollection()
414 {
415 return getOrCreateElementByTagName(StaticStrings.SUPERCOLLECTION_ELEMENT, null, null);
416 }
417
418 public boolean ready()
419 {
420 return document != null;
421 }
422
423 /** ************************** Private Methods ***************************/
424
425 /** Retrieve or create the indexes Element. */
426 static private Element getOrCreateElementByTagName(String name, String conditional_attribute, String required_value)
427 {
428 Element document_element = document.getDocumentElement();
429 NodeList elements = document_element.getElementsByTagName(name);
430 int elements_length = elements.getLength();
431 if (elements_length > 0)
432 {
433 if (conditional_attribute == null)
434 {
435 document_element = null;
436 return (Element) elements.item(0);
437 }
438 else
439 {
440 for (int i = 0; i < elements_length; i++)
441 {
442 Element element = (Element) elements.item(i);
443 if (element.getAttribute(conditional_attribute).equals(required_value))
444 {
445 document_element = null;
446 return element;
447 }
448 element = null;
449 }
450 }
451 }
452 // Create the element
453 Element element = document.createElement(name);
454 // If there was a property set it
455 if (conditional_attribute != null)
456 {
457 element.setAttribute(conditional_attribute, required_value);
458 }
459 Node target_node = findInsertionPoint(element);
460 if (target_node != null)
461 {
462 document_element.insertBefore(element, target_node);
463 }
464 else
465 {
466 document_element.appendChild(element);
467 }
468 document_element = null;
469 return element;
470 }
471
472 /**
473 * Write the text to the buffer. This is used so we don't have to worry
474 * about storing intermediate String values just so we can calaulate length
475 * and offset.
476 *
477 * @param writer
478 * the BufferedWriter to which the str will be written
479 * @param str
480 * the String to be written
481 */
482 private void write(BufferedWriter writer, String str) throws IOException
483 {
484 writer.write(str, 0, str.length());
485 }
486
487 public void saveIfNecessary()
488 {
489
490 // Generate a string version of internal document
491 String config_file_string = null;
492 if (Gatherer.GS3)
493 {
494 config_file_string = CollectionConfigXMLReadWrite.generateStringVersion(document);
495 }
496 else
497 {
498 config_file_string = CollectCfgReadWrite.generateStringVersion(document);
499 }
500 // compare to saved version
501 if (saved_config_file_string != null)
502 {
503 if (saved_config_file_string.equals(config_file_string))
504 {
505 DebugStream.println(collect_config_filename + " file hasn't changed so no save necessary...");
506 return;
507 }
508 }
509
510 // We need to save...
511 DebugStream.println(collect_config_filename + " file has changed, saving now...");
512
513 // If we're using the Local Library we must release the collection before writing to the collect.cfg file
514 String collection_name = CollectionManager.getLoadedCollectionName(true); // url style slash
515 boolean collection_released = false;
516 if (Gatherer.c_man.built() && LocalLibraryServer.isRunning() == true)
517 {
518 // Release the collection
519 LocalLibraryServer.releaseCollection(collection_name);
520 collection_released = true;
521 }
522
523 // Make a backup of the existing config file
524 if (collect_config_file.exists())
525 {
526 String config_filename;
527 String backup_filename;
528 if (Gatherer.GS3)
529 {
530 config_filename = Utility.COLLECTION_CONFIG_XML;
531 backup_filename = Utility.COLLECTION_CONFIG_BAK;
532 }
533 else
534 {
535 config_filename = StaticStrings.COLLECT_CFG;
536 backup_filename = Utility.COLLECT_BAK;
537 }
538 File original_file = new File(collect_config_file.getParentFile(), config_filename);
539 File backup_file = new File(collect_config_file.getParentFile(), backup_filename);
540 if (backup_file.exists())
541 {
542 backup_file.delete();
543 }
544 if (!original_file.renameTo(backup_file))
545 {
546 System.err.println("Warning: can't rename " + config_filename + " to " + backup_filename);
547 }
548 }
549
550 // now save the file
551 if (Gatherer.GS3)
552 {
553 CollectionConfigXMLReadWrite.save(collect_config_file, document);
554 }
555 else
556 {
557 // we have already converted to string, so save here
558 try
559 {
560 OutputStream ostream = new FileOutputStream(collect_config_file);
561 Writer file_writer = new OutputStreamWriter(ostream, ENCODING);
562 BufferedWriter buffered_writer = new BufferedWriter(file_writer);
563 buffered_writer.write(config_file_string);
564 buffered_writer.close();
565 }
566 catch (Exception exception)
567 {
568 DebugStream.println("Error in CollectionConfiguration.save(): " + exception);
569 DebugStream.printStackTrace(exception);
570 }
571
572 }
573
574 // save the string version
575 saved_config_file_string = config_file_string;
576
577 // If we're using a remote Greenstone server, upload the new collect.cfg file
578 if (Gatherer.isGsdlRemote)
579 {
580 Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection_name, collect_config_file);
581 }
582
583 // Now re-add the collection to the Local Library server
584 if (collection_released)
585 {
586 LocalLibraryServer.addCollection(collection_name);
587 }
588 }
589
590}
Note: See TracBrowser for help on using the repository browser.