source: trunk/gli/src/org/greenstone/gatherer/gems/MetadataSet.java@ 12306

Last change on this file since 12306 was 10345, checked in by mdewsnip, 19 years ago

Removed some more crap out of the Utility class.

  • Property svn:keywords set to Author Date Id Revision
File size: 21.2 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.gems;
38
39import java.io.*;
40import java.net.*;
41import java.util.*;
42import org.apache.xerces.dom.*;
43import org.greenstone.gatherer.Configuration;
44import org.greenstone.gatherer.DebugStream;
45import org.greenstone.gatherer.Dictionary;
46import org.greenstone.gatherer.util.StaticStrings;
47import org.greenstone.gatherer.util.XMLTools;
48import org.w3c.dom.*;
49/** An semi-data class to hold details about a loaded metadata set, it also provides some methods to manipulating the data within.
50 * @author John Thompson, Greenstone Digital Library, University of Waikato
51 * @version 2.3b
52 */
53public class MetadataSet {
54 /** The <Strong>Document</strong> of the DOM model. */
55 private Document document = null;
56 /** The document <strong>Element</strong> of the DOM model. */
57 private Element root = null;
58 /** The <strong>File</strong> this metadata set was loaded from. */
59 public String old_filename = null;
60 private File file = null;
61 /** The list of metadata elements which are, of course, children of the root node. */
62 private ArrayList elements = null;
63
64 // the nmae of the collection if this set is collection-specific
65 private String collection = null;
66 //set_changed is check in msm(metadatasetmanager).save() and if this is true then we save
67 //the collection, otherwise we leave it as is
68 private boolean setChanged = false;
69 private String current_language_code;
70 /** The description of this metadata set. Cached as it takes more computation time. */
71 private String description = null;
72 /** The name of this metadata set. Cached as it takes more computation time. */
73 private String name = null;
74 /** An element of the tree pruning filter enumeration, that indicates all nodes in the tree should be retained. */
75 static final int ALL_VALUES = 1;
76 /** An element of the tree pruning filter enumeration, that indicates only metadata Subject nodes or higher should remain after pruning. */
77 static final int SUBJECTS_ONLY = 2;
78 /** An element of the tree pruning filter enumeration, that indicates no value nodes i.e. the entire AssignedValues subtree, should remain after pruning. */
79 static final int NO_VALUES = 3;
80
81 public MetadataSet(String metadata_template) {
82
83 this.file = new File(metadata_template);
84 this.document = XMLTools.parseXMLFile(metadata_template, true);
85
86 init(true); // use class loader
87 }
88
89 /** Constructor.
90 * @param file The file the metadata set should be loaded from.
91 */
92 public MetadataSet(File file) {
93 this.file = file;
94 this.document = XMLTools.parseXMLFile(file);
95
96 init(false); // don't use class loader
97 }
98
99 /** Copy constructor.
100 * @param original The original metadata set to copy from.
101 */
102 public MetadataSet(MetadataSet original) {
103 // We have to create a new document.
104 document = new DocumentImpl(original.getDocument().getDoctype());
105 root = (Element) document.importNode(original.getDocument().getDocumentElement(), true);
106 document.appendChild(root);
107 elements = XMLTools.getChildElementsByTagName(root, "Element");
108 //elements = root.getElementsByTagName("Element");
109 file = original.getFile();
110 }
111
112 public boolean getSetChanged() {
113
114 return setChanged;
115 }
116 public void setSetChanged(boolean val) {
117
118 setChanged = val;
119 }
120
121
122 public void setCollection(String coll) {
123 collection = coll;
124
125 }
126 /** Add a mds level attribute.
127 * @param name The name of the attribute to add as a <Strong>String</strong>.
128 * @param value The value as a <strong>String</strong>.
129 */
130 public void addAttribute(String name, String value) {
131 root.setAttribute(name, value);
132 }
133
134
135 /** Add a new default metadata element with the given name to this metadata set.
136 This method also handles the addtion of 'subelements'
137 * @param name The name of this element as a <strong>String</strong>.
138 * @param parent The parent element as an <strong>GEMSNode</strong>. If null, the parent is the metadataset root.
139 * @return An <strong>ElementWrapper</strong> around the newly created element or null if the element was not created.
140 */
141 public ElementWrapper addElement(String name, String language, GEMSNode parent)
142 {
143 Element parent_element = null;
144
145 //The element that we are creating
146 Element element = document.createElementNS("","Element");
147 element.setAttribute("name",name);
148
149 //Add identifier attribute. This handles if we use the new or old language identification
150 MSMUtils.addElementAttribute(element, "identifier", language, name);
151
152 if(parent == null) //A element
153 {
154 parent_element = root;
155 elements.add(element); //Add the new element to the list of 'em
156 }
157 else //A subelement!
158 {
159 //Add the new subelement to the list of 'em
160 parent.getElement().addSubelementToList(element);
161 parent_element = parent.getElement().getElement(); //get the parent as an Element
162 }
163
164 parent_element.appendChild(element);
165
166 return new ElementWrapper(element);
167 }
168
169
170 public int compare(Element e1, Element e2) {
171 int result = 0;
172 // Check that they're not the same element.
173 if(e1 != e2) {
174 int index_e1 = -1;
175 int index_e2 = -1;
176 // Locate the indexes for each element.
177 for(int i = 0; i < elements.size(); i++) {
178 Node element = (Node)elements.get(i);
179 if(element == e1) {
180 index_e1 = i;
181 }
182 if(element == e2) {
183 index_e2 = i;
184 }
185 }
186 if(index_e1 < index_e2) {
187 result = -1;
188 }
189 else {
190 result = 1;
191 }
192 }
193 return result;
194 }
195
196 /** A method to determine if this metadata set contains an element with a certain name (case sensitive).
197 * @param name A <strong>String</strong> which is the name of the element whose presence we are checking.
198 * @return A <i>boolean</i> which is <i>true</i> if the named element exists, <i>false</i> otherwise.
199 */
200 public boolean containsElement(String name) {
201 for(int i = 0; i < elements.size(); i++) {
202 Element sibling = (Element) elements.get(i);
203 String sibling_name = sibling.getAttribute("name");
204 if(sibling_name.equals(name)) {
205 return true;
206 }
207 }
208 return false;
209 }
210
211 public NamedNodeMap getAttributes() {
212 return root.getAttributes();
213 }
214
215 /** Method to retrieve the contact address of the metadata set creator.
216 * @return A <strong>String</strong> containing the address.
217 */
218 /* private String getContact() {
219 return root.getAttribute("contact");
220 } */
221 /** Method to retrieve the name of the creator of this metadata set.
222 * @return A <strong>String</strong> containing the name.
223 */
224 public String getCreator() {
225 return root.getAttribute("creator");
226 }
227 /** Method to retrieve the description of this metadata set. Note that this is language specific, so we determine the desired language from the Dictionary. If no such entry exists, first try returning the english version and failing that the first description found.
228 * @return The description as a <strong>String</strong>.
229 */
230 public String getDescription() {
231 if(current_language_code != null && !Configuration.getLanguage().equals(current_language_code)) {
232 description = null;
233 }
234 if(description == null) {
235 description = getAttribute(StaticStrings.DESCRIPTION_ELEMENT, Dictionary.get("GEMS.No_Description"));
236 }
237 return description;
238 }
239
240 /** Method to retrieve the <strong>Document</strong> associated with this metadata set.
241 * @return The <strong>Document</strong> representing this metadata set.
242 */
243 public Document getDocument() {
244 return document;
245 }
246 /** Method to retrieve the metadata element indicated by an index.
247 * @param index An <i>int</i> specifying the required element.
248 * @return The <strong>Element</strong> at the index.
249 */
250 public Element getElement(int index) {
251 return (Element)elements.get(index);
252 }
253 /** This method is used to acquire a reference to the element which matches the given metadata. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
254 * @param metadata A <strong>Metadata</strong> object representing an element and value assignment.
255 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
256 */
257// public Element getElement(Metadata metadata) {
258// return metadata.getElement().getElement();
259// }
260 /** This method is used to acquire a reference to the element which has the name specified. Note that this is not the same as <i>metadata.getElement()</i> as the reference returned by it may now be obsolete.
261 * @param name A <strong>String</strong> stating the desired objects name.
262 * @return A 'live' reference to an <strong>Element</strong> which is the same as that referenced by the given metadata, or <i>null</i> if there is no such element.
263 */
264 public Element getElement(String name) {
265 // Strip any namespace.
266 while(name.indexOf(".") != -1 && !name.equals(".")) {
267 name = name.substring(name.indexOf(".") + 1);
268 }
269 ///ystem.err.println("Get element named " + name);
270 for(int i = 0; i < elements.size(); i++) {
271 Element element = (Element) elements.get(i);
272 ///ystem.err.println("Compare to: " + element.getAttribute("name"));
273 if(element.getAttribute("name").equals(name)) {
274 return element;
275 }
276 }
277 return null;
278 }
279
280 public Element getElement(Element parent_element, String name) {
281 DebugStream.println("Get element named " + name + " from " + parent_element.getAttribute("name"));
282 ArrayList elements = XMLTools.getChildElementsByTagName(parent_element, "Element");
283 for(int i = 0; i < elements.size(); i++) {
284 Element element = (Element) elements.get(i);
285 if(element.getAttribute("name").equals(name) && element.getParentNode() == parent_element) {
286 elements = null;
287 return element;
288 }
289 }
290 elements = null;
291 return null;
292 }
293 /** Method to acquire a list of all the elements in this metadata set.
294 * @return A <strong>NodeList</strong> containing all of this sets elements.
295 */
296 public ArrayList getElements() {
297 return elements;
298 }
299
300 /** Method to retrieve a list of all the elements in this metadata set, sorted.
301 * @return A <strong>Vector</strong> containing all of the elements of this sets, sorted.
302 */
303// public Vector getElementsSorted() {
304// Vector elements_list = new Vector();
305// for (int i = 0; i < elements.size(); i++) {
306// elements_list.add(new ElementWrapper((Element) elements.get(i)));
307// }
308// Collections.sort(elements_list, MSMUtils.METADATA_COMPARATOR);
309// return elements_list;
310// }
311
312 /** Method to retrieve the original file this metadata set was created from.
313 * @return A <strong>File</strong>.
314 */
315 public File getFile() {
316 return file;
317 }
318 /** Get the last changed attribute.
319 * @return Last changed as a <strong>String</strong>.
320 */
321 public String getLastChanged() {
322 return root.getAttribute("lastchanged");
323 }
324
325 /** Method to get this metadata sets name. Note that this is language specific, so we determine the desired language from the Dictionary.
326 If no such entry exists, first try returning the english version and failing that the first name found.
327 * @return A <strong>String</strong> which contains its name.
328 */
329 public String getName() {
330 if(current_language_code != null && !Configuration.getLanguage().equals(current_language_code)) {
331 // name = null;
332 return null;
333 }
334 if(name == null) {
335
336 //name = getAttribute(StaticStrings.NAME_ELEMENT, Dictionary.get("MSM.No_Name"));
337 return getAttribute(StaticStrings.NAME_ELEMENT, Dictionary.get("GEMS.No_Name"));
338
339 }
340
341 return name;
342 }
343 /** Method to retrieve this metadata sets namespace.
344 * @return The namespace as a <strong>String</strong>.
345 */
346 public String getNamespace() {
347 return root.getAttribute("namespace");
348 }
349
350 /**
351 Method to change this metadata set's namespace
352 @param: The new namespace as a <strong>String</strong>
353 */
354 public void setNamespace(String new_namespace)
355 {
356 root.setAttribute("namespace", new_namespace);
357 }
358
359 /** Method to retrieve the root element, i.e. the Document Element, of the DOM model behind this metadata set.
360 * @return An <strong>Element</strong> which is at the root of the modal.
361 */
362 public Element getRoot() {
363 return root;
364 }
365
366 /** Remove a mds level attribute.
367 * @param name The name of the attribute to remove.
368 */
369 public void removeAttribute(String name) {
370 root.removeAttribute(name);
371 }
372
373 /** Method to remove the given element from this metadata set.
374 * @param element The <strong>Element</strong> to be removed.
375 * @param parent The parent element (wrapped, so can access its subelement list), as an <strong>ElementWrapper</strong>. If null, the parent is root.
376 */
377 public void removeElement(Element element, ElementWrapper parent) {
378 try {
379 ElementWrapper element_wrapped = new ElementWrapper(element);
380
381 if(parent == null)
382 {
383 elements.remove(element);
384 root.removeChild(element);
385 }
386 else
387 {
388 parent.removeSubelementFromList(element);
389 parent.getElement().removeChild(element);
390 }
391
392 } catch(Exception DOMException) { //The element does not exist!
393 System.err.println("Exception: '"+ DOMException + "' occured. Cannot remove element because it does not exist.");
394 }
395 }
396
397 /**
398 Rename the metadata element.
399 @param oldElement An <strong>Element</strong> we wish to rename
400 @param newName A <strong>String</strong> which contains the new name of the element.
401 @return An <strong>ElementWrapper</strong> around the newly named element
402 */
403 public void renameElement(Element oldElement, String newName)
404 {
405 oldElement.setAttribute("name", newName); //Rename the element
406 }
407
408
409 /** Set one of the mds level attributes.
410 * @param name The attribute to change.
411 * @param value its new value.
412 */
413 public void setAttribute(String name, String value) {
414 root.setAttribute(name, value);
415 }
416 /** Once the metadata set has been saved to a different location, this is used to update the file parameter.
417 * @param file The new location of this metadata set <strong>File</strong>.
418 */
419 public void setFile(File file) {
420 this.file = file;
421 }
422
423 public void setName(String name) {
424 // Retrieve the name element. We look for the first english one.
425 boolean found_it = false;
426 Element name_element = null;
427 Element metadataset_element = document.getDocumentElement();
428 NodeList name_elements = metadataset_element.getElementsByTagName(StaticStrings.NAME_ELEMENT);
429 for(int i = 0; (i < name_elements.getLength() && !found_it); i++) {
430 Element possible_name_element = (Element) name_elements.item(i);
431 if(possible_name_element.getAttribute(StaticStrings.LANGUAGE_ATTRIBUTE).equals("en")) {
432 // Found it.
433 found_it = true;
434 name_element = possible_name_element;
435 //System.out.println("Found english name: " + name_element); //debug
436 }
437 }
438 // If there is none add one. Note that we can only add english metadata sets. Although others can edit them to add further names as necessary.
439 if(name_element == null) {
440 name_element = document.createElement(StaticStrings.NAME_ELEMENT);
441 name_element.setAttribute(StaticStrings.LANGUAGE_ATTRIBUTE, "en");
442 metadataset_element.insertBefore(name_element, metadataset_element.getFirstChild());
443 }
444 // Replace the text node
445 while(name_element.hasChildNodes()) {
446 name_element.removeChild(name_element.getFirstChild());
447 }
448 name_element.appendChild(document.createTextNode(name));
449 }
450
451 /** Method to determine the number of elements in this set.
452 * @return An <i>int</i> specifying the element count.
453 */
454 public int size() {
455 return elements.size();
456 }
457 /** Method to translate this class into a meaningful string, which in this case is the metadata sets name.
458 * @return The metadata sets name as a <strong>String</strong>.
459 */
460 public String toString() {
461
462 String name = getName();
463 // If there is no given name, then use the namespace as there is garaunteed to be one of them.
464 if(name == null || name.length() == 0) {
465 name = root.getAttribute("namespace");
466 }
467 // Append namespace
468 String namespace = root.getAttribute("namespace");
469 if(namespace == null || namespace.equals("")) {
470 namespace = MetadataSetManager.EXTRACTED_METADATA_NAMESPACE;
471 }
472 name = name + " (" + namespace + ")";
473 if (collection == null)
474 return name;
475 return "["+collection+"] "+name;
476 }
477
478 /** This method retrieves the required attribute from the Metadata Set, typically it's name or it's description. Note that this method is language dependent, and moreover supports both legacy metadata sets and the new sets optimized for multiple languages.
479 * @param element_name the name of the type of element the required information is in as a String
480 * @param default_string the value to return if no such element is found also as a String
481 * @see org.greenstone.gatherer.Configuration#getLanguage()
482 * @see org.greenstone.gatherer.msm.MSMUtils#getValue(Node)
483 * @see org.greenstone.gatherer.util.StaticStrings#CODE_ATTRIBUTE
484 * @see org.greenstone.gatherer.util.StaticStrings#SETLANGUAGE_ELEMENT
485 */
486 private String getAttribute(String element_name, String default_string) {
487 String result = null;
488
489 // Determine the language code.
490 current_language_code = Configuration.getLanguage();
491
492 ///ystem.err.println("Searching for the " + element_name + " in " + current_language_code);
493
494 // New Metadata Set Format makes use of deferred-node-expansion to save memory - rather than create nodes for a name and description in each language, nodes which have potentially huge strings, we instead create simplier SETLANGUAGE nodes, and then only expand the one in the desired language. Of course if a user happens to change to every available language slightly more memory will be used than in the old method. For instance consider the DLS with 25 languages, each with a name node of 50 bytes and an descriptions of 500. Thus old style > 13750 bytes while new style < 600.
495 NodeList set_language_elements = document.getElementsByTagName(StaticStrings.SETLANGUAGE_ELEMENT);
496 for(int b = 0; b < set_language_elements.getLength(); b++) {
497 Element set_language_element = (Element) set_language_elements.item(b);
498 String code = set_language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE).toLowerCase();
499 if(code.equals(current_language_code)) {
500 NodeList specific_elements = set_language_element.getElementsByTagName(element_name);
501 if(specific_elements.getLength() > 0) {
502 Element specific_element = (Element) specific_elements.item(0);
503 result = MSMUtils.getValue(specific_element);
504 specific_element = null;
505 }
506 specific_elements = null;
507 }
508 code = null;
509 set_language_element = null;
510 }
511 set_language_elements = null;
512 // And we may be all done
513 if(result != null) {
514 return result;
515 }
516
517 // Failing that we move on to an older style search - start by recovering all Name elements
518 NodeList possible_elements = document.getElementsByTagName(element_name);
519 // Iterate through the available names looking for the appropriate one. Also make note of the first name, then overwrite it with any english one.
520 boolean found = false;
521 for(int i = 0; !found && i < possible_elements.getLength(); i++) {
522 Element possible_element = (Element) possible_elements.item(i);
523 String possible_element_code = possible_element.getAttribute("language").toLowerCase();
524 if(possible_element_code.equals(current_language_code) || name == null) {
525 result = MSMUtils.getValue(possible_element);
526 found = true;
527 }
528 possible_element_code = null;
529 possible_element = null;
530 }
531 possible_elements = null;
532 // Failing all that set an error message
533 if(result == null) {
534 result = default_string;
535 }
536 return result;
537 }
538
539 private void init(boolean use_classloader) {
540
541 if(this.document != null) {
542 this.root = document.getDocumentElement();
543 this.elements = XMLTools.getChildElementsByTagName(root, "Element");
544 }
545 else {
546 DebugStream.println("Error! Missing mds file: " + file.getAbsolutePath());
547 }
548 }
549}
Note: See TracBrowser for help on using the repository browser.