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

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

Initial revision

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