source: trunk/gli/src/org/greenstone/gatherer/msm/MetadataSet.java@ 4527

Last change on this file since 4527 was 4504, checked in by kjdon, 21 years ago

fixed up adding and removing elements from teh set - need to add/remove their associated value trees too./shutdown.sh also removeValueTree now deals with GValueModels rather than Elements which it did in a really old version of value trees

  • Property svn:keywords set to Author Date Id Revision
File size: 24.0 KB
Line 
1package org.greenstone.gatherer.msm;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38import java.io.*;
39import java.net.*;
40import java.util.*;
41import org.apache.xerces.dom.*;
42import org.greenstone.gatherer.Gatherer;
43import org.greenstone.gatherer.valuetree.GValueModel;
44import org.greenstone.gatherer.valuetree.GValueNode;
45import org.greenstone.gatherer.util.Utility;
46import org.w3c.dom.*;
47/** An semi-data class to hold details about a loaded metadata set, it also provides some methods to manipulating the data within.
48 * @author John Thompson, Greenstone Digital Library, University of Waikato
49 * @version 2.3b
50 */
51public class MetadataSet {
52 /** The <Strong>Document</strong> of the DOM model. */
53 private Document document = null;
54 /** The document <strong>Element</strong> of the DOM model. */
55 private Element root = null;
56 /** The <strong>File</strong> this metadata set was loaded from. */
57 private File file = null;
58 /** A mapping from metadata elements to the root element of the value trees for that element. */
59 private Hashtable value_trees = null;
60 /** The list of metadata elements which are, of course, children of the root node. */
61 private NodeList elements = null;
62 /** The description of this metadata set. Cached as it takes more computation time. */
63 private String description = null;
64 /** The name of this metadata set. Cached as it takes more computation time. */
65 private String name = null;
66 /** An element of the tree pruning filter enumeration, that indicates all nodes in the tree should be retained. */
67 static final int ALL_VALUES = 1;
68 /** An element of the tree pruning filter enumeration, that indicates only metadata Subject nodes or higher should remain after pruning. */
69 static final int SUBJECTS_ONLY = 2;
70 /** An element of the tree pruning filter enumeration, that indicates no value nodes i.e. the entire AssignedValues subtree, should remain after pruning. */
71 static final int NO_VALUES = 3;
72
73 public MetadataSet(String metadata_template) {
74 URL url = ClassLoader.getSystemResource(metadata_template);
75 try {
76 init(new File(URLDecoder.decode(url.getFile(), "UTF-8")));
77 }
78 catch(UnsupportedEncodingException exception) {
79 Gatherer.printStackTrace(exception);
80 }
81 }
82
83 /** Constructor.
84 * @param file The file the metadata set should be loaded from.
85 */
86 public MetadataSet(File file) {
87 init(file);
88 }
89
90 /** Metadata Set already parsed constructor.
91 * @param file The file the metadata was loaded from.
92 * @param document The DOM model <strong>Document</strong> containing the metadata set.
93 */
94 public MetadataSet(File file, Document document) {
95 this.document = document;
96 this.elements = document.getElementsByTagName("Element");
97 this.file = file;
98 this.root = document.getDocumentElement();
99 this.value_trees = new Hashtable();
100 // Now for each element read in its value tree if present.
101 for(int i = elements.getLength() - 1; i >= 0; i--) {
102 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
103 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
104 ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
105 if(value_file.exists()) {
106 Document value_document = Utility.parse(value_file, false);
107 if(value_document != null) {
108 value_trees.put(value_element, new GValueModel(value_element, value_document));
109 }
110 //else {
111 //atherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
112 //}
113 }
114 }
115 }
116 /** Copy constructor.
117 * @param original The original metadata set to copy from.
118 */
119 public MetadataSet(MetadataSet original) {
120 this.value_trees = new Hashtable();
121 // We have to create a new document.
122 document = new DocumentImpl(original.getDocument().getDoctype());
123 root = (Element) document.importNode(original.getDocument().getDocumentElement(), true);
124 document.appendChild(root);
125 elements = root.getElementsByTagName("Element");
126 file = original.getFile();
127 // Now for each element read in its value tree if present.
128 for(int i = elements.getLength() - 1; i >= 0; i--) {
129 ElementWrapper value_element_wrapper = new ElementWrapper((Element)elements.item(i));
130 GValueModel value_tree = original.getValueTree(value_element_wrapper);
131 Document value_document = value_tree.getDocument();
132 Document value_document_copy = new DocumentImpl(value_document.getDoctype());
133 Element value_element = value_document.getDocumentElement();
134 Element value_element_copy = (Element) value_document_copy.importNode(value_element, true);
135 value_document_copy.appendChild(value_element_copy);
136 GValueModel value_tree_copy = new GValueModel(value_element_wrapper, value_document_copy);
137 value_trees.put(value_element_wrapper, value_tree_copy);
138 }
139 }
140 /** Conditional copy constructor.
141 * @param original The original metadata set to copy from.
142 * @param condition An <i>int</i> which matches one of the tree pruning filter types.
143 */
144 public MetadataSet(MetadataSet original, int condition) {
145 this(original);
146 // Now based on condition, we may have to remove some nodes from
147 // this model.
148 switch(condition) {
149 case ALL_VALUES:
150 // Do nothing.
151 break;
152 case SUBJECTS_ONLY:
153 // For each element retrieve its AssignedValues element.
154 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
155 ElementWrapper value_element = (ElementWrapper)keys.nextElement();
156 GValueModel value_tree = (GValueModel)value_trees.get(value_element);
157 Document value_tree_document = value_tree.getDocument();
158 Element value_tree_root_element = value_tree_document.getDocumentElement();
159 // Traverse tree and remove leaf nodes.
160 MSMUtils.traverseTree(value_tree_root_element, MSMUtils.NONE, true);
161 }
162 break;
163 case NO_VALUES:
164 // Remove assigned values trees.
165 value_trees.clear();
166 break;
167 }
168 }
169
170 /** Add a mds level attribute.
171 * @param name The name of the attribute to add as a <Strong>String</strong>.
172 * @param value The value as a <strong>String</strong>.
173 */
174 public void addAttribute(String name, String value) {
175 root.setAttribute(name, value);
176 }
177
178 /** Add a new default metadata element with the given name to this metadata set.
179 * @param name The name of this element as a <strong>String</strong>.
180 * @return An <strong>ElementWrapper</strong> around the newly created element or null if the element was not created.
181 */
182 public ElementWrapper addElement(String name) {
183 Text text = document.createTextNode(name);
184 Element identifier = document.createElementNS("","Attribute");
185 identifier.setAttribute("name","identifier");
186 identifier.appendChild(text);
187 Element element = document.createElementNS("","Element");
188 element.setAttribute("name",name);
189 element.appendChild(identifier);
190 root.appendChild(element);
191 ElementWrapper wrapper = new ElementWrapper(element);
192 ///ystem.err.println("Added a new element " + wrapper);
193 return wrapper;
194 }
195
196 /** Method to add a new metadata element to this metadata set, if and only if the element is not already present.
197 * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
198 * @param model A <strong>GValueModel</strong> value tree
199 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
200 */
201 public String addElement(Element others_element, GValueModel model) {
202 if(!containsElement(others_element.getAttribute("name"))) {
203 // First get ownership of the new element, then add it.
204 Element our_element = (Element)document.importNode(others_element, true);
205 // add the value tree
206 root.appendChild(our_element);
207 if (model != null) {
208 addValueTree(new ElementWrapper(our_element), model);
209 }
210 return null;
211 }
212 else {
213 return "MSMPrompt.Name_Exists";
214 }
215 }
216
217 /** Method to add a new metadata element with the specified new name to this metadata set, if and only if the name is not already in use.
218 * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
219 * @param new_name The new name to be given this element, as a <strong>String</strong>.
220 * @param model A <strong>GValueModel</strong> value tree
221 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
222 */
223 public String addElement(Element others_element, String new_name, GValueModel model) {
224 if(!containsElement(new_name)) {
225 // First get ownership of the new element, then add it.
226 Element our_element =
227 (Element) document.importNode(others_element, true);
228 // Change name
229 our_element.setAttribute("name", new_name);
230 // we also want to change the english identifier of this element
231 MSMUtils.setIdentifier(our_element, new_name);
232 // Add it to teh set
233 root.appendChild(our_element);
234 // add the value tree
235 if (model != null) {
236 addValueTree(new ElementWrapper(our_element), model);
237 }
238 return null;
239 }
240 else {
241 return "MSMPrompt.Name_Exists";
242 }
243 }
244 /** Add a value tree to a given metadata element.
245 * @param element The <strong>ElementWrapper</strong> containing the element you wish to add a value tree for.
246 * @param model A <strong>GValueModel</strong> value tree
247 */
248 public void addValueTree(ElementWrapper element, GValueModel model) {
249 ///ystem.err.println("Adding value tree for " + element.toString());
250 value_trees.put(element, model);
251 }
252
253 public int compare(Element e1, Element e2) {
254 int result = 0;
255 // Check that they're not the same element.
256 if(e1 != e2) {
257 int index_e1 = -1;
258 int index_e2 = -1;
259 // Locate the indexes for each element.
260 for(int i = 0; i < elements.getLength(); i++) {
261 Node element = elements.item(i);
262 if(element == e1) {
263 index_e1 = i;
264 }
265 if(elements == e2) {
266 index_e2 = i;
267 }
268 }
269 if(index_e1 < index_e2) {
270 result = -1;
271 }
272 else {
273 result = 1;
274 }
275 }
276 return result;
277 }
278
279 /** A method to determine if this metadata set contains an element with a certain name.
280 * @param name A <strong>String</strong> which is the name of the element whose presence we are checking.
281 * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise.
282 */
283 public boolean containsElement(String name) {
284 for(int i = 0; i < elements.getLength(); i++) {
285 Element sibling = (Element) elements.item(i);
286 String sibling_name = sibling.getAttribute("name");
287 if(sibling_name.equals(name)) {
288 return true;
289 }
290 }
291 return false;
292 }
293
294 public NamedNodeMap getAttributes() {
295 return root.getAttributes();
296 }
297
298 /** Method to retrieve the contact address of the metadata set creator.
299 * @return A <strong>String</strong> containing the address.
300 */
301 public String getContact() {
302 return root.getAttribute("contact");
303 }
304 /** Method to retrieve the name of the creator of this metadata set.
305 * @return A <strong>String</strong> containing the name.
306 */
307 public String getCreator() {
308 return root.getAttribute("creator");
309 }
310 /** Method to retrieve the description of this metadata set. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first description found.
311 * @return The description as a <strong>String</strong>.
312 */
313 public String getDescription() {
314 if(description == null) {
315 // Determine the code.
316 String language_code = Gatherer.dictionary.getLanguage();
317 // Recover all Description elements
318 NodeList descriptions = document.getElementsByTagName("Description");
319 // Iterate through the available descriptions looking for the appropriate one. Also make note of the first description, then overwrite it with any english one.
320 boolean found = false;
321 for(int i = 0; !found && i < descriptions.getLength(); i++) {
322 Element pos_description = (Element) descriptions.item(i);
323 String pos_description_code = pos_description.getAttribute("language");
324 if(pos_description_code.equalsIgnoreCase(language_code)) {
325 description = MSMUtils.getValue(pos_description);
326 found = true;
327 }
328 else if(pos_description_code.equalsIgnoreCase("en")) {
329 description = MSMUtils.getValue(pos_description);
330 }
331 else if(description == null) {
332 description = MSMUtils.getValue(pos_description);
333 }
334 pos_description_code = null;
335 pos_description = null;
336 }
337 descriptions = null;
338 language_code = null;
339 // Failing all that set an error message
340 if(description == null) {
341 description = Gatherer.dictionary.get("MSM.No_Description");
342 }
343 }
344 return description;
345 }
346 /** Method to retrieve the <strong>Document</strong> associated with this metadata set.
347 * @return The <strong>Document</strong> representing this metadata set.
348 */
349 public Document getDocument() {
350 return document;
351 }
352 /** Method to retrieve the metadata element indicated by an index.
353 * @param index An <i>int</i> specifying the required element.
354 * @return The <strong>Element</strong> at the index.
355 */
356 public Element getElement(int index) {
357 return (Element)elements.item(index);
358 }
359 /** This method is used to acquire a reference to the element which matches the given metadata. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
360 * @param metadata A <strong>Metadata</strong> object representing an element and value assignment.
361 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
362 */
363 public Element getElement(Metadata metadata) {
364 return metadata.getElement().getElement();
365 }
366 /** This method is used to acquire a reference to the element which has the name specified. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
367 * @param name A <strong>String</strong> stating the desired objects name.
368 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
369 */
370 public Element getElement(String name) {
371 // Strip any namespace.
372 while(name.indexOf(".") != -1 && !name.equals(".")) {
373 name = name.substring(name.indexOf(".") + 1);
374 }
375 ///ystem.err.println("Get element named " + name);
376 for(int i = 0; i < elements.getLength(); i++) {
377 Element element = (Element) elements.item(i);
378 ///ystem.err.println("Compare to: " + element.getAttribute("name"));
379 if(element.getAttribute("name").equals(name)) {
380 return element;
381 }
382 }
383 return null;
384 }
385 /** Method to acquire a list of all the elements in this metadata set.
386 * @return A <strong>NodeList</strong> containing all of this sets elements.
387 */
388 public NodeList getElements() {
389 return elements;
390 }
391 /** Method to retrieve the original file this metadata set was created from.
392 * @return A <strong>File</strong>.
393 */
394 public File getFile() {
395 return file;
396 }
397 /** Get the last changed attribute.
398 * @return Last changed as a <strong>String</strong>.
399 */
400 public String getLastChanged() {
401 return root.getAttribute("lastchanged");
402 }
403 /** Method to get this metadata sets name. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first name found.
404 * @return A <strong>String</strong> which contains its name.
405 */
406 public String getName() {
407 if(name == null) {
408 // Determine the code.
409 String language_code = Gatherer.dictionary.getLanguage();
410 // Recover all Name elements
411 NodeList names = document.getElementsByTagName("Name");
412 // Iterate through the available names looking for the appropriate one. Also make note of the first name, then overwrite it with any english one.
413 boolean found = false;
414 for(int i = 0; !found && i < names.getLength(); i++) {
415 Element pos_name = (Element) names.item(i);
416 String pos_name_code = pos_name.getAttribute("language");
417 if(pos_name_code.equalsIgnoreCase(language_code)) {
418 name = MSMUtils.getValue(pos_name);
419 found = true;
420 }
421 else if(pos_name_code.equalsIgnoreCase("en")) {
422 name = MSMUtils.getValue(pos_name);
423 }
424 else if(name == null) {
425 name = MSMUtils.getValue(pos_name);
426 }
427 pos_name_code = null;
428 pos_name = null;
429 }
430 names = null;
431 language_code = null;
432 // Failing all that set an error message
433 if(name == null) {
434 name = Gatherer.dictionary.get("MSM.No_Name");
435 }
436 }
437 return name;
438 }
439 /** Method to retrieve this metadata sets namespace.
440 * @return The namespace as a <strong>String</strong>.
441 */
442 public String getNamespace() {
443 return root.getAttribute("namespace");
444 }
445 /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set.
446 * @return An <strong>Element</strong> which is at the root of the modal.
447 */
448 public Element getRoot() {
449 return root;
450 }
451 /** Retrieve the value tree from this set that matches the given element.
452 * @param element The target <strong>ElementWrapper</strong>.
453 * @return A <strong>GValueModel</strong> value tree, or <i>null</i> if no such element or value tree.
454 */
455 public GValueModel getValueTree(ElementWrapper element) {
456 GValueModel value_tree = null;
457 // Stinking hashtable get doesn't use the overridden equals. So I'll do a loop, which should be pretty small ie O(n) for n metadata elements.
458 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
459 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
460 if(sibling.equals(element)) {
461 value_tree = (GValueModel) value_trees.get(sibling);
462 break;
463 }
464 }
465 // If we've found no value tree, create a new one.
466 if(value_tree == null) {
467 value_tree = new GValueModel(element);
468 value_trees.put(element, value_tree);
469 }
470 return value_tree;
471 }
472 /** Remove a mds level attribute.
473 * @param name The name of the attribute to remove.
474 */
475 public void removeAttribute(String name) {
476 root.removeAttribute(name);
477 }
478 /** Method to remove the given element from this metadata set.
479 * @param element The <strong>Element</strong> to be removed.
480 */
481 public void removeElement(Element element) {
482 // we need to remove the value tree too!!
483 removeValueTree(new ElementWrapper(element));
484 root.removeChild(element);
485 }
486 /** Used to remove the value tree for a specific element.
487 * @param element The <strong>ElementWrapper</strong> whose tree you wish to remove.
488 * @return The <strong>GValueModel</strong> we just removed
489 */
490 public GValueModel removeValueTree(ElementWrapper element) {
491 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
492 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
493 if(sibling.equals(element)) {
494 GValueModel value_tree = (GValueModel) value_trees.get(sibling);
495 value_trees.remove(sibling);
496 return value_tree;
497 }
498 }
499 return null;
500 }
501 /** Set one of the mds level attributes.
502 * @param name The attribute to change.
503 * @param value its new value.
504 */
505 public void setAttribute(String name, String value) {
506 root.setAttribute(name, value);
507 }
508 /** Once the metadata set has been saved to a different location, this is used to update the file parameter.
509 * @param file The new location of this metadata set <strong>File</strong>.
510 */
511 public void setFile(File file) {
512 this.file = file;
513 }
514
515 public void setName(String name) {
516 // Retrieve the name element. We look for the first english one.
517 Element name_element = null;
518 Element metadataset_element = document.getDocumentElement();
519 NodeList name_elements = metadataset_element.getElementsByTagName(Utility.NAME_ELEMENT);
520 for(int i = 0; i < name_elements.getLength(); i++) {
521 Element possible_name_element = (Element) name_elements.item(i);
522 if(possible_name_element.getAttribute(Utility.LANGUAGE_ATTRIBUTE).equals(Utility.ENGLISH_VALUE)) {
523 // Found it.
524 name_element = possible_name_element;
525 }
526 }
527 // If there is none add one. Note that we can only add english metadata sets. Although others can edit them to add further names as necessary.
528 if(name_element == null) {
529 name_element = document.createElement(Utility.NAME_ELEMENT);
530 name_element.setAttribute(Utility.LANGUAGE_ATTRIBUTE, Utility.ENGLISH_VALUE);
531 metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild());
532 }
533 // Replace the text node
534 while(name_element.hasChildNodes()) {
535 name_element.removeChild(name_element.getFirstChild());
536 }
537 name_element.appendChild(document.createTextNode(name));
538 }
539
540 /** Method to determine the number of elements in this set.
541 * @return An <i>int</i> specifying the element count.
542 */
543 public int size() {
544 return elements.getLength();
545 }
546 /** Method to translate this class into a meaningful string, which in this case is the metadata sets name.
547 * @return The metadata sets name as a <strong>String</strong>.
548 */
549 public String toString() {
550 String name = getName();
551 // If there is no given name, then use the namespace as there is garaunteed to be one of them.
552 if(name == null || name.length() == 0) {
553 name = root.getAttribute("namespace");
554 }
555 // Append namespace
556 String namespace = root.getAttribute("namespace");
557 if(namespace == null || namespace.equals("")) {
558 namespace = Utility.EXTRACTED_METADATA_NAMESPACE;
559 }
560 name = name + " (" + namespace + ")";
561 return name;
562 }
563
564 private void init(File file) {
565 this.file = file;
566 this.value_trees = new Hashtable();
567 this.document = Utility.parse(file, false);
568 if(document != null) {
569 this.elements = document.getElementsByTagName("Element");
570 this.root = document.getDocumentElement();
571 // Now for each element read in its value tree if present.
572 for(int i = elements.getLength() - 1; i >= 0; i--) {
573 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
574 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
575 ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
576 if(value_file.exists()) {
577 Document value_document = Utility.parse(value_file, false);
578 if(value_document != null) {
579 value_trees.put(value_element, new GValueModel(value_element, value_document));
580 }
581 else {
582 Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
583 }
584 }
585 }
586 }
587 else {
588 Gatherer.println("Error! Missing mds file: " + file.getAbsolutePath());
589 }
590 }
591}
Note: See TracBrowser for help on using the repository browser.