source: trunk/gli/src/org/greenstone/gatherer/msm/MSMUtils.java@ 4487

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

now passes the metadata set as well to getStructuralDetails, so we can get teh namespace and name of the set

  • Property svn:keywords set to Author Date Id Revision
File size: 35.7 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <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 */
37
38
39
40
41
42
43/* GPL_HEADER */
44package org.greenstone.gatherer.msm;
45/**************************************************************************************
46 * Title: Gatherer
47 * Description: The Gatherer: a tool for gathering and enriching a digital collection.
48 * Company: The University of Waikato
49 * Written: / /01
50 * Revised: 16/08/02 Improved
51 * @author John Thompson, Greenstone Digital Libraries
52 * @version 2.3
53 **************************************************************************************/
54import java.io.*;
55import java.util.*;
56import org.greenstone.gatherer.Gatherer;
57import org.greenstone.gatherer.cdm.CommandTokenizer;
58import org.greenstone.gatherer.mem.Attribute;
59import org.greenstone.gatherer.msm.MetadataSet;
60import org.greenstone.gatherer.util.ArrayTools;
61import org.greenstone.gatherer.util.Utility;
62import org.greenstone.gatherer.valuetree.GValueModel;
63import org.w3c.dom.*;
64/** This class contains a plethora of methods associated with handling the content of <strong>MetadataSet</strong>s and the <strong>Element</strong>s within. For example this is where you will find methods for comparing various parts of two <strong>MetadataSet</strong>s for equality. It also has methods for extracting common and useful data from <strong>Element</strong>s such as the AssignedValue nodes.
65 * @author John Thompson, Greenstone Digital Libraries
66 * @version 2.3
67 */
68public class MSMUtils {
69 /** Used to order metadata according to set standard element order then alphabetically by value. */
70 static public MetadataComparator METADATA_COMPARATOR = new MetadataComparator();
71 /** An element of the enumeration of type filter. */
72 static public final int NONE = 0;
73 /** An element of the enumeration of type filter. */
74 static public final int VALUES = 1;
75 /** An element of the enumeration of type filter. */
76 static public final int ALIASES = 2;
77 /** An element of the enumeration of type filter. */
78 static public final int BOTH = 3;
79 /** The character used to separate name space from metadata element. */
80 static public final char NS_SEP= '.';
81 /** Method to add one node as a child of another, after migrating into the target document.
82 * @param parent The <strong>Node</strong> we are inserting into.
83 * @param child The original <strong>Node</strong> we are inserting. Must first be cloned into the parents document.
84 */
85 static final public void add(Node parent, Node child) {
86 Document document = parent.getOwnerDocument();
87 Node new_child = document.importNode(child, true);
88 parent.appendChild(new_child);
89 }
90
91 static final public void addElementAttribute(Node node, String name, String language, String value) {
92 Document document = node.getOwnerDocument();
93 Element attribute_node = document.createElementNS("", "Attribute");
94 attribute_node.setAttribute("name", name);
95 attribute_node.setAttribute("language", language);
96 node.appendChild(attribute_node);
97 Node attribute_text = document.createTextNode(value);
98 attribute_node.appendChild(attribute_text);
99 }
100
101 /** A method for comparing two AssignedValues trees. This compares not only the Subject hierarchies but also the values themselves.
102 * @param avt A <strong>Node</strong> which is the root of an AssignedValues tree.
103 * @param bvt The <strong>Node</strong> which is the root of the tree we wish to compare it to.
104 * @return <i>true</i> if the two trees are equal, <i>false</i> otherwise.
105 */
106 static final public boolean assignedValuesEqual(Node avt, Node bvt) {
107 if(avt == null && bvt == null) {
108 return true; // Both are null so both are equal.
109 }
110 else if(avt == null || bvt == null) {
111 // One is null and the other isn't.
112 return false;
113 }
114 else {
115 Hashtable a_map = new Hashtable();
116 getValueMappings(avt, null, a_map);
117 Hashtable b_map = new Hashtable();
118 getValueMappings(bvt, null, b_map);
119 if(a_map.size() == b_map.size()) {
120 /** @TODO - Figure out what to do now. */
121 return true;
122 }
123 }
124 return false;
125 }
126 /** A method for comparing two attribute nodes of type Node not Attr as you might think. Attr objects are used to describe the attributes of tags themselves, while in a metadata set we intend attribute nodes to describe qualities of metadata elements. It's just confusing because the two systems (DOM model and Dublin Core) are quite similar.
127 * @param an A <strong>Node</strong> representing some attribute of an element.
128 * @param bn The <strong>Node</strong> we wish to compare it to.
129 * @return <i>true</i> if and only if the attributes are equal.
130 */
131 static final public boolean attributesEqual(Node an, Node bn) {
132 // Check we are comparing apples and apples...
133 if(an.getNodeName().equals("Attribute") && bn.getNodeName().equals("Attribute")) {
134 Element ae = (Element) an;
135 Element be = (Element) bn;
136 // Ensure we are comparing the same type of attribute.
137 if(ae.getAttribute("name").equals(be.getAttribute("name"))) {
138 // And finally retrieve and compare the values.
139 if(getValue(ae).equals(getValue(be))) {
140 // We have a match.
141 return true;
142 }
143 }
144 }
145 // And if anything goes wrong we can't be dealing with equal attributes.
146 return false;
147 }
148 /** Method to determine if a certain path of elements can be found in another tree. This method tests by comparing node names, and finally the #text node past the leaf end of the path.
149 * @param tree The root <strong>Node</strong> of the tree to be searched.
150 * @param path The <strong>Node[]</strong> path to be found.
151 * @return <i>true</i> if the path was found (including matching #text elements at the leaf), or <i>false</i> otherwise.
152 */
153 static final public boolean containsPath(Node tree, Node path[]) {
154 // If there is no tree then there are no values.
155 if(tree == null) {
156 return false;
157 }
158 // Ensure we are comparing equivent things.
159 if(tree.getNodeName().equals("AssignedValues") && path[0].getNodeName().equals("AssignedValues")) {
160 int index = 1;
161 while(index < path.length && tree != null) {
162 Node next = null;
163 for(Node n = tree.getFirstChild(); n != null && next == null; n = n.getNextSibling()) {
164 if(n.getNodeName().equals(path[index].getNodeName())) {
165 next = n;
166 index++;
167 }
168 tree = next;
169 }
170 }
171 // Tree may now be pointing at the same node as
172 // path[path.length - 1] so we should test their child text nodes.
173 if(tree != null) {
174 Node tree_text = tree.getFirstChild();
175 Node path_text = path[path.length - 1].getFirstChild();
176 if(tree_text.getNodeValue().equals(path_text.getNodeValue())) {
177 // Path found!
178 return true;
179 }
180 }
181 }
182 return false;
183 }
184 /** Method to compare two metadata elements (of type Element, which is bound to get more than a bit confusing) for equality. This test may only check the structural (ie pretty much unchanging) consistancy, or may include the AssignedValue tree as well (which will be different for each collection I'd imagine).
185 * @param a_set The <strong>MetadataSet</strong> a comes from.
186 * @param a An <strong>Element</strong>.
187 * @param b_set The <strong>MetadataSet</strong> b comes from.
188 * @param b The <strong>Element</strong> to compare it to.
189 * @param values <i>true</i> if the AssignedValues tree should also be compared, <i>false</i> otherwise.
190 * @return <i>true</i> if the elements are equal, <i>false</i> otherwise.
191 */
192 static final public boolean elementsEqual(MetadataSet a_set, Element ae, MetadataSet b_set, Element be, boolean values) {
193 // Compare Element Attr(ibutes) within the DOM, not to be confused with comparing element attributes in a Dublin Core sense...
194 NamedNodeMap aas = ae.getAttributes();
195 NamedNodeMap bas = be.getAttributes();
196 // For each attribute in a...
197 for(int i = 0; i < aas.getLength(); i++) {
198 Attr aa = (Attr)aas.item(i);
199 // Try to retrieve an attribute of the same name from b.
200 Attr ba = (Attr)bas.getNamedItem(aa.getNodeName());
201 // Now if there was no such attribute, or if the values for the
202 // two attributes are different the structures different.
203 if(ba == null || (!aa.getValue().equals(ba.getValue()))) {
204 //ystem.err.println("Attributes are not equal");
205 return false;
206 }
207 }
208 // Quickest test of children is to see we have the same number in
209 // each. Remember to modify for missing AssignedValues which have
210 // nothing to do with structure.
211 int anc = getAttributeCount(ae);
212 int bnc = getAttributeCount(be);
213 if(anc != bnc) {
214 return false;
215 }
216 // Now we compare the child nodes of the two Elements taking into
217 // account three special cases...
218 // 1. We don't test the AssignedValues element here.
219 // 2. Remember OptionList node.
220 // 3. The Attributes of each metadata element.
221 // For each child node of a.
222 for(Node an = ae.getFirstChild(); an !=null; an =an.getNextSibling()) {
223 if(an.getNodeName().equals("OptionList")) {
224 //ystem.err.println("Matching OptionLists.");
225 Node bn = getNodeFromNamed(be, "OptionList");
226 if(bn == null || !optionListsEqual(an, bn)) {
227 //ystem.err.println("OptionLists are not equal");
228 return false;
229 }
230 }
231 // Matching attributes.
232 else if(an.getNodeName().equals("Attribute")) {
233 //ystem.err.println("Matching Attributes.");
234 boolean matched = false;
235 for(Node bn = be.getFirstChild(); bn != null && !matched;
236 bn = bn.getNextSibling()) {
237 if(bn.getNodeName().equals("Attribute")) {
238 matched = attributesEqual(an, bn);
239 }
240 }
241 if(!matched) {
242 //ystem.err.println("Cannot match attribute.");
243 return false;
244 }
245 }
246 }
247 // Finally, if we've been asked to compares value trees (for some unknown reason) go ahead and compare them too.
248 if(values) {
249 GValueModel avt = a_set.getValueTree(new ElementWrapper(ae));
250 GValueModel bvt = b_set.getValueTree(new ElementWrapper(be));
251 return assignedValuesEqual(avt.getDocument().getDocumentElement(), bvt.getDocument().getDocumentElement());
252 }
253 // If we've got this far the elements match!
254 return true;
255 }
256 /** This method extracts the assigned value trees details, if any, from a certain element and stores them in an array ready to be passed as arguments to the Dictionary.
257 * @param element The <strong>Element</strong> whose values we wish to view.
258 * @return A <strong>String[]</strong> containing the details of the assigned values tree.
259 * @see org.greenstone.gatherer.Dictionary
260 */
261 static final public String[] getAssignedValuesDetails(MetadataSet mds, Element element) {
262 String details[] = null;
263 //Node avt = getNodeFromNamed(element, "AssignedValues");
264 GValueModel avt = mds.getValueTree(new ElementWrapper(element));
265 if(avt != null) {
266 Hashtable mapping = new Hashtable();
267 getValueMappings(avt.getDocument().getDocumentElement(), null, mapping);
268 ArrayList values = new ArrayList(mapping.keySet());
269 Collections.sort(values);
270 details = new String[1];
271 for(int i = 0; i < values.size(); i++) {
272 if(details[0] == null) {
273 details[0] = " " + values.get(i);
274 }
275 else {
276 details[0] = details[0] + "\n " + values.get(i);
277 }
278 }
279 mapping = null;
280 values = null;
281 }
282 avt = null;
283 return details;
284 }
285
286 static final public TreeSet getAttributes(Element element) {
287 TreeSet attributes = new TreeSet();
288 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
289 if(n.getNodeName().equals("Attribute")) {
290 Element e = (Element)n;
291 attributes.add(new Attribute(e.getAttribute("name"), e.getAttribute("language"), getValue(e)));
292 }
293 }
294 return attributes;
295 }
296
297 /** Method to count the number of Attribute nodes under a certain Element. This ignores other nodes such as #text, OptionList and AssignedValues nodes.
298 * @param element The <strong>Element</strong> whose attributes you want to count.
299 * @return An <i>int</i> which is the number of attribute nodes.
300 */
301 static final public int getAttributeCount(Node element) {
302 int count = 0;
303 for(Node n = element.getFirstChild(); n != null;
304 n = n.getNextSibling()) {
305 if(n.getNodeName().equals("Attribute")) {
306 count++;
307 }
308 }
309 return count;
310 }
311
312 /*************************************************************************/
313
314 /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element. Note that this method is language specific.
315 * @param element The target element <strong>Node</strong>.
316 * @param name The name of the attribute you wish to return.
317 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
318 */
319 static final public Element getAttributeNodeNamed(Node element, String name) {
320 Element attribute = null;
321 String language_code = Locale.getDefault().getLanguage();
322 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
323 if(n.getNodeName().equals("Attribute")) {
324 Element e = (Element)n;
325 if(e.getAttribute("name").equals(name)) {
326 if(e.getAttribute("language").equalsIgnoreCase(language_code)) {
327 return e;
328 }
329 else if(e.getAttribute("language").equalsIgnoreCase("en")) {
330 attribute = e;
331 }
332 else if(attribute == null) {
333 attribute = e;
334 }
335 }
336 e = null;
337 }
338 }
339 return attribute;
340 }
341
342 /** This method is a slight variation on getNodeNamed in that it is especially written to retrieve the attribute Nodes of a certain name present under the given element.
343 * @param element The target element <strong>Node</strong>.
344 * @param name The name of the attribute you wish to return.
345 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
346 */
347 static final public Element[] getAttributeNodesNamed(Node element, String name) {
348 Element attributes[] = null;
349 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
350 if(n.getNodeName().equals("Attribute")) {
351 Element e = (Element)n;
352 if(e.getAttribute("name").equals(name)) {
353 if(attributes == null) {
354 attributes = new Element[1];
355 attributes[0] = e;
356 }
357 else {
358 Element temp[] = attributes;
359 attributes = new Element[temp.length + 1];
360 System.arraycopy(temp, 0, attributes, 0, temp.length);
361 attributes[temp.length] = e;
362 temp = null;
363 }
364 }
365 e = null;
366 }
367 }
368 return attributes;
369 }
370
371 /** Method to construct an elements description by retrieving the correct attribute.
372 * @param element The <strong>Element</strong> whose name we wish to retrieve.
373 * @return A <strong>String</strong> which is the elements description, or an empty string if no description exists.
374 */
375 static final public String getDescription(Node element) {
376 String definition = "";
377 Element definition_node = getAttributeNodeNamed(element, "definition");
378 if(definition_node != null) {
379 definition = getValue(definition_node);
380 }
381 String comment = "";
382 Element comment_node = getAttributeNodeNamed(element, "comment");
383 if(comment_node != null) {
384 comment = getValue(comment_node);
385 }
386 String description = definition + comment;
387 return Utility.stripNL(description.trim());
388 }
389
390 /** Extracts the file name pattern from within a fileset of a Greenstone Directory Metadata model.
391 * @param fileset The fileset Node in question.
392 * @return The pattern as a String.
393 */
394 static final public String getFileNamePattern(Node fileset) {
395 // Locate the child node called filename
396 for(Node child = fileset.getFirstChild(); child != null; child = child.getNextSibling()) {
397 if(child.getNodeName().equalsIgnoreCase("FileName")) {
398 // Find the file string.
399 return MSMUtils.getValue(child);
400 }
401 }
402 return null;
403 }
404
405 /*************************************************************************/
406 /** Method to create the fully namespace quantified identifier for this element.
407 * @param element The <strong>Node</strong> in question.
408 * @return A fully qualified identifier as a <strong>String</strong>
409 */
410 static final public String getFullIdentifier(Node element) {
411 if(element == null) {
412 return "Error";
413 }
414 // Get namespace for given element.
415 Element root = (Element)element.getParentNode();
416 if(root != null) {
417 String identifier = root.getAttribute("namespace");
418 // Get name and return it.
419 if(identifier.equals("")) {
420 identifier = Utility.EXTRACTED_METADATA_NAMESPACE;
421 }
422 return identifier + NS_SEP + getIdentifier(element);
423 }
424 return getIdentifier(element); // No namespace anymore
425 } // static public String getFullIdentier(Node element)
426
427 /*************************************************************************/
428 /** Method to construct an elements fully qualified name. Note that this is different from a nodes identifier. Think of name as a short, unique reference to an metadata element, whereas identifier can be much longer, language specific and non-unique.
429 * @param element An <strong>Element</strong> whose name we are interested in.
430 * @return A <strong>String</strong> representing this given elements fully namespace qualified name.
431 */
432 static final public String getFullName(Element element) {
433 if(element == null) {
434 return null;
435 }
436 // Get namespace for given element.
437 Element root = (Element)element.getParentNode();
438 String identifier = root.getAttribute("namespace") + NS_SEP + element.getAttribute("name");
439 // Get name and return it.
440 if(identifier.startsWith(".")) {
441 identifier = identifier.substring(1);
442 }
443 return identifier;
444 } // static public String getFullName(Element element)
445
446 /** Method to construct an elements identifier by retrieving the correct attribute. Language specific, based on default Locale.
447 * @param element The <strong>Element</strong> whose name we wish to retrieve.
448 * @return A <strong>String</strong> which is the elements identifier, or an empty string if no identifier exists.
449 */
450 static final public String getIdentifier(Node element) {
451 String identifier = null;
452 // Determine locale code.
453 String language_code = Locale.getDefault().getLanguage();
454 // Get the 'identifier' Element with the correct locale
455 for(Node node = element.getFirstChild(); node != null;
456 node = node.getNextSibling()) {
457 if(node.getNodeName().equals("Attribute")) {
458 Element target = (Element)node;
459 if(target.getAttribute("name").equals("identifier")) {
460 Node text = target.getFirstChild();
461 if(target.getAttribute("language").equalsIgnoreCase(language_code)) {
462 return text.getNodeValue();
463 }
464 else if(target.getAttribute("language").equalsIgnoreCase("en")) {
465 identifier = text.getNodeValue();
466 }
467 else if(identifier == null) {
468 identifier = text.getNodeValue();
469 }
470 text = null;
471 }
472 target = null;
473 }
474 }
475 language_code = null;
476 // We may have harvested some identifier from the file.
477 if(identifier != null) {
478 return identifier;
479 }
480 // Failing the above we return the nodes name instead.
481 return ((Element)element).getAttribute("name");
482 }
483
484 /** Retrieve the metadata description element from this fileset node.
485 * @param fileset The fileset in question.
486 * @return The description node or null if no such node.
487 */
488 static final public Node getMetadataDescription(Node fileset) {
489 // Locate the child node called filename
490 for(Node child = fileset.getLastChild(); child != null; child = child.getPreviousSibling()) {
491 if(child.getNodeName().equalsIgnoreCase("Description")) {
492 return child;
493 }
494 }
495 return null;
496 }
497
498 /** Method to retrieve from the node given, a certain child node with the specified name.
499 * @param parent The <strong>Node</strong> whose children should be searched.
500 * @param name The required nodes name as a <strong>String</strong>.
501 * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise.
502 */
503 static final public Node getNodeFromNamed(Node parent, String name) {
504 Node child = null;
505 for(Node i = parent.getFirstChild(); i != null && child == null;
506 i = i.getNextSibling()) {
507 if(i.getNodeName().equals(name)) {
508 child = i;
509 }
510 }
511 return child;
512 }
513 /** Look for the occurances 'field' of the element and return it if found.
514 * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute.
515 */
516 static final public int getOccurances(Element element) {
517 int count = 0;
518 String number = null;
519 if((number = element.getAttribute("occurances")) != null) {
520 try {
521 count = Integer.parseInt(number);
522 }
523 catch(Exception error) {
524 count = 0;
525 }
526 }
527 return count;
528 }
529 /** This method extracts the option list details, if any, from a certain element and stores them in an array ready to be passed as arguments to the <strong>Dictionary</strong>.
530 * @param element The <strong>Element</strong> whose option list we wish to view.
531 * @return A <strong>String[]</strong> containing the details of the option list.
532 * TODO implement.
533 * @see org.greenstone.gatherer.Dictionary
534 */
535 static final public String[] getOptionListDetails(Element element) {
536 return null;
537 }
538
539 /** This method traces the path between the 'root' node given and the target node. This path is returned starting at the root.
540 * @param root The root <strong>Node</strong> to start this path at.
541 * @param target The final <strong>Node</strong> to end up at.
542 * @return A <strong>Node[]</strong> that contains the sequence of nodes from root to target.
543 */
544 static final public Node[] getPath(Node root, Node target) {
545 if(root == target) {
546 return ArrayTools.add(null, root);
547 }
548 else {
549 return ArrayTools.add(getPath(root, target.getParentNode()), target);
550 }
551 }
552
553 /** This method extracts the structural details from a certain element and stores them in an array, all ready for passing to the <strong>Dictionary</strong>.
554 * @param element The <Strong>Element</strong> whose details we wish to gather.
555 * @return A <strong>String[]</strong> containing the structural details.
556 */
557 static final public String[] getStructuralDetails(MetadataSet mds, Element element) {
558 String details[] = new String[4];
559 Element root = (Element)element.getParentNode();
560 //details[0] = root.getAttribute("name");
561 //details[1] = root.getAttribute("namespace");
562 details[0] = mds.getName();
563 details[1] = mds.getNamespace();
564 details[2] = getFullName(element);
565 details[3] = null;
566 // Get attributes
567 Vector attributes = new Vector();
568 for(Node n=element.getFirstChild(); n!=null; n=n.getNextSibling()) {
569 if(n.getNodeName().equals("Attribute")) {
570 Element temp = (Element)n;
571 attributes.add(temp.getAttribute("name") + "=" + getValue(n));
572 }
573 }
574 // Sort attributes
575 Collections.sort(attributes);
576 // Add attributes to details.
577 for(int i = 0; i < attributes.size(); i++) {
578 if(details[3] == null) {
579 details[3] = " " + attributes.get(i);
580 }
581 else {
582 details[3] = details[3] + "\n " + attributes.get(i);
583 }
584 }
585 return details;
586 }
587
588 /** Method to retrieve the value of a given node (not the assigned values tree!).
589 * @param element The <strong>Element</strong> whose value we wish to find.
590 * @return The value found as a <strong>String</strong>, or <i>null</i> if this element has no value.
591 */
592 static final public String getValue(Node element) {
593 // If we've been given a subject node first retrieve its value node.
594 if(element.getNodeName().equals("Subject")) {
595 element = getNodeFromNamed(element, "Value");
596 }
597 if(element != null && element.hasChildNodes()) {
598 Node text = element.getFirstChild();
599 return text.getNodeValue();
600 }
601 return "";
602 }
603
604 /** Method to traverse the given value tree, and build up a hashtable of mappings between the value path key names and the Subject nodes of the tree.
605 * @param current The root <strong>Node</strong> of a subtree of the AssignedValues tree.
606 * @param prefix The value path key <strong>String</string>, which shows the path from the root of the AssignedValue tree to <i>current</i>s parent using '\' as a separator.
607 * @param values A <strong>Hashtable</strong> containing the mapping discovered so far in our tree traversal.
608 */
609 static final public void getValueMappings(Node current, String prefix, Hashtable values) {
610 if(current != null) {
611 String name = current.getNodeName();
612 String new_prefix = prefix;
613 // If we've found the outer layer of a new value, add it to our
614 // mapping.
615 if(name.equals("Subject")) {
616 Node value_node = getNodeFromNamed(current, "Value");
617 String value = getValue(value_node);
618 if(new_prefix != null) {
619 new_prefix = new_prefix + "\\" + value;
620 }
621 else {
622 new_prefix = value;
623 }
624 values.put(new_prefix, current);
625 }
626 if(name.equals("Subject") || name.equals("AssignedValues")) {
627 for(Node child = current.getFirstChild(); child != null;
628 child = child.getNextSibling()) {
629 getValueMappings(child, new_prefix, values);
630 }
631 }
632 }
633 }
634 /** Parses the value tree template file.
635 * @return The Document parsed.
636 */
637 static final public Document getValueTreeTemplate() {
638 return Utility.parse(Utility.METADATA_VALUE_TEMPLATE, true);
639 }
640 /** Method to compare two OptionsLists for equality.
641 * @param al A <strong>Node</strong> which represents an OptionList.
642 * @param bl The <strong>Node</strong> we wish to test against.
643 * @return A <i>boolean</i> which is <i>true</i> if the two option lists are equal, <i>false</i> otherwise.
644 * TODO Implementation
645 */
646 static final public boolean optionListsEqual(Node al, Node bl) {
647 // Compare the 'restricted' attribute of the two lists.
648 Element ae = (Element) al;
649 Element be = (Element) bl;
650 if(!ae.getAttribute("restricted").equals
651 (be.getAttribute("restricted"))) {
652 return false;
653 }
654 // Compare the Values under each list.
655 for(Node an = al.getFirstChild(); an != null;
656 an = an.getNextSibling()){
657 if(an.getNodeName().equals("Value")) {
658 boolean matched = false;
659 for(Node bn = bl.getFirstChild(); bn != null && !matched;
660 bn = bn.getNextSibling()) {
661 if(bn.getNodeName().equals("Value")) {
662 matched = valuesEqual(an, bn);
663 }
664 }
665 if(!matched) {
666 return false;
667 }
668 }
669 }
670 return true;
671 }
672
673 static final public void setIdentifier(Node element, String value) {
674 // Get the 'identifier' Element
675 for(Node node = element.getFirstChild(); node != null;
676 node = node.getNextSibling()) {
677 if(node.getNodeName().equals("Attribute")) {
678 Element target = (Element)node;
679 if(target.getAttribute("name").equals("identifier")) {
680 Node text = target.getFirstChild();
681 text.setNodeValue(value);
682 }
683 }
684 }
685 }
686 /** Set the value of the element attribute occurances.
687 * @param element The <strong>Element</strong> to change.
688 * @param value The value to change by as an <i>int</i>.
689 */
690 static final public void setOccurance(Element element, int value) {
691 Integer new_value = new Integer(getOccurances(element) + value);
692 element.setAttribute("occurances", new_value.toString());
693 }
694 /** This method also traverses the tree, but this one is used to gather all the values and aliases at once, and to 'prune' the tree if necessary.
695 * @param current The current root <strong>Node</strong> of this AssignedValues tree subtree.
696 * @param return_filter This <i>int</i> specifies what nodes from the tree should be returned, where;<br>VALUES = return values only<br>ALIASES = return aliases only<br>BOTH = return both values and aliases<br>NONE = return nothing.
697 * @param remove_leaves A leaf node is a subject that contains no child subjects. If this <i>boolean</i> is set to <i>true</i> then the leaf nodes will be removed from the tree.
698 * @return A <strong>Node[]</strong> containing whatever values or aliases have been found during the trees traversal.
699 */
700 static final public Node[] traverseTree(Node current, int return_filter, boolean remove_leaves) {
701 Node leaves[] = null;
702 String name = current.getNodeName();
703 if(name.equals("Value") && (return_filter == VALUES || return_filter == BOTH)) {
704 leaves = ArrayTools.add(leaves, current);
705 }
706 else if(name.equals("Alias") && (return_filter == ALIASES || return_filter == BOTH)) {
707 leaves = ArrayTools.add(leaves, current);
708 }
709 else if(name.equals("Subject")) {
710 boolean has_subject_child = false;
711 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
712 for(int i = 0; i < children.length; i++) {
713 if(children[i].getNodeName().equals("Subject")) {
714 has_subject_child = true;
715 }
716 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
717 }
718 if(!has_subject_child && remove_leaves) {
719 Node parent = current.getParentNode();
720 parent.removeChild(current);
721 }
722 }
723 else if(name.equals("AssignedValues")) {
724 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
725 for(int i = 0; i < children.length; i++) {
726 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
727 }
728 }
729 return leaves;
730 }
731
732 /** This method is used to systematically merge two AssignedValues tree. Both trees have their current values mapped, then the new tree is searched for key paths that don't exist in the current tree. If such a key is found, the Subject <strong>Node</strong> it maps to is retrieved and then imported and added to whatever was the closest available node (in terms of tree path) in the current tree.
733 * @param a_set The MetadataSet from which the Element a came from.
734 * @param a The Element at the root of the current AssignedValues tree.
735 * @param b_set The MetadataSet from which the Element b came from.
736 * @param b The root Element of the tree that is being merged.
737 * @return A <i>boolean</i> which is <i>true</i> if the trees merged without error, <i>false</i> otherwise.
738 */
739 static final public boolean updateValueTree(MetadataSet a_set, Element a, MetadataSet b_set, Element b) {
740 GValueModel avt = a_set.getValueTree(new ElementWrapper(a));
741 GValueModel bvt = b_set.getValueTree(new ElementWrapper(b));
742 // If neither element even has a value tree, we're all done.
743 if(avt == null && bvt == null) {
744 avt = null;
745 bvt = null;
746 return true;
747 }
748 // If the new element has no value tree then nothing needs to be done.
749 else if(avt != null && bvt == null) {
750 avt = null;
751 bvt = null;
752 return true;
753 }
754 // If only the new element has a value tree, then add all of its values
755 // immediately.
756 else if(avt == null && bvt != null) {
757 a_set.addValueTree(new ElementWrapper(a), bvt);
758 avt = null;
759 bvt = null;
760 return true;
761 }
762 // We have both trees for both elements, time to merge.
763 else {
764 Document document = avt.getDocument();
765 Hashtable a_map = new Hashtable();
766 getValueMappings(document.getDocumentElement(), null, a_map);
767 Hashtable b_map = new Hashtable();
768 getValueMappings(bvt.getDocument().getDocumentElement(), null, b_map);
769 // For each new entry in b_map
770 for(Enumeration b_keys = b_map.keys(); b_keys.hasMoreElements(); ) {
771 String b_key = (String)b_keys.nextElement();
772 // Test if there is already an entry in a_map.
773 if(!a_map.containsKey(b_key)) {
774 // If not, search through a_map for the longest match.
775 Node target = document.getDocumentElement();
776 String last_match = null;
777 for(Enumeration a_keys = a_map.keys();
778 a_keys.hasMoreElements(); ) {
779 String a_key = (String)a_keys.nextElement();
780 if(b_key.startsWith(a_key)) {
781 if(last_match == null || a_key.length() > last_match.length()) {
782 last_match = a_key;
783 target = (Node)a_map.get(a_key);
784 }
785 }
786 a_key = null;
787 }
788 // Now import the node at b_key and add it to target.
789 Node subtree = (Node)b_map.get(b_key);
790 subtree = document.importNode(subtree, true);
791 // Find the node to insert before...
792 String name = getValue(subtree);
793 Node move = null;
794 for(Node n = target.getFirstChild(); n != null && move == null; n = n.getNextSibling()) {
795 if(n.getNodeName().equals("Subject")) {
796 if(name.compareTo(getValue(n)) <= 0) {
797 move = n;
798 }
799 }
800 }
801 if(move == null) {
802 target.appendChild(subtree);
803 }
804 else {
805 target.insertBefore(subtree, move);
806 }
807 target = null;
808 last_match = null;
809 subtree = null;
810 name = null;
811 move = null;
812 }
813 b_key = null;
814 }
815 document = null;
816 a_map = null;
817 b_map = null;
818 avt = null;
819 bvt = null;
820 return true;
821 }
822 }
823
824 /** Method to determine if two Value nodes are equal.
825 * @param av A <strong>Node</strong> representing a value.
826 * @param bv The <strong>Node</strong> we want to compare it to.
827 * @return A <i>boolean</i> which is <i>true</i> if the two value nodes are equal, <i>false</i> otherwise.
828 */
829 static final public boolean valuesEqual(Node av, Node bv) {
830 // Check we are comparing apples and apples...
831 if(av.getNodeName().equals("Value") &&
832 bv.getNodeName().equals("Value")) {
833 // Retrieve and then compare their text values.
834 Node at = av.getFirstChild();
835 Node bt = bv.getFirstChild();
836 if(at.getNodeValue().equals(bt.getNodeValue())) {
837 return true;
838 }
839 }
840 return false;
841 }
842
843 /** A comparator for sorting metadata element-value pairs into their standard order (elements) then alphabetical order (values). */
844 static final private class MetadataComparator
845 implements Comparator {
846 /** Compares its two arguments for order. */
847 public int compare(Object o1, Object o2) {
848 int result = 0;
849 Metadata m1 = (Metadata) o1;
850 Metadata m2 = (Metadata) o2;
851 ///ystem.err.println("MSMUtils.compare(" + m1 + ", " + m2 + ") = ");
852 ElementWrapper e1 = m1.getElement();
853 ElementWrapper e2 = m2.getElement();
854 // First we compare the namespaces
855 result = e1.getNamespace().compareTo(e2.getNamespace());
856 if(result == 0 && Gatherer.c_man != null && Gatherer.c_man.ready() && e1.getNamespace() != null) {
857 // Now, given both elements are in the same set, we compare the element ordering using methods in MetadataSet
858 MetadataSet set = Gatherer.c_man.getCollection().msm.getSet(e1.getNamespace());
859 ///ystem.err.print("MetadataSet.compare(" + e1 + ", " + e2 + ") = ");
860 result = set.compare(e1.getElement(), e2.getElement());
861 ///ystem.err.println(result);
862 if(result == 0) {
863 // Finally we compare the values alphabetically.
864 result = m1.getValue().toLowerCase().compareTo(m2.getValue().toLowerCase());
865 }
866 }
867 ///ystem.err.println("Result: " + result);
868 return result;
869 }
870
871 /** Indicates whether some other object is "equal to" this Comparator. */
872 public boolean equals(Object obj) {
873 return compare(this, obj) == 0;
874 }
875 }
876}
Note: See TracBrowser for help on using the repository browser.