source: main/trunk/gli/src/org/greenstone/gatherer/metadata/MetadataXMLFileManager.java@ 23410

Last change on this file since 23410 was 23410, checked in by kjdon, 13 years ago

Katherine fixed the reason why non-accumulating metadata (gs.FilenameEncoding) was glitchy and at times did not override meta assigned at higher levels with those assigned at lower levels. The applicable_metadata_xml_files needed to be inspected in a guaranteed top-down order for the override to work.

  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/**
2 *############################################################################
3 * A component of the Greenstone Librarian Interface, part of the Greenstone
4 * digital library suite from the New Zealand Digital Library Project at the
5 * University of Waikato, New Zealand.
6 *
7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
8 *
9 * Copyright (C) 2005 New Zealand Digital Library Project
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *############################################################################
25 */
26
27package org.greenstone.gatherer.metadata;
28
29
30import java.io.*;
31import java.util.*;
32import org.greenstone.gatherer.DebugStream;
33import org.greenstone.gatherer.Gatherer;
34import org.greenstone.gatherer.collection.CollectionManager;
35import org.greenstone.gatherer.collection.CollectionTreeNode;
36import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
37import org.greenstone.gatherer.util.XMLTools;
38
39
40/** This class is a static class that manages the metadata.xml files */
41public class MetadataXMLFileManager
42{
43 static private ArrayList metadata_xml_files = new ArrayList();
44 /** The objects listening for MetadataChanged events. */
45 static private ArrayList metadata_changed_listeners = new ArrayList();
46 /** Keep track of which metadata.xml files have been modified so we can upload them to the server */
47 static private ArrayList modified_metadata_xml_files = new ArrayList();
48
49 /** For non-accumulating metadata (gs.FilenameEncoding), need the metadata.xml files
50 * sorted in top-down folder level order, which is achieved through this Comparator.
51 * By declaring a static class member here, we avoid recreating this object for every
52 * comparison, which would otherwise have resulted in a constructor call each time. */
53 static private MetadataXMLFileComparator metadataXMLFileComparator = new MetadataXMLFileComparator();
54
55
56 static public void addMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
57 {
58 addMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
59 }
60
61
62 static public void addMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
63 {
64 ArrayList metadata_values = new ArrayList();
65 metadata_values.add(metadata_value);
66 addMetadata(file_nodes, metadata_values);
67 }
68
69
70 static public void addMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
71 {
72 // Check the list of metadata values is non-empty
73 if (metadata_values.isEmpty()) {
74 return;
75 }
76
77 // Add the metadata to each file node in turn
78 for (int i = 0; i < file_nodes.length; i++) {
79 File current_file = file_nodes[i].getFile();
80 DebugStream.println("Adding metadata to " + current_file.getAbsolutePath());
81
82 // Find which metadata.xml file needs editing
83 boolean applicable_metadata_xml_file_found = false;
84 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
85 String current_file_directory_path = current_file_directory.getAbsolutePath();
86 for (int j = 0; j < metadata_xml_files.size(); j++) {
87 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
88
89 // This metadata.xml file is only applicable if it is at the same level as the file
90 if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
91 applicable_metadata_xml_file_found = true;
92 metadata_xml_file.addMetadata(current_file, metadata_values);
93 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
94 modified_metadata_xml_files.add(metadata_xml_file);
95 }
96 }
97 }
98
99 // If no applicable metadata.xml file exists, we have to create a new one
100 if (!applicable_metadata_xml_file_found) {
101 // Create a new (empty) metadata.xml file in the file's directory...
102 File new_metadata_xml_file_file = new File(current_file_directory, "metadata.xml");
103 XMLTools.writeXMLFile(new_metadata_xml_file_file, XMLTools.parseXMLFile("xml/metadata.xml", true));
104
105 // ...load it...
106 MetadataXMLFile new_metadata_xml_file = loadMetadataXMLFile(new_metadata_xml_file_file,true);
107
108 // ...and add the metadata
109 new_metadata_xml_file.addMetadata(current_file, metadata_values);
110 if (!modified_metadata_xml_files.contains(new_metadata_xml_file)) {
111 modified_metadata_xml_files.add(new_metadata_xml_file);
112 }
113 }
114 }
115
116 // Let any listeners know that the metadata has changed
117 fireMetadataChangedEvent(file_nodes);
118 }
119
120
121 static public void addMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
122 {
123 metadata_changed_listeners.add(metadata_changed_listener);
124 }
125
126
127 static public void clearMetadataXMLFiles()
128 {
129 metadata_xml_files.clear();
130 }
131
132
133 static private void fireMetadataChangedEvent(CollectionTreeNode[] file_nodes)
134 {
135 // Send the event off to all the MetadataChangedListeners
136 for (int i = 0; i < metadata_changed_listeners.size(); i++) {
137 ((MetadataChangedListener) metadata_changed_listeners.get(i)).metadataChanged(file_nodes);
138 }
139 }
140
141
142 /** Returns the metadata assigned to a file outside the collection, excluding folder-level/inherited metadata. */
143 static public ArrayList getMetadataAssignedDirectlyToExternalFile(File file)
144 {
145 DebugStream.println("Getting metadata assigned directly to external file " + file + "...");
146
147 // Build up a list of applicable metadata.xml files
148 ArrayList applicable_metadata_xml_files = new ArrayList();
149
150 File directory = (file.isDirectory() ? file : file.getParentFile());
151 while (directory != null) {
152 File metadata_xml_file = new File(directory, "metadata.xml");
153 if (metadata_xml_file.exists() && !metadata_xml_file.isDirectory()) {
154 // It is very important that shallower files come before deeper ones
155 applicable_metadata_xml_files.add(0, new MetadataXMLFile(metadata_xml_file.getAbsolutePath()));
156 }
157
158 directory = directory.getParentFile();
159 }
160
161 // Get the metadata assigned to the specified file from the applicable metadata.xml files
162 ArrayList assigned_metadata = getMetadataAssignedToFile(file, applicable_metadata_xml_files);
163
164 // Remove any folder-level metadata
165 for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
166 if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
167 assigned_metadata.remove(i);
168 }
169 }
170
171 return assigned_metadata;
172 }
173
174
175 /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */
176 static public ArrayList getMetadataAssignedDirectlyToFile(File file)
177 {
178 // Get all the metadata assigned to the specified file...
179 ArrayList assigned_metadata = getMetadataAssignedToFile(file);
180
181 // ...then remove any folder-level metadata
182 for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
183 if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
184 assigned_metadata.remove(i);
185 }
186 }
187
188 return assigned_metadata;
189 }
190
191
192 /** Returns all the metadata assigned to a file inside the collection. */
193 static public ArrayList getMetadataAssignedToFile(File file)
194 {
195 // Build up a list of applicable metadata.xml files
196 ArrayList applicable_metadata_xml_files = new ArrayList();
197
198 // Look at each loaded metadata.xml file to see if it is potentially applicable
199 String file_directory_path = (file.isDirectory() ? file : file.getParentFile()).getAbsolutePath() + File.separator;
200 for (int i = 0; i < metadata_xml_files.size(); i++) {
201 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
202
203 // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
204 if (file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath() + File.separator)) {
205 applicable_metadata_xml_files.add(metadata_xml_file);
206 }
207 }
208
209 // sort the metadataxml files in order starting from those in the
210 // topmost folders down to the one in the lowest level folder.
211 Collections.sort(applicable_metadata_xml_files, metadataXMLFileComparator);
212 // Return the metadata assigned to the specified file from the applicable metadata.xml files
213 return getMetadataAssignedToFile(file, applicable_metadata_xml_files);
214 }
215
216
217 static private ArrayList getMetadataAssignedToFile(File file, ArrayList applicable_metadata_xml_files)
218 {
219 // Build up a list of metadata values assigned to this file
220 ArrayList metadata_values_all = new ArrayList();
221
222 // Look at each applicable metadata.xml file to see if it assigns metadata to this file
223 for (int i = 0; i < applicable_metadata_xml_files.size(); i++) {
224 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) applicable_metadata_xml_files.get(i);
225 DebugStream.println("Applicable metadata.xml file: " + metadata_xml_file);
226
227 ArrayList metadata_values = metadata_xml_file.getMetadataAssignedToFile(file);
228 for (int j = 0; j < metadata_values.size(); j++) {
229 MetadataValue metadata_value = (MetadataValue) metadata_values.get(j);
230
231 // Overriding metadata: remove any values with this metadata element
232 if (metadata_value.isAccumulatingMetadata() == false) {
233 for (int k = metadata_values_all.size() - 1; k >= 0; k--) {
234 if (((MetadataValue) metadata_values_all.get(k)).getMetadataElement().equals(metadata_value.getMetadataElement())) {
235 metadata_values_all.remove(k);
236 }
237 }
238 }
239
240 metadata_values_all.add(metadata_value);
241 }
242 }
243
244 return metadata_values_all;
245 }
246
247
248 static public void loadMetadataXMLFiles(File directory, boolean skimfile)
249 {
250 // Make sure the directory (import) exists
251 if (directory.exists() == false) {
252 return;
253 }
254
255 // Look recursively at each subfile of the directory for metadata.xml files
256 File[] directory_files = directory.listFiles();
257 for (int i = 0; i < directory_files.length; i++) {
258 File child_file = directory_files[i];
259 if (child_file.isDirectory()) {
260 loadMetadataXMLFiles(child_file,skimfile);
261 }
262 else if (child_file.getName().equals("metadata.xml")) {
263 loadMetadataXMLFile(child_file,skimfile);
264 }
265 }
266 }
267
268
269 static private MetadataXMLFile loadMetadataXMLFile(File metadata_xml_file_file, boolean skimfile)
270 {
271 MetadataXMLFile metadata_xml_file = new MetadataXMLFile(metadata_xml_file_file.getAbsolutePath());
272 if (metadata_xml_files.contains(metadata_xml_file)) {
273 // This metadata.xml file has already been loaded, so return the loaded object
274 return (MetadataXMLFile) metadata_xml_files.get(metadata_xml_files.indexOf(metadata_xml_file));
275 }
276 if(skimfile){
277 metadata_xml_file.skimFile();
278 }
279 metadata_xml_files.add(metadata_xml_file);
280 return metadata_xml_file;
281 }
282
283
284 static public void removeMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
285 {
286 removeMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
287 }
288
289
290 static public void removeMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
291 {
292 ArrayList metadata_values = new ArrayList();
293 metadata_values.add(metadata_value);
294 removeMetadata(file_nodes, metadata_values);
295 }
296
297
298 static public void removeMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
299 {
300 // Check the list of metadata values is non-empty
301 if (metadata_values.isEmpty()) {
302 return;
303 }
304
305 // Remove the metadata from each file node in turn
306 for (int i = 0; i < file_nodes.length; i++) {
307 File current_file = file_nodes[i].getFile();
308 DebugStream.println("Removing metadata from " + current_file.getAbsolutePath());
309
310 // Find which metadata.xml file needs editing
311 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
312 String current_file_directory_path = current_file_directory.getAbsolutePath();
313 for (int j = 0; j < metadata_xml_files.size(); j++) {
314 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
315
316 // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
317 if (current_file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath())) {
318 metadata_xml_file.removeMetadata(current_file, metadata_values);
319 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
320 modified_metadata_xml_files.add(metadata_xml_file);
321 }
322 }
323 }
324 }
325
326 // Let any listeners know that the metadata has changed
327 fireMetadataChangedEvent(file_nodes);
328 }
329
330
331 static public void removeMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
332 {
333 metadata_changed_listeners.remove(metadata_changed_listener);
334 }
335
336
337 static public void replaceMetadata(CollectionTreeNode[] file_nodes, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
338 {
339 // Replace the metadata in each file node in turn
340 for (int i = 0; i < file_nodes.length; i++) {
341 File current_file = file_nodes[i].getFile();
342 DebugStream.println("Replacing metadata in " + current_file.getAbsolutePath());
343
344 // Find which metadata.xml file needs editing
345 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
346 String current_file_directory_path = current_file_directory.getAbsolutePath();
347 for (int j = 0; j < metadata_xml_files.size(); j++) {
348 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
349
350 // This metadata.xml file is only applicable if it is at the same level as the file
351 if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
352 metadata_xml_file.replaceMetadata(current_file, old_metadata_value, new_metadata_value);
353 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
354 modified_metadata_xml_files.add(metadata_xml_file);
355 }
356 }
357 }
358 }
359
360 // Let any listeners know that the metadata has changed
361 fireMetadataChangedEvent(file_nodes);
362 }
363
364
365 /** Ensures that all the metadata is written to metadata.xml files. */
366 static public void saveMetadataXMLFiles()
367 {
368 // Save the file currently loaded into memory out to disk
369 MetadataXMLFile.saveLoadedFile();
370
371 // If the collection is stored on a remote server, upload all the modified files now
372 if (Gatherer.isGsdlRemote) {
373 if (modified_metadata_xml_files.isEmpty()) {
374 DebugStream.println("No modified metadata.xml files to upload.");
375 return;
376 }
377
378 // Upload the files modified since last time, then reset the list
379 Gatherer.remoteGreenstoneServer.uploadCollectionFiles(
380 CollectionManager.getLoadedCollectionName(), (File[]) modified_metadata_xml_files.toArray(new File[0]));
381 modified_metadata_xml_files.clear();
382 }
383 }
384
385
386 static public void unloadMetadataXMLFile(File metadata_xml_file_file)
387 {
388 DebugStream.println("Unloading metadata.xml file " + metadata_xml_file_file);
389
390 // Find the metadata.xml file in the list of loaded files, and remove it
391 for (int i = 0; i < metadata_xml_files.size(); i++) {
392 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
393 if (metadata_xml_file_file.getAbsolutePath().equals(metadata_xml_file.getAbsolutePath())) {
394 metadata_xml_files.remove(i);
395 break;
396 }
397 }
398 }
399
400
401 /**
402 * Comparator to order MetadataXMLFiles in ascending order from
403 * those in a higher level folder to those in a lower level folder
404 * It is based on the assumption that all MetadataXMLFiles sent to
405 * it to compare will be linear descendants of one toplevel folder
406 * E.g. /A/metadata.xml, /A/B/metadata.xml, /A/B/C/D/metadata.xml.
407 * In other words, that each is a substring of one other until we
408 * get to the toplevel folder.
409 */
410 private static class MetadataXMLFileComparator implements Comparator {
411
412 public int compare(Object o1, Object o2) {
413 if(!(o1 instanceof MetadataXMLFile)) {
414 return -1;
415 } else if (!(o2 instanceof MetadataXMLFile)) {
416 return 1;
417 }
418
419 // Both are MetadataXMLFiles objects. Remove the terminating
420 // "metadata.xml" from their filenames to get their containing folder
421 String filename1 = ((MetadataXMLFile)o1).getParentFile().getAbsolutePath();
422 String filename2 = ((MetadataXMLFile)o2).getParentFile().getAbsolutePath();
423
424 // if 1 is a prefix 2, then 1 < 2 in the ordering (1 comes before 2)
425 if(filename2.startsWith(filename1)) {
426 return -1;
427 } else if(filename1.startsWith(filename2)) {
428 return 1;
429 } else {
430 // unlikely that the metadata.xml files will be the same
431 // or that neither is a prefix of the other
432 return filename1.compareTo(filename2); // sorts in ascending order
433 }
434
435 }
436
437 public boolean equals(Object obj) {
438 if(!(obj instanceof MetadataXMLFileComparator)) {
439 return false;
440 }
441
442 // else it is the same sort of comparator
443 return true;
444 }
445
446 }
447
448}
Note: See TracBrowser for help on using the repository browser.