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

Last change on this file since 6568 was 6568, checked in by jmt12, 20 years ago

Fixed bug where legacy metadata sets were not locating the english string if one in the requested language was not found

  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 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 int NONE = 0;
59 /** An element of the enumeration of type filter. */
60 static public int VALUES = 1;
61 /** An element of the enumeration of type filter. */
62 static public int ALIASES = 2;
63 /** An element of the enumeration of type filter. */
64 static public int BOTH = 3;
65 /** The character used to separate name space from metadata element. */
66 static public char NS_SEP= '.';
67 /** The character used to separate subfields from metadata element. */
68 static public 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 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 /** Method to add an attribute element to the given element. This method makes use of the language_dependant attribute of the document to not only determine if the attribute is language dependant, but also to see whether a Language element should be created if doesn't already exist.
80 * @param element_element the Element to add the attribute element to
81 * @param attribute_name_str the name of the new attribute to add as a String
82 * @param language_code_str the two letter code String of the language this attribute is to be added as
83 * @param value_str the String to be assigned as the attribute elements value
84 * @see org.greenstone.gatherer.msm.MSMUtils#isAttributeLanguageDependant
85 * @see org.greenstone.gatherer.msm.MSMUtils#setValue(Element, String)
86 * @see org.greenstone.gatherer.util.StaticStrings#ATTRIBUTE_ELEMENT
87 * @see org.greenstone.gatherer.util.StaticStrings#CODE_ATTRIBUTE
88 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ATTRIBUTE
89 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ELEMENT
90 * @see org.greenstone.gatherer.util.StaticStrings#NAME_ATTRIBUTE
91 */
92 static public void addElementAttribute(Element element_element, String attribute_name_str, String language_code_str, String value_str) {
93 Document document = element_element.getOwnerDocument();
94 // Create the basic new attribute (everything except language attribute)
95 Element attribute_element = document.createElement(StaticStrings.ATTRIBUTE_ELEMENT);
96 attribute_element.setAttribute(StaticStrings.NAME_ATTRIBUTE, attribute_name_str);
97 MSMUtils.setValue(attribute_element, value_str);
98 // Start off by determining if we have to add this node in the new multilingual optimized way
99 if(isAttributeLanguageDependant(document, attribute_name_str)) {
100 boolean found = false;
101 // Try to retrieve a language element for the given language code
102 NodeList language_elements = element_element.getElementsByTagName(StaticStrings.LANGUAGE_ELEMENT);
103 for(int i = 0; i < language_elements.getLength(); i++) {
104 Element language_element = (Element) language_elements.item(i);
105 if(language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE).equals(language_code_str)) {
106 found = true;
107 // Add attribute
108 language_element.appendChild(attribute_element);
109 }
110 language_element = null;
111 }
112 language_elements = null;
113 // If it still hasn't been found, then add it
114 if(!found) {
115 Element language_element = document.createElement(StaticStrings.LANGUAGE_ELEMENT);
116 language_element.setAttribute(StaticStrings.CODE_ATTRIBUTE, language_code_str);
117 element_element.appendChild(language_element);
118 // Add attribute
119 language_element.appendChild(attribute_element);
120 language_element = null;
121 }
122 }
123 // Just add the attribute the old fashioned way
124 else {
125 attribute_element.setAttribute(StaticStrings.LANGUAGE_ATTRIBUTE, language_code_str);
126 element_element.appendChild(attribute_element);
127 }
128 // Clean up
129 attribute_element = null;
130 document = null;
131 }
132
133 /** A method for comparing two AssignedValues trees. This compares not only the Subject hierarchies but also the values themselves.
134 * @param avt A <strong>Node</strong> which is the root of an AssignedValues tree.
135 * @param bvt The <strong>Node</strong> which is the root of the tree we wish to compare it to.
136 * @return <i>true</i> if the two trees are equal, <i>false</i> otherwise.
137 */
138 static final public boolean assignedValuesEqual(Node avt, Node bvt) {
139 if(avt == null && bvt == null) {
140 return true; // Both are null so both are equal.
141 }
142 else if(avt == null || bvt == null) {
143 // One is null and the other isn't.
144 return false;
145 }
146 else {
147 Hashtable a_map = new Hashtable();
148 getValueMappings(avt, null, a_map);
149 Hashtable b_map = new Hashtable();
150 getValueMappings(bvt, null, b_map);
151 if(a_map.size() == b_map.size()) {
152 /** @TODO - Figure out what to do now. */
153 return true;
154 }
155 }
156 return false;
157 }
158 /** 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.
159 * @param an A <strong>Node</strong> representing some attribute of an element.
160 * @param bn The <strong>Node</strong> we wish to compare it to.
161 * @return <i>true</i> if and only if the attributes are equal.
162 */
163 static final public boolean attributesEqual(Node an, Node bn) {
164 // Check we are comparing apples and apples...
165 if(an.getNodeName().equals("Attribute") && bn.getNodeName().equals("Attribute")) {
166 Element ae = (Element) an;
167 Element be = (Element) bn;
168 // Ensure we are comparing the same type of attribute.
169 if(ae.getAttribute("name").equals(be.getAttribute("name"))) {
170 // And finally retrieve and compare the values.
171 if(getValue(ae).equals(getValue(be))) {
172 // We have a match.
173 return true;
174 }
175 }
176 }
177 // And if anything goes wrong we can't be dealing with equal attributes.
178 return false;
179 }
180
181 /** Remove all of the child nodes from a certain node. */
182 static final public void clear(Node parent) {
183 while(parent.hasChildNodes()) {
184 parent.removeChild(parent.getFirstChild());
185 }
186 }
187
188 /** 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.
189 * @param tree The root <strong>Node</strong> of the tree to be searched.
190 * @param path The <strong>Node[]</strong> path to be found.
191 * @return <i>true</i> if the path was found (including matching #text elements at the leaf), or <i>false</i> otherwise.
192 */
193 /* static final private boolean containsPath(Node tree, Node path[]) {
194 // If there is no tree then there are no values.
195 if(tree == null) {
196 return false;
197 }
198 // Ensure we are comparing equivent things.
199 if(tree.getNodeName().equals("AssignedValues") && path[0].getNodeName().equals("AssignedValues")) {
200 int index = 1;
201 while(index < path.length && tree != null) {
202 Node next = null;
203 for(Node n = tree.getFirstChild(); n != null && next == null; n = n.getNextSibling()) {
204 if(n.getNodeName().equals(path[index].getNodeName())) {
205 next = n;
206 index++;
207 }
208 tree = next;
209 }
210 }
211 // Tree may now be pointing at the same node as
212 // path[path.length - 1] so we should test their child text nodes.
213 if(tree != null) {
214 Node tree_text = tree.getFirstChild();
215 Node path_text = path[path.length - 1].getFirstChild();
216 if(tree_text.getNodeValue().equals(path_text.getNodeValue())) {
217 // Path found!
218 return true;
219 }
220 }
221 }
222 return false;
223 } */
224 /** 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).
225 * @param a_set The <strong>MetadataSet</strong> a comes from.
226 * @param a An <strong>Element</strong>.
227 * @param b_set The <strong>MetadataSet</strong> b comes from.
228 * @param b The <strong>Element</strong> to compare it to.
229 * @param values <i>true</i> if the AssignedValues tree should also be compared, <i>false</i> otherwise.
230 * @return <i>true</i> if the elements are equal, <i>false</i> otherwise.
231 */
232 static final public boolean elementsEqual(MetadataSet a_set, Element ae, MetadataSet b_set, Element be, boolean values) {
233 // Compare Element Attr(ibutes) within the DOM, not to be confused with comparing element attributes in a Dublin Core sense...
234 NamedNodeMap aas = ae.getAttributes();
235 NamedNodeMap bas = be.getAttributes();
236 // For each attribute in a...
237 for(int i = 0; i < aas.getLength(); i++) {
238 Attr aa = (Attr)aas.item(i);
239 // Try to retrieve an attribute of the same name from b.
240 Attr ba = (Attr)bas.getNamedItem(aa.getNodeName());
241 // Now if there was no such attribute, or if the values for the
242 // two attributes are different the structures different.
243 if(ba == null || (!aa.getValue().equals(ba.getValue()))) {
244 //ystem.err.println("Attributes are not equal");
245 return false;
246 }
247 }
248 // Quickest test of children is to see we have the same number in
249 // each. Remember to modify for missing AssignedValues which have
250 // nothing to do with structure.
251 int anc = getAttributeCount(ae);
252 int bnc = getAttributeCount(be);
253 if(anc != bnc) {
254 return false;
255 }
256 // Now we compare the child nodes of the two Elements taking into
257 // account three special cases...
258 // 1. We don't test the AssignedValues element here.
259 // 2. Remember OptionList node.
260 // 3. The Attributes of each metadata element.
261 // For each child node of a.
262 for(Node an = ae.getFirstChild(); an !=null; an =an.getNextSibling()) {
263 if(an.getNodeName().equals("OptionList")) {
264 //ystem.err.println("Matching OptionLists.");
265 Node bn = getNodeFromNamed(be, "OptionList");
266 if(bn == null || !optionListsEqual(an, bn)) {
267 //ystem.err.println("OptionLists are not equal");
268 return false;
269 }
270 }
271 // Matching attributes.
272 else if(an.getNodeName().equals("Attribute")) {
273 //ystem.err.println("Matching Attributes.");
274 boolean matched = false;
275 for(Node bn = be.getFirstChild(); bn != null && !matched;
276 bn = bn.getNextSibling()) {
277 if(bn.getNodeName().equals("Attribute")) {
278 matched = attributesEqual(an, bn);
279 }
280 }
281 if(!matched) {
282 //ystem.err.println("Cannot match attribute.");
283 return false;
284 }
285 }
286 }
287 // Finally, if we've been asked to compares value trees (for some unknown reason) go ahead and compare them too.
288 if(values) {
289 GValueModel avt = a_set.getValueTree(new ElementWrapper(ae));
290 GValueModel bvt = b_set.getValueTree(new ElementWrapper(be));
291 return assignedValuesEqual(avt.getDocument().getDocumentElement(), bvt.getDocument().getDocumentElement());
292 }
293 // If we've got this far the elements match!
294 return true;
295 }
296 /** 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.
297 * @param element The <strong>Element</strong> whose values we wish to view.
298 * @return A <strong>String[]</strong> containing the details of the assigned values tree.
299 * @see org.greenstone.gatherer.Dictionary
300 */
301 static final public String[] getAssignedValuesDetails(MetadataSet mds, Element element) {
302 String details[] = null;
303 //Node avt = getNodeFromNamed(element, "AssignedValues");
304 GValueModel avt = mds.getValueTree(new ElementWrapper(element));
305 if(avt != null) {
306 Hashtable mapping = new Hashtable();
307 getValueMappings(avt.getDocument().getDocumentElement(), null, mapping);
308 ArrayList values = new ArrayList(mapping.keySet());
309 Collections.sort(values);
310 details = new String[1];
311 for(int i = 0; i < values.size(); i++) {
312 if(details[0] == null) {
313 details[0] = " " + values.get(i);
314 }
315 else {
316 details[0] = details[0] + "\n " + values.get(i);
317 }
318 }
319 mapping = null;
320 values = null;
321 }
322 avt = null;
323 return details;
324 }
325
326 /** Retrieve all of the attributes for the given element as a tree set. Note that this requires significant manipulation if the source is a multilingual optimized metadata set.
327 * @param element the Element whose attributes we wish to catalog
328 * @return a TreeSet of the attributes sorted by their natural ordering
329 * @see org.greenstone.gatherer.msm.MSMUtils#getValue(Element)
330 * @see org.greenstone.gatherer.util.StaticStrings#ATTRIBUTE_ELEMENT
331 * @see org.greenstone.gatherer.util.StaticStrings#CODE_ATTRIBUTE
332 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ATTRIBUTE
333 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ELEMENT
334 * @see org.greenstone.gatherer.util.StaticStrings#NAME_ATTRIBUTE
335 */
336 static public TreeSet getAttributes(Element element) {
337 TreeSet attribute_tree = new TreeSet();
338 for(Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
339 if(node instanceof Element) {
340 Element some_element = (Element) node;
341 String some_element_name = some_element.getNodeName();
342 if(some_element_name.equals(StaticStrings.ATTRIBUTE_ELEMENT)) {
343 attribute_tree.add(new Attribute(some_element.getAttribute(StaticStrings.NAME_ATTRIBUTE), some_element.getAttribute(StaticStrings.LANGUAGE_ATTRIBUTE), MSMUtils.getValue(some_element)));
344 }
345 else if(some_element_name.equals(StaticStrings.LANGUAGE_ELEMENT)) {
346 String language_code = some_element.getAttribute(StaticStrings.CODE_ATTRIBUTE);
347 NodeList attribute_elements = some_element.getElementsByTagName(StaticStrings.ATTRIBUTE_ELEMENT);
348 for(int i = 0; i < attribute_elements.getLength(); i++) {
349 Element attribute_element = (Element) attribute_elements.item(i);
350 attribute_tree.add(new Attribute(attribute_element.getAttribute(StaticStrings.NAME_ATTRIBUTE), language_code, MSMUtils.getValue(element)));
351 attribute_element = null;
352 }
353 attribute_elements = null;
354 language_code = null;
355 }
356 some_element_name = null;
357 some_element = null;
358 }
359 }
360 return attribute_tree;
361 }
362
363 /** Method to count the number of Attribute nodes under a certain Element. This ignores other nodes such as #text, OptionList and AssignedValues nodes.
364 * @param element The <strong>Element</strong> whose attributes you want to count.
365 * @return An <i>int</i> which is the number of attribute nodes.
366 */
367 static final public int getAttributeCount(Node element) {
368 int count = 0;
369 for(Node n = element.getFirstChild(); n != null;
370 n = n.getNextSibling()) {
371 if(n.getNodeName().equals("Attribute")) {
372 count++;
373 }
374 }
375 return count;
376 }
377
378 /*************************************************************************/
379 /** 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.
380 * @param element The target element <strong>Node</strong>.
381 * @param name The name of the attribute you wish to return.
382 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
383 */
384 static final public Element getAttributeNodeNamed(Node element, String name) {
385 Element attribute = null;
386 String language_code = Gatherer.config.getLanguage();
387 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
388 if(n.getNodeName().equals("Attribute")) {
389 Element e = (Element)n;
390 if(e.getAttribute("name").equals(name)) {
391 if(e.getAttribute("language").equalsIgnoreCase(language_code)) {
392 return e;
393 }
394 else if(e.getAttribute("language").equalsIgnoreCase("en")) {
395 attribute = e;
396 }
397 else if(attribute == null) {
398 attribute = e;
399 }
400 }
401 e = null;
402 }
403 }
404 return attribute;
405 }
406
407 /** 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.
408 * @param element The target element <strong>Node</strong>.
409 * @param name The name of the attribute you wish to return.
410 * @return An <strong>Element[]</strong> containing the attributes you requested, or <i>null</i> if no such attributes exists.
411 */
412 static final public Element[] getAttributeNodesNamed(Node element, String name) {
413 Element attributes[] = null;
414 for(Node n = element.getFirstChild(); n != null; n = n.getNextSibling()) {
415 if(n.getNodeName().equals("Attribute")) {
416 Element e = (Element)n;
417 if(e.getAttribute("name").equals(name)) {
418 if(attributes == null) {
419 attributes = new Element[1];
420 attributes[0] = e;
421 }
422 else {
423 Element temp[] = attributes;
424 attributes = new Element[temp.length + 1];
425 System.arraycopy(temp, 0, attributes, 0, temp.length);
426 attributes[temp.length] = e;
427 temp = null;
428 }
429 }
430 e = null;
431 }
432 }
433 return attributes;
434 }
435
436 /** Method to construct an elements description by retrieving the correct attribute.
437 * @param element the Element whose name we wish to retrieve
438 * @return a String which is the elements description, or an empty string if no description exists
439 * @see org.greenstone.gatherer.msm.MSMUtils#getElementAttribute
440 * @see org.greenstone.gatherer.util.StaticStrings#COMMENT_VALUE
441 * @see org.greenstone.gatherer.util.StaticStrings#DEFINITION_VALUE
442 * @see org.greenstone.gatherer.util.StaticStrings#EMPTY_STR
443 * @see org.greenstone.gatherer.util.StaticStrings#SPACE_CHARACTER
444 */
445 static public String getDescription(Element element) {
446 String language_code_str = Gatherer.config.getLanguage();
447 StringBuffer description = new StringBuffer(StaticStrings.EMPTY_STR);
448 description.append(getElementAttribute(element, StaticStrings.DEFINITION_VALUE, language_code_str));
449 if(description.length() > 0) {
450 description.append(StaticStrings.SPACE_CHARACTER);
451 }
452 description.append(getElementAttribute(element, StaticStrings.COMMENT_VALUE, language_code_str));
453 language_code_str = null;
454 return description.toString();
455 }
456
457 /** Retrieve the value for the requested attribute in the required language. Once again this method must be aware of the differences between the old metadata sets and the new multilingual optimized ones.
458 * @param element_element the Element whose attributes we are searching through
459 * @param attribute_name_str the name of the desired attribute as a String
460 * @param language_code_str the two letter code String indicating the desired language
461 * @see org.greenstone.gatherer.msm.MSMUtils#getValue
462 * @see org.greenstone.gatherer.msm.MSMUtils#isAttributeLanguageDependant
463 * @see org.greenstone.gatherer.util.StaticStrings#ATTRIBUTE_ELEMENT
464 * @see org.greenstone.gatherer.util.StaticStrings#CODE_ATTRIBUTE
465 * @see org.greenstone.gatherer.util.StaticStrings#EMPTY_STR
466 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ATTRIBUTE
467 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ELEMENT
468 * @see org.greenstone.gatherer.util.StaticStrings#NAME_ATTRIBUTE
469 */
470 static public String getElementAttribute(Element element_element, String attribute_name_str, String language_code_str) {
471 boolean found = false;
472 String result = StaticStrings.EMPTY_STR;
473 Document document = element_element.getOwnerDocument();
474 // Determine if the attribute is language specific
475 if(isAttributeLanguageDependant(document, attribute_name_str)) {
476 NodeList language_elements = element_element.getElementsByTagName(StaticStrings.LANGUAGE_ELEMENT);
477 for(int i = 0; !found && i < language_elements.getLength(); i++) {
478 Element language_element = (Element) language_elements.item(i);
479 if(language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE).equals(language_code_str)) {
480 NodeList attribute_elements = language_element.getElementsByTagName(StaticStrings.ATTRIBUTE_ELEMENT);
481 for(int j = 0; !found && j < attribute_elements.getLength(); j++) {
482 Element attribute_element = (Element) attribute_elements.item(j);
483 if(attribute_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(attribute_name_str)) {
484 found = true;
485 result = MSMUtils.getValue(attribute_element);
486 }
487 attribute_element = null;
488 }
489 attribute_elements = null;
490 }
491 language_element = null;
492 }
493 language_elements = null;
494 }
495 else {
496 boolean first_match = false;
497 NodeList attribute_elements = element_element.getElementsByTagName(StaticStrings.ATTRIBUTE_ELEMENT);
498 for(int k = 0; !found && k < attribute_elements.getLength(); k++) {
499 Element attribute_element = (Element) attribute_elements.item(k);
500 // We don't want to consider those attributes found inside language elements
501 if(attribute_element.getParentNode() == element_element) {
502 ///ystem.err.println("First level");
503 String target_name_str = attribute_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
504 String target_language_str = attribute_element.getAttribute(StaticStrings.LANGUAGE_ATTRIBUTE);
505 ///ystem.err.println("Does " + target_name_str + " equal " + attribute_name_str + "?");
506 if(attribute_name_str.equals(target_name_str)) {
507 ///ystem.err.println("Does " + target_language_str + " equal " + language_code_str + "?");
508 if(language_code_str.equals(target_language_str)) {
509 ///ystem.err.println("Perfect match!");
510 found = true;
511 result = MSMUtils.getValue(attribute_element);
512 }
513 else if((result == StaticStrings.EMPTY_STR || first_match) && isLegacyMDS(document)) {
514 ///ystem.err.println("Legacy MDS");
515 // Special case for old style documents, where the english match is good enough
516 if(target_language_str.equals(StaticStrings.ENGLISH_LANGUAGE_STR)) {
517 ///ystem.err.println("English plate.");
518 result = MSMUtils.getValue(attribute_element);
519 }
520 // Super special case where the first match is better than nothing
521 else if(result == StaticStrings.EMPTY_STR && !first_match) {
522 ///ystem.err.println("First match.");
523 first_match = true;
524 result = MSMUtils.getValue(attribute_element);
525 }
526 }
527 }
528 target_language_str = null;
529 target_name_str = null;
530 }
531 //else {
532 ///ystem.err.println("Second level");
533 //}
534 attribute_element = null;
535 }
536 attribute_elements = null;
537
538 }
539 document = null;
540 return result;
541 }
542
543 /*************************************************************************/
544 /** 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.
545 * @param element An <strong>Element</strong> whose name we are interested in.
546 * @return A <strong>String</strong> representing this given elements fully namespace qualified name.
547 */
548 static final public String getFullName(Element element) {
549 return getFullName(element, "");
550
551 }
552 /*************************************************************************/
553 /** 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.
554 * @param element An <strong>Element</strong> whose name we are interested in.
555 * @return A <strong>String</strong> representing this given elements fully namespace qualified name.
556 */
557 static final public String getFullName(Element element, String namespace) {
558 StringBuffer name_buffer = new StringBuffer();
559 if(element == null) {
560 return "Error";
561 }
562 // First get the root node.
563 Document document = element.getOwnerDocument();
564 Element root = document.getDocumentElement();
565 document = null;
566 // Retrieve this elements name
567 name_buffer.append(element.getAttribute("name"));
568 // Now we check if element has a parent node, other than root. If so we begin building up the full name
569 Element parent_element = (Element) element.getParentNode();
570 while(parent_element != null && parent_element != root) {
571 name_buffer.insert(0, SF_SEP);
572 name_buffer.insert(0, parent_element.getAttribute("name"));
573 parent_element = (Element)parent_element.getParentNode();
574 }
575 parent_element = null;
576 // Finally insert the namespace and we are all done.
577 if(root != null) {
578 namespace = root.getAttribute("namespace");
579 }
580 root = null;
581 // If no root, or no namespace found, assume its extracted (at least then they can't edit it)
582 if(namespace == null || namespace.equals("")) {
583 namespace = Utility.EXTRACTED_METADATA_NAMESPACE;
584 }
585 name_buffer.insert(0, NS_SEP);
586 name_buffer.insert(0, namespace);
587 namespace = null;
588 return name_buffer.toString();
589 } // static public String getFullName(Element element)
590
591 /** Method to construct an elements name (sic identifier) by retrieving the correct attribute, language specific.
592 * @param element the Element whose name we wish to retrieve
593 * @return a String which is the elements identifier, or an empty string if no identifier exists
594 * @see org.greenstone.gatherer.msm.MSMUtils#getElementAttribute
595 * @see org.greenstone.gatherer.util.StaticStrings#IDENTIFIER_VALUE
596 * @see org.greenstone.gatherer.util.StaticStrings#NAME_ATTRIBUTE
597 */
598 static final public String getIdentifier(Element element) {
599 String identifier = getElementAttribute(element, StaticStrings.IDENTIFIER_VALUE, Gatherer.config.getLanguage());
600 // Failing the above we return the nodes name instead.
601 if(identifier == null || identifier.length() == 0) {
602 identifier = element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
603 }
604 return identifier;
605 }
606
607 /** Method to retrieve from the node given, a certain child node with the specified name.
608 * @param parent The <strong>Node</strong> whose children should be searched.
609 * @param name The required nodes name as a <strong>String</strong>.
610 * @return The requested <strong>Node</strong> if it is found, <i>null</i> otherwise.
611 */
612 static final public Node getNodeFromNamed(Node parent, String name) {
613 Node child = null;
614 for(Node i = parent.getFirstChild(); i != null && child == null;
615 i = i.getNextSibling()) {
616 if(i.getNodeName().equals(name)) {
617 child = i;
618 }
619 }
620 return child;
621 }
622 /** Look for the occurances 'field' of the element and return it if found.
623 * @return An <i>int</i> which matches the number in the occurances attribute of the element, or 0 if no such attribute.
624 */
625 static final public int getOccurances(Element element) {
626 int count = 0;
627 String number = null;
628 if((number = element.getAttribute("occurances")) != null) {
629 try {
630 count = Integer.parseInt(number);
631 }
632 catch(Exception error) {
633 count = 0;
634 }
635 }
636 return count;
637 }
638 /** 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>.
639 * @param element The <strong>Element</strong> whose option list we wish to view.
640 * @return A <strong>String[]</strong> containing the details of the option list.
641 * TODO implement.
642 * @see org.greenstone.gatherer.Dictionary
643 */
644 static final public String[] getOptionListDetails(Element element) {
645 return null;
646 }
647
648 /** This method traces the path between the 'root' node given and the target node. This path is returned starting at the root.
649 * @param root The root <strong>Node</strong> to start this path at.
650 * @param target The final <strong>Node</strong> to end up at.
651 * @return A <strong>Node[]</strong> that contains the sequence of nodes from root to target.
652 */
653 static final public Node[] getPath(Node root, Node target) {
654 if(root == target) {
655 return ArrayTools.add(null, root);
656 }
657 else {
658 return ArrayTools.add(getPath(root, target.getParentNode()), target);
659 }
660 }
661
662 /** 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>.
663 * @param element The <Strong>Element</strong> whose details we wish to gather.
664 * @return A <strong>String[]</strong> containing the structural details.
665 */
666 static final public String[] getStructuralDetails(MetadataSet mds, Element element) {
667 String details[] = new String[4];
668 //Element root = (Element)element.getParentNode();
669 //details[0] = root.getAttribute("name");
670 //details[1] = root.getAttribute("namespace");
671 details[0] = mds.getName();
672 details[1] = mds.getNamespace();
673 details[2] = getFullName(element);
674 details[3] = null;
675 // Get attributes
676 Vector attributes = new Vector();
677 for(Node n=element.getFirstChild(); n!=null; n=n.getNextSibling()) {
678 if(n.getNodeName().equals("Attribute")) {
679 Element temp = (Element)n;
680 attributes.add(temp.getAttribute("name") + "=" + getValue(n));
681 }
682 }
683 // Sort attributes
684 Collections.sort(attributes);
685 // Add attributes to details.
686 for(int i = 0; i < attributes.size(); i++) {
687 if(details[3] == null) {
688 details[3] = " " + attributes.get(i);
689 }
690 else {
691 details[3] = details[3] + "\n " + attributes.get(i);
692 }
693 }
694 return details;
695 }
696
697 /** Method to retrieve the value of a given node (not the assigned values tree!).
698 * @param element The <strong>Element</strong> whose value we wish to find.
699 * @return The value found as a <strong>String</strong>, or <i>null</i> if this element has no value.
700 */
701 static final public String getValue(Node element) {
702 // If we've been given a subject node first retrieve its value node.
703 if(element.getNodeName().equals("Subject")) {
704 element = getNodeFromNamed(element, "Value");
705 }
706 // If we've got a value node, then reconstruct the text. Remember that DOM will split text over 256 characters into several text nodes
707 if(element != null && element.hasChildNodes()) {
708 StringBuffer text_buffer = new StringBuffer();
709 NodeList text_nodes = element.getChildNodes();
710 for(int i = 0; i < text_nodes.getLength(); i++) {
711 Node possible_text = text_nodes.item(i);
712 if(possible_text.getNodeName().equals(StaticStrings.TEXT_NODE)) {
713 text_buffer.append(possible_text.getNodeValue());
714 }
715 }
716 return text_buffer.toString();
717 }
718 return "";
719 }
720
721 /** 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.
722 * @param current The root <strong>Node</strong> of a subtree of the AssignedValues tree.
723 * @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.
724 * @param values A <strong>Hashtable</strong> containing the mapping discovered so far in our tree traversal.
725 */
726 static final public void getValueMappings(Node current, String prefix, Hashtable values) {
727 if(current != null) {
728 String name = current.getNodeName();
729 String new_prefix = prefix;
730 // If we've found the outer layer of a new value, add it to our
731 // mapping.
732 if(name.equals("Subject")) {
733 Node value_node = getNodeFromNamed(current, "Value");
734 String value = getValue(value_node);
735 if(new_prefix != null) {
736 new_prefix = new_prefix + "\\" + value;
737 }
738 else {
739 new_prefix = value;
740 }
741 values.put(new_prefix, current);
742 }
743 if(name.equals("Subject") || name.equals("AssignedValues")) {
744 for(Node child = current.getFirstChild(); child != null;
745 child = child.getNextSibling()) {
746 getValueMappings(child, new_prefix, values);
747 }
748 }
749 }
750 }
751 /** Parses the value tree template file.
752 * @return The Document parsed.
753 */
754 static final public Document getValueTreeTemplate() {
755 return Utility.parse(Utility.METADATA_VALUE_TEMPLATE, true);
756 }
757
758 /** Determine if the named attribute is language specific for this collection. This information is found in a DOM attribute of the document element, as a comma separated list of attribute names.
759 * @param document the Document for which we wish to check the language requirements
760 * @param attribute_name_str the name of the attribute we a testing as a String
761 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGEDEPENDANT_ATTRIBUTE
762 */
763 static public boolean isAttributeLanguageDependant(Document document, String attribute_name_str) {
764 String language_specific_attributes = document.getDocumentElement().getAttribute(StaticStrings.LANGUAGEDEPENDANT_ATTRIBUTE).toLowerCase();
765 return language_specific_attributes.indexOf(attribute_name_str) != -1;
766 }
767
768 /** Determine if the given document is a legacy MDS or a new multilingual one. The easiest way to tell is whether there is a language_dependant attribute in the document element.
769 * @param document the Document to test
770 * @return true if this is an old mds, false otherwise
771 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGEDEPENDANT_ATTRIBUTE
772 * @see org.greenstone.gatherer.util.StaticStrings#EMPTY_STR
773 */
774 static public boolean isLegacyMDS(Document document) {
775 ///ystem.err.println("isLegacyMDS(): l_d = " + document.getDocumentElement().getAttribute(StaticStrings.LANGUAGEDEPENDANT_ATTRIBUTE));
776 return (document.getDocumentElement().getAttribute(StaticStrings.LANGUAGEDEPENDANT_ATTRIBUTE)).equals(StaticStrings.EMPTY_STR);
777 }
778
779 /** Method to compare two OptionsLists for equality.
780 * @param al A <strong>Node</strong> which represents an OptionList.
781 * @param bl The <strong>Node</strong> we wish to test against.
782 * @return A <i>boolean</i> which is <i>true</i> if the two option lists are equal, <i>false</i> otherwise.
783 * TODO Implementation
784 */
785 static final public boolean optionListsEqual(Node al, Node bl) {
786 // Compare the 'restricted' attribute of the two lists.
787 Element ae = (Element) al;
788 Element be = (Element) bl;
789 if(!ae.getAttribute("restricted").equals
790 (be.getAttribute("restricted"))) {
791 return false;
792 }
793 // Compare the Values under each list.
794 for(Node an = al.getFirstChild(); an != null;
795 an = an.getNextSibling()){
796 if(an.getNodeName().equals("Value")) {
797 boolean matched = false;
798 for(Node bn = bl.getFirstChild(); bn != null && !matched;
799 bn = bn.getNextSibling()) {
800 if(bn.getNodeName().equals("Value")) {
801 matched = valuesEqual(an, bn);
802 }
803 }
804 if(!matched) {
805 return false;
806 }
807 }
808 }
809 return true;
810 }
811
812 /** A method to remove a specific attribute element from an element. This attribute must match in name, language and in value before being removed. Note that this method supports both legacy and multilingual optimized versions of the mds.
813 * @param element_element the Element which represent the metadata element we are altering
814 * @param attribute_name_str the name of the attribute to remove as a String
815 * @param language_code_str the language code we must match as a String
816 * @param value_str the value String which also must match before we remove anything
817 * @return true if the desired attribute was successfully found and removed, false otherwise
818 * @see org.greenstone.gatherer.msm.MSMUtils#isAttributeLanguageDependant
819 * @see org.greenstone.gatherer.msm.MSMUtils#getValue(Element)
820 * @see org.greenstone.gatherer.util.StaticStrings#ATTRIBUTE_ELEMENT
821 * @see org.greenstone.gatherer.util.StaticStrings#CODE_ATTRIBUTE
822 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ATTRIBUTE
823 * @see org.greenstone.gatherer.util.StaticStrings#LANGUAGE_ELEMENT
824 * @see org.greenstone.gatherer.util.StaticStrings#NAME_ATTRIBUTE
825 */
826 static public boolean removeElementAttribute(Element element_element, String attribute_name_str, String language_code_str, String value_str) {
827 // Multilingual Optimized version
828 // 1. Determine the if this is one of the language specific attributes
829 if(isAttributeLanguageDependant(element_element.getOwnerDocument(), attribute_name_str)) {
830 // Retrieve the language elements, and determine the correct one
831 NodeList language_elements = element_element.getElementsByTagName(StaticStrings.LANGUAGE_ELEMENT);
832 for(int i = 0; i < language_elements.getLength(); i++) {
833 Element language_element = (Element) language_elements.item(i);
834 if(language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE).equalsIgnoreCase(language_code_str)) {
835 NodeList attribute_elements = language_element.getElementsByTagName(StaticStrings.ATTRIBUTE_ELEMENT);
836 for(int j = 0; j < attribute_elements.getLength(); j++) {
837 Element attribute_element = (Element) attribute_elements.item(j);
838 String target_name_str = attribute_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
839 String target_value_str = MSMUtils.getValue(attribute_element);
840 if(attribute_name_str.equals(target_name_str) && value_str.equals(target_value_str)) {
841 language_element.removeChild(attribute_element);
842 if(attribute_elements.getLength() == 0) {
843 element_element.removeChild(language_element);
844 }
845 target_value_str = null;
846 target_name_str = null;
847 attribute_element = null;
848 attribute_elements = null;
849 language_element = null;
850 language_elements = null;
851 return true;
852 }
853 target_value_str = null;
854 target_name_str = null;
855 attribute_element = null;
856 }
857 attribute_elements = null;
858 }
859 language_element = null;
860 }
861 language_elements = null;
862 // Not found
863 return false;
864 }
865 // Otherwise just use the old method
866
867 // Find the attribute to remove
868 NodeList attribute_elements = element_element.getElementsByTagName(StaticStrings.ATTRIBUTE_ELEMENT);
869 for (int k = 0; k < attribute_elements.getLength(); k++) {
870 Element attribute_element = (Element) attribute_elements.item(k);
871 // Remember to ignore any attributes that live within nested language elements
872 if (attribute_element.getParentNode() == element_element && attribute_element.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals(attribute_name_str) && attribute_element.getAttribute(StaticStrings.LANGUAGE_ATTRIBUTE).equalsIgnoreCase(language_code_str) && MSMUtils.getValue(attribute_element).equals(value_str)) {
873 // Match found, so remove the attribute node and return
874 element_element.removeChild(attribute_element);
875 attribute_element = null;
876 attribute_elements = null;
877 return true;
878 }
879 attribute_element = null;
880 }
881 attribute_elements = null;
882 // No match found
883 return false;
884 }
885
886 /** can only be used to set the english value */
887 static final public void setIdentifier(Node element, String value) {
888 // Get the 'identifier' Element
889 for(Node node = element.getFirstChild(); node != null;
890 node = node.getNextSibling()) {
891 if(node.getNodeName().equals("Attribute")) {
892 Element target = (Element)node;
893 if(target.getAttribute("name").equals("identifier") && target.getAttribute("language").equals("en")) {
894 Node text = target.getFirstChild();
895 text.setNodeValue(value);
896 break;
897 }
898 }
899 }
900 }
901 /** Set the value of the element attribute occurances.
902 * @param element The <strong>Element</strong> to change.
903 * @param value The value to change by as an <i>int</i>.
904 */
905 static final public void setOccurance(Element element, int value) {
906 Integer new_value = new Integer(getOccurances(element) + value);
907 element.setAttribute("occurances", new_value.toString());
908 }
909
910 /** Set the #text node value of some element.
911 * @param element the Element whose value we wish to set
912 * @param value the new value for the element as a String
913 */
914 static final public void setValue(Element element, String value) {
915 // Remove any existing child node(s)
916 clear(element);
917 // Add new text node.
918 element.appendChild(element.getOwnerDocument().createTextNode(value));
919 }
920
921 /** 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.
922 * @param current The current root <strong>Node</strong> of this AssignedValues tree subtree.
923 * @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.
924 * @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.
925 * @return A <strong>Node[]</strong> containing whatever values or aliases have been found during the trees traversal.
926 */
927 static final public Node[] traverseTree(Node current, int return_filter, boolean remove_leaves) {
928 Node leaves[] = null;
929 String name = current.getNodeName();
930 if(name.equals("Value") && (return_filter == VALUES || return_filter == BOTH)) {
931 leaves = ArrayTools.add(leaves, current);
932 }
933 else if(name.equals("Alias") && (return_filter == ALIASES || return_filter == BOTH)) {
934 leaves = ArrayTools.add(leaves, current);
935 }
936 else if(name.equals("Subject")) {
937 boolean has_subject_child = false;
938 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
939 for(int i = 0; i < children.length; i++) {
940 if(children[i].getNodeName().equals("Subject")) {
941 has_subject_child = true;
942 }
943 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
944 }
945 if(!has_subject_child && remove_leaves) {
946 Node parent = current.getParentNode();
947 parent.removeChild(current);
948 }
949 }
950 else if(name.equals("AssignedValues")) {
951 Node children[] = ArrayTools.nodeListToNodeArray(current.getChildNodes());
952 for(int i = 0; i < children.length; i++) {
953 leaves = ArrayTools.add(leaves, traverseTree(children[i], return_filter, remove_leaves));
954 }
955 }
956 return leaves;
957 }
958
959 /** 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.
960 * @param a_set The MetadataSet from which the Element a came from.
961 * @param a The Element at the root of the current AssignedValues tree.
962 * @param b_set The MetadataSet from which the Element b came from.
963 * @param b The root Element of the tree that is being merged.
964 * @return A <i>boolean</i> which is <i>true</i> if the trees merged without error, <i>false</i> otherwise.
965 */
966 static final public boolean updateValueTree(MetadataSet a_set, Element a, MetadataSet b_set, Element b) {
967 GValueModel avt = a_set.getValueTree(new ElementWrapper(a));
968 GValueModel bvt = b_set.getValueTree(new ElementWrapper(b));
969 // If neither element even has a value tree, we're all done.
970 if(avt == null && bvt == null) {
971 avt = null;
972 bvt = null;
973 return true;
974 }
975 // If the new element has no value tree then nothing needs to be done.
976 else if(avt != null && bvt == null) {
977 avt = null;
978 bvt = null;
979 return true;
980 }
981 // If only the new element has a value tree, then add all of its values
982 // immediately.
983 else if(avt == null && bvt != null) {
984 a_set.addValueTree(new ElementWrapper(a), bvt);
985 avt = null;
986 bvt = null;
987 return true;
988 }
989 // We have both trees for both elements, time to merge.
990 else {
991 Document document = avt.getDocument();
992 Hashtable a_map = new Hashtable();
993 getValueMappings(document.getDocumentElement(), null, a_map);
994 Hashtable b_map = new Hashtable();
995 getValueMappings(bvt.getDocument().getDocumentElement(), null, b_map);
996 // For each new entry in b_map
997 for(Enumeration b_keys = b_map.keys(); b_keys.hasMoreElements(); ) {
998 String b_key = (String)b_keys.nextElement();
999 // Test if there is already an entry in a_map.
1000 if(!a_map.containsKey(b_key)) {
1001 // If not, search through a_map for the longest match.
1002 Node target = document.getDocumentElement();
1003 String last_match = null;
1004 for(Enumeration a_keys = a_map.keys();
1005 a_keys.hasMoreElements(); ) {
1006 String a_key = (String)a_keys.nextElement();
1007 if(b_key.startsWith(a_key)) {
1008 if(last_match == null || a_key.length() > last_match.length()) {
1009 last_match = a_key;
1010 target = (Node)a_map.get(a_key);
1011 }
1012 }
1013 a_key = null;
1014 }
1015 // Now import the node at b_key and add it to target.
1016 Node subtree = (Node)b_map.get(b_key);
1017 subtree = document.importNode(subtree, true);
1018 // Find the node to insert before...
1019 String name = getValue(subtree);
1020 Node move = null;
1021 for(Node n = target.getFirstChild(); n != null && move == null; n = n.getNextSibling()) {
1022 if(n.getNodeName().equals("Subject")) {
1023 if(name.compareTo(getValue(n)) <= 0) {
1024 move = n;
1025 }
1026 }
1027 }
1028 if(move == null) {
1029 target.appendChild(subtree);
1030 }
1031 else {
1032 target.insertBefore(subtree, move);
1033 }
1034 target = null;
1035 last_match = null;
1036 subtree = null;
1037 name = null;
1038 move = null;
1039 }
1040 b_key = null;
1041 }
1042 document = null;
1043 a_map = null;
1044 b_map = null;
1045 avt = null;
1046 bvt = null;
1047 return true;
1048 }
1049 }
1050
1051 /** Method to determine if two Value nodes are equal.
1052 * @param av A <strong>Node</strong> representing a value.
1053 * @param bv The <strong>Node</strong> we want to compare it to.
1054 * @return A <i>boolean</i> which is <i>true</i> if the two value nodes are equal, <i>false</i> otherwise.
1055 */
1056 static final public boolean valuesEqual(Node av, Node bv) {
1057 // Check we are comparing apples and apples...
1058 if(av.getNodeName().equals("Value") &&
1059 bv.getNodeName().equals("Value")) {
1060 // Retrieve and then compare their text values.
1061 Node at = av.getFirstChild();
1062 Node bt = bv.getFirstChild();
1063 if(at.getNodeValue().equals(bt.getNodeValue())) {
1064 return true;
1065 }
1066 }
1067 return false;
1068 }
1069
1070 /** A comparator for sorting metadata element-value pairs into their standard order (elements) then alphabetical order (values). */
1071 static final private class MetadataComparator
1072 implements Comparator {
1073 /** Compares its two arguments for order. */
1074 public int compare(Object o1, Object o2) {
1075 int result = 0;
1076 ElementWrapper e1 = null;
1077 ElementWrapper e2 = null;
1078 String v1 = null;
1079 String v2 = null;
1080 if(o1 instanceof Metadata && o2 instanceof Metadata) {
1081 Metadata m1 = (Metadata) o1;
1082 Metadata m2 = (Metadata) o2;
1083 ///ystem.err.println("MSMUtils.compare(" + m1 + ", " + m2 + ") = ");
1084 e1 = m1.getElement();
1085 e2 = m2.getElement();
1086 v1 = m1.getValue().toLowerCase();
1087 v2 = m2.getValue().toLowerCase();
1088 }
1089 else if(o1 instanceof ElementWrapper && o2 instanceof ElementWrapper) {
1090 e1 = (ElementWrapper) o1;
1091 e2 = (ElementWrapper) o2;
1092 }
1093 if(e1 != null && e2 != null) {
1094 // First we compare the namespaces
1095 result = e1.getNamespace().compareTo(e2.getNamespace());
1096 if(result == 0 && Gatherer.c_man != null && Gatherer.c_man.ready() && e1.getNamespace() != null) {
1097 // Now, given both elements are in the same set, we compare the element ordering using methods in MetadataSet
1098 MetadataSet set = Gatherer.c_man.getCollection().msm.getSet(e1.getNamespace());
1099 ///ystem.err.print("MetadataSet.compare(" + e1 + ", " + e2 + ") = ");
1100 if(set != null) {
1101 result = set.compare(e1.getElement(), e2.getElement());
1102 ///ystem.err.println(result);
1103 if(result == 0 && v1 != null && v2 != null) {
1104 // Finally we compare the values alphabetically.
1105 result = v1.compareTo(v2);
1106 }
1107 }
1108 else {
1109 return 0;
1110 }
1111 }
1112 }
1113 else {
1114 result = o1.toString().compareTo(o2.toString());
1115 }
1116 ///ystem.err.println("Result: " + result);
1117 return result;
1118 }
1119
1120 /** Indicates whether some other object is "equal to" this Comparator. */
1121 public boolean equals(Object obj) {
1122 return compare(this, obj) == 0;
1123 }
1124 }
1125}
Note: See TracBrowser for help on using the repository browser.