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

Last change on this file since 4331 was 4331, checked in by jmt12, 21 years ago

Method to compare two metadata elements for their ordering within the DOM model - John

  • Property svn:keywords set to Author Date Id Revision
File size: 23.6 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 Gatherer.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 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
199 */
200 public String addElement(Element others_element) {
201 if(!containsElement(others_element.getAttribute("name"))) {
202 // First get ownership of the new element, then add it.
203 Node our_element = document.importNode(others_element, true);
204 root.appendChild(our_element);
205 return null;
206 }
207 else {
208 return "Element name already exists!";
209 }
210 }
211
212 /** 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.
213 * @param others_element An <strong>Element</strong> we wish to add to this metadata set, that currently belongs to some other set.
214 * @param new_name The new name to be given this element, as a <strong>String</strong>.
215 * @return <i>null</i> if the add is successful, otherwise a <strong>String</strong> containing an error message (phrase key).
216 */
217 public String addElement(Element others_element, String new_name) {
218 if(!containsElement(new_name)) {
219 // First get ownership of the new element, then add it.
220 Element our_element =
221 (Element) document.importNode(others_element, true);
222 // Change name
223 our_element.setAttribute("name", new_name);
224 // Add it
225 root.appendChild(our_element);
226 return null;
227 }
228 else {
229 return "MSMPrompt.Name_Exists";
230 }
231 }
232 /** Add a value tree to a given metadata element.
233 * @param element The <strong>ElementWrapper</strong> containing the element you wish to add a value tree for.
234 * @param value_tree The root <strong>Element</strong> of the value tree.
235 */
236 public void addValueTree(ElementWrapper element, GValueModel model) {
237 ///ystem.err.println("Adding value tree for " + element.toString());
238 value_trees.put(element, model);
239 }
240
241 public int compare(Element e1, Element e2) {
242 int result = 0;
243 // Check that they're not the same element.
244 if(e1 != e2) {
245 int index_e1 = -1;
246 int index_e2 = -1;
247 // Locate the indexes for each element.
248 for(int i = 0; i < elements.getLength(); i++) {
249 Node element = elements.item(i);
250 if(element == e1) {
251 index_e1 = i;
252 }
253 if(elements == e2) {
254 index_e2 = i;
255 }
256 }
257 if(index_e1 < index_e2) {
258 result = -1;
259 }
260 else {
261 result = 1;
262 }
263 }
264 return result;
265 }
266
267 /** A method to determine if this metadata set contains an element with a certain name.
268 * @param name A <strong>String</strong> which is the name of the element whose presence we are checking.
269 * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise.
270 */
271 public boolean containsElement(String name) {
272 for(int i = 0; i < elements.getLength(); i++) {
273 Element sibling = (Element) elements.item(i);
274 String sibling_name = sibling.getAttribute("name");
275 if(sibling_name.equals(name)) {
276 return true;
277 }
278 }
279 return false;
280 }
281
282 public NamedNodeMap getAttributes() {
283 return root.getAttributes();
284 }
285
286 /** Method to retrieve the contact address of the metadata set creator.
287 * @return A <strong>String</strong> containing the address.
288 */
289 public String getContact() {
290 return root.getAttribute("contact");
291 }
292 /** Method to retrieve the name of the creator of this metadata set.
293 * @return A <strong>String</strong> containing the name.
294 */
295 public String getCreator() {
296 return root.getAttribute("creator");
297 }
298 /** 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.
299 * @return The description as a <strong>String</strong>.
300 */
301 public String getDescription() {
302 if(description == null) {
303 // Determine the code.
304 String language_code = Gatherer.dictionary.getLanguage();
305 // Recover all Description elements
306 NodeList descriptions = document.getElementsByTagName("Description");
307 // Iterate through the available descriptions looking for the appropriate one. Also make note of the first description, then overwrite it with any english one.
308 boolean found = false;
309 for(int i = 0; !found && i < descriptions.getLength(); i++) {
310 Element pos_description = (Element) descriptions.item(i);
311 String pos_description_code = pos_description.getAttribute("language");
312 if(pos_description_code.equalsIgnoreCase(language_code)) {
313 description = MSMUtils.getValue(pos_description);
314 found = true;
315 }
316 else if(pos_description_code.equalsIgnoreCase("en")) {
317 description = MSMUtils.getValue(pos_description);
318 }
319 else if(description == null) {
320 description = MSMUtils.getValue(pos_description);
321 }
322 pos_description_code = null;
323 pos_description = null;
324 }
325 descriptions = null;
326 language_code = null;
327 // Failing all that set an error message
328 if(description == null) {
329 description = Gatherer.dictionary.get("MSM.No_Description");
330 }
331 }
332 return description;
333 }
334 /** Method to retrieve the <strong>Document</strong> associated with this metadata set.
335 * @return The <strong>Document</strong> representing this metadata set.
336 */
337 public Document getDocument() {
338 return document;
339 }
340 /** Method to retrieve the metadata element indicated by an index.
341 * @param index An <i>int</i> specifying the required element.
342 * @return The <strong>Element</strong> at the index.
343 */
344 public Element getElement(int index) {
345 return (Element)elements.item(index);
346 }
347 /** 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.
348 * @param metadata A <strong>Metadata</strong> object representing an element and value assignment.
349 * @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.
350 */
351 public Element getElement(Metadata metadata) {
352 return metadata.getElement().getElement();
353 }
354 /** 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.
355 * @param name A <strong>String</strong> stating the desired objects name.
356 * @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.
357 */
358 public Element getElement(String name) {
359 // Strip any namespace.
360 while(name.indexOf(".") != -1 && !name.equals(".")) {
361 name = name.substring(name.indexOf(".") + 1);
362 }
363 ///ystem.err.println("Get element named " + name);
364 for(int i = 0; i < elements.getLength(); i++) {
365 Element element = (Element) elements.item(i);
366 ///ystem.err.println("Compare to: " + element.getAttribute("name"));
367 if(element.getAttribute("name").equals(name)) {
368 return element;
369 }
370 }
371 return null;
372 }
373 /** Method to acquire a list of all the elements in this metadata set.
374 * @return A <strong>NodeList</strong> containing all of this sets elements.
375 */
376 public NodeList getElements() {
377 return elements;
378 }
379 /** Method to retrieve the original file this metadata set was created from.
380 * @return A <strong>File</strong>.
381 */
382 public File getFile() {
383 return file;
384 }
385 /** Get the last changed attribute.
386 * @return Last changed as a <strong>String</strong>.
387 */
388 public String getLastChanged() {
389 return root.getAttribute("lastchanged");
390 }
391 /** 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.
392 * @return A <strong>String</strong> which contains its name.
393 */
394 public String getName() {
395 if(name == null) {
396 // Determine the code.
397 String language_code = Gatherer.dictionary.getLanguage();
398 // Recover all Name elements
399 NodeList names = document.getElementsByTagName("Name");
400 // Iterate through the available names looking for the appropriate one. Also make note of the first name, then overwrite it with any english one.
401 boolean found = false;
402 for(int i = 0; !found && i < names.getLength(); i++) {
403 Element pos_name = (Element) names.item(i);
404 String pos_name_code = pos_name.getAttribute("language");
405 if(pos_name_code.equalsIgnoreCase(language_code)) {
406 name = MSMUtils.getValue(pos_name);
407 found = true;
408 }
409 else if(pos_name_code.equalsIgnoreCase("en")) {
410 name = MSMUtils.getValue(pos_name);
411 }
412 else if(name == null) {
413 name = MSMUtils.getValue(pos_name);
414 }
415 pos_name_code = null;
416 pos_name = null;
417 }
418 names = null;
419 language_code = null;
420 // Failing all that set an error message
421 if(name == null) {
422 name = Gatherer.dictionary.get("MSM.No_Name");
423 }
424 }
425 return name;
426 }
427 /** Method to retrieve this metadata sets namespace.
428 * @return The namespace as a <strong>String</strong>.
429 */
430 public String getNamespace() {
431 return root.getAttribute("namespace");
432 }
433 /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set.
434 * @return An <strong>Element</strong> which is at the root of the modal.
435 */
436 public Element getRoot() {
437 return root;
438 }
439 /** Retrieve the value tree from this set that matches the given element.
440 * @param element The target <strong>ElementWrapper</strong>.
441 * @return A <strong>GValueModel</strong> value tree, or <i>null</i> if no such element or value tree.
442 */
443 public GValueModel getValueTree(ElementWrapper element) {
444 GValueModel value_tree = null;
445 ///ystem.err.println("Retrieving value tree for element: " + element.toString());
446 // 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.
447 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
448 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
449 if(sibling.equals(element)) {
450 value_tree = (GValueModel) value_trees.get(sibling);
451 }
452 }
453 // If we've found no value tree, create a new one.
454 if(value_tree == null) {
455 value_tree = new GValueModel(element);
456 value_trees.put(element, value_tree);
457 }
458 return value_tree;
459 }
460 /** Remove a mds level attribute.
461 * @param name The name of the attribute to remove.
462 */
463 public void removeAttribute(String name) {
464 root.removeAttribute(name);
465 }
466 /** Method to remove the given element from this metadata set.
467 * @param element The <strong>Element</strong> to be removed.
468 */
469 public void removeElement(Element element) {
470 root.removeChild(element);
471 }
472 /** Used to remove the value tree for a specific element.
473 * @param element The <strong>ElementWrapper</strong> whose tree you wish to remove.
474 * @return The <strong>Element</strong> at the root of the value tree we just removed.
475 */
476 public Element removeValueTree(ElementWrapper element) {
477 for(Enumeration keys = value_trees.keys(); keys.hasMoreElements(); ) {
478 ElementWrapper sibling = (ElementWrapper) keys.nextElement();
479 if(sibling.equals(element)) {
480 Element tree_root = (Element) value_trees.get(sibling);
481 value_trees.remove(sibling);
482 return tree_root;
483 }
484 }
485 return null;
486 }
487 /** Set one of the mds level attributes.
488 * @param name The attribute to change.
489 * @param value its new value.
490 */
491 public void setAttribute(String name, String value) {
492 root.setAttribute(name, value);
493 }
494 /** Once the metadata set has been saved to a different location, this is used to update the file parameter.
495 * @param file The new location of this metadata set <strong>File</strong>.
496 */
497 public void setFile(File file) {
498 this.file = file;
499 }
500
501 public void setName(String name) {
502 // Retrieve the name element. We look for the first english one.
503 Element name_element = null;
504 Element metadataset_element = document.getDocumentElement();
505 NodeList name_elements = metadataset_element.getElementsByTagName(Utility.NAME_ELEMENT);
506 for(int i = 0; i < name_elements.getLength(); i++) {
507 Element possible_name_element = (Element) name_elements.item(i);
508 if(possible_name_element.getAttribute(Utility.LANGUAGE_ATTRIBUTE).equals(Utility.ENGLISH_VALUE)) {
509 // Found it.
510 name_element = possible_name_element;
511 }
512 }
513 // 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.
514 if(name_element == null) {
515 name_element = document.createElement(Utility.NAME_ELEMENT);
516 name_element.setAttribute(Utility.LANGUAGE_ATTRIBUTE, Utility.ENGLISH_VALUE);
517 metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild());
518 }
519 // Replace the text node
520 while(name_element.hasChildNodes()) {
521 name_element.removeChild(name_element.getFirstChild());
522 }
523 name_element.appendChild(document.createTextNode(name));
524 }
525
526 /** Method to determine the number of elements in this set.
527 * @return An <i>int</i> specifying the element count.
528 */
529 public int size() {
530 return elements.getLength();
531 }
532 /** Method to translate this class into a meaningful string, which in this case is the metadata sets name.
533 * @return The metadata sets name as a <strong>String</strong>.
534 */
535 public String toString() {
536 String name = getName();
537 // If there is no given name, then use the namespace as there is garaunteed to be one of them.
538 if(name == null || name.length() == 0) {
539 name = root.getAttribute("namespace");
540 }
541 // Append namespace
542 String namespace = root.getAttribute("namespace");
543 if(namespace == null || namespace.equals("")) {
544 namespace = Utility.EXTRACTED_METADATA_NAMESPACE;
545 }
546 name = name + " (" + namespace + ")";
547 return name;
548 }
549
550 private void init(File file) {
551 this.file = file;
552 this.value_trees = new Hashtable();
553 this.document = Utility.parse(file, false);
554 if(document != null) {
555 this.elements = document.getElementsByTagName("Element");
556 this.root = document.getDocumentElement();
557 // Now for each element read in its value tree if present.
558 for(int i = elements.getLength() - 1; i >= 0; i--) {
559 ElementWrapper value_element = new ElementWrapper((Element)elements.item(i));
560 File value_file = new File(file.getParentFile(), value_element.toString() + ".mdv");
561 ///ystem.err.println("Searching for " + value_file.getAbsolutePath());
562 if(value_file.exists()) {
563 Document value_document = Utility.parse(value_file, false);
564 if(value_document != null) {
565 value_trees.put(value_element, new GValueModel(value_element, value_document));
566 }
567 else {
568 Gatherer.println("Error! Missing mdv file: " + value_file.getAbsolutePath());
569 }
570 }
571 }
572 }
573 else {
574 Gatherer.println("Error! Missing mds file: " + file.getAbsolutePath());
575 }
576 }
577}
Note: See TracBrowser for help on using the repository browser.