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

Last change on this file since 5777 was 5777, checked in by mdewsnip, 21 years ago

Fixed bug 169: metadata element names and identifiers being confused when importing metadata (in collect view). Names and identifiers are still opposite to the way I would have them, but at least the bug is fixed.

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