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

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

Commented out about 60 unused functions.

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