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 | */
|
---|
27 | package 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 | **************************************************************************************/
|
---|
39 | import java.io.*;
|
---|
40 | import java.util.*;
|
---|
41 | import org.greenstone.gatherer.Gatherer;
|
---|
42 | import org.greenstone.gatherer.cdm.CommandTokenizer;
|
---|
43 | import org.greenstone.gatherer.mem.Attribute;
|
---|
44 | import org.greenstone.gatherer.msm.MetadataSet;
|
---|
45 | import org.greenstone.gatherer.util.ArrayTools;
|
---|
46 | import org.greenstone.gatherer.util.StaticStrings;
|
---|
47 | import org.greenstone.gatherer.util.Utility;
|
---|
48 | import org.greenstone.gatherer.valuetree.GValueModel;
|
---|
49 | import 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 | */
|
---|
54 | public 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 ae An <strong>Element</strong>.
|
---|
227 | * @param b_set The <strong>MetadataSet</strong> b comes from.
|
---|
228 | * @param be 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(Node)
|
---|
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(attribute_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(Node)
|
---|
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 | }
|
---|