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

Last change on this file since 13706 was 13706, checked in by mdewsnip, 17 years ago

The second half of speeding up metadata access and editing substantially (particularly when metadata.xml files are large) by caching one metadata.xml file in memory. The loaded metadata.xml file is no longer written to disk every time a piece of metadata is added/edited, but only when a new metadata.xml file is loaded.

  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 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
50 static public void addMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
51 {
52 addMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
53 }
54
55
56 static public void addMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
57 {
58 ArrayList metadata_values = new ArrayList();
59 metadata_values.add(metadata_value);
60 addMetadata(file_nodes, metadata_values);
61 }
62
63
64 static public void addMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
65 {
66 // Check the list of metadata values is non-empty
67 if (metadata_values.isEmpty()) {
68 return;
69 }
70
71 // Add the metadata to each file node in turn
72 for (int i = 0; i < file_nodes.length; i++) {
73 File current_file = file_nodes[i].getFile();
74 DebugStream.println("Adding metadata to " + current_file.getAbsolutePath());
75
76 // Find which metadata.xml file needs editing
77 boolean applicable_metadata_xml_file_found = false;
78 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
79 String current_file_directory_path = current_file_directory.getAbsolutePath();
80 for (int j = 0; j < metadata_xml_files.size(); j++) {
81 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
82
83 // This metadata.xml file is only applicable if it is at the same level as the file
84 if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
85 applicable_metadata_xml_file_found = true;
86 metadata_xml_file.addMetadata(current_file, metadata_values);
87 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
88 modified_metadata_xml_files.add(metadata_xml_file);
89 }
90 }
91 }
92
93 // If no applicable metadata.xml file exists, we have to create a new one
94 if (!applicable_metadata_xml_file_found) {
95 // Create a new (empty) metadata.xml file in the file's directory...
96 File new_metadata_xml_file_file = new File(current_file_directory, "metadata.xml");
97 XMLTools.writeXMLFile(new_metadata_xml_file_file, XMLTools.parseXMLFile("xml/metadata.xml", true));
98
99 // ...load it...
100 MetadataXMLFile new_metadata_xml_file = loadMetadataXMLFile(new_metadata_xml_file_file);
101
102 // ...and add the metadata
103 new_metadata_xml_file.addMetadata(current_file, metadata_values);
104 if (!modified_metadata_xml_files.contains(new_metadata_xml_file)) {
105 modified_metadata_xml_files.add(new_metadata_xml_file);
106 }
107 }
108 }
109
110 // Let any listeners know that the metadata has changed
111 fireMetadataChangedEvent(file_nodes);
112 }
113
114
115 static public void addMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
116 {
117 metadata_changed_listeners.add(metadata_changed_listener);
118 }
119
120
121 static public void clearMetadataXMLFiles()
122 {
123 metadata_xml_files.clear();
124 }
125
126
127 static private void fireMetadataChangedEvent(CollectionTreeNode[] file_nodes)
128 {
129 // Send the event off to all the MetadataChangedListeners
130 for (int i = 0; i < metadata_changed_listeners.size(); i++) {
131 ((MetadataChangedListener) metadata_changed_listeners.get(i)).metadataChanged(file_nodes);
132 }
133 }
134
135
136 /** Returns the metadata assigned to a file outside the collection, excluding folder-level/inherited metadata. */
137 static public ArrayList getMetadataAssignedDirectlyToExternalFile(File file)
138 {
139 DebugStream.println("Getting metadata assigned directly to external file " + file + "...");
140
141 // Build up a list of applicable metadata.xml files
142 ArrayList applicable_metadata_xml_files = new ArrayList();
143
144 File directory = (file.isDirectory() ? file : file.getParentFile());
145 while (directory != null) {
146 File metadata_xml_file = new File(directory, "metadata.xml");
147 if (metadata_xml_file.exists() && !metadata_xml_file.isDirectory()) {
148 // It is very important that shallower files come before deeper ones
149 applicable_metadata_xml_files.add(0, new MetadataXMLFile(metadata_xml_file.getAbsolutePath()));
150 }
151
152 directory = directory.getParentFile();
153 }
154
155 // Get the metadata assigned to the specified file from the applicable metadata.xml files
156 ArrayList assigned_metadata = getMetadataAssignedToFile(file, applicable_metadata_xml_files);
157
158 // Remove any folder-level metadata
159 for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
160 if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
161 assigned_metadata.remove(i);
162 }
163 }
164
165 return assigned_metadata;
166 }
167
168
169 /** Returns the metadata assigned to a file inside the collection, excluding folder-level/inherited metadata. */
170 static public ArrayList getMetadataAssignedDirectlyToFile(File file)
171 {
172 // Get all the metadata assigned to the specified file...
173 ArrayList assigned_metadata = getMetadataAssignedToFile(file);
174
175 // ...then remove any folder-level metadata
176 for (int i = assigned_metadata.size() - 1; i >= 0; i--) {
177 if (((MetadataValue) assigned_metadata.get(i)).isInheritedMetadata()) {
178 assigned_metadata.remove(i);
179 }
180 }
181
182 return assigned_metadata;
183 }
184
185
186 /** Returns all the metadata assigned to a file inside the collection. */
187 static public ArrayList getMetadataAssignedToFile(File file)
188 {
189 // Build up a list of applicable metadata.xml files
190 ArrayList applicable_metadata_xml_files = new ArrayList();
191
192 // Look at each loaded metadata.xml file to see if it is potentially applicable
193 String file_directory_path = (file.isDirectory() ? file : file.getParentFile()).getAbsolutePath() + File.separator;
194 for (int i = 0; i < metadata_xml_files.size(); i++) {
195 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
196
197 // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
198 if (file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath() + File.separator)) {
199 applicable_metadata_xml_files.add(metadata_xml_file);
200 }
201 }
202
203 // Return the metadata assigned to the specified file from the applicable metadata.xml files
204 return getMetadataAssignedToFile(file, applicable_metadata_xml_files);
205 }
206
207
208 static private ArrayList getMetadataAssignedToFile(File file, ArrayList applicable_metadata_xml_files)
209 {
210 // Build up a list of metadata values assigned to this file
211 ArrayList metadata_values_all = new ArrayList();
212
213 // Look at each applicable metadata.xml file to see if it assigns metadata to this file
214 for (int i = 0; i < applicable_metadata_xml_files.size(); i++) {
215 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) applicable_metadata_xml_files.get(i);
216 DebugStream.println("Applicable metadata.xml file: " + metadata_xml_file);
217
218 ArrayList metadata_values = metadata_xml_file.getMetadataAssignedToFile(file);
219 for (int j = 0; j < metadata_values.size(); j++) {
220 MetadataValue metadata_value = (MetadataValue) metadata_values.get(j);
221
222 // Overriding metadata: remove any values with this metadata element
223 if (metadata_value.isAccumulatingMetadata() == false) {
224 for (int k = metadata_values_all.size() - 1; k >= 0; k--) {
225 if (((MetadataValue) metadata_values_all.get(k)).getMetadataElement().equals(metadata_value.getMetadataElement())) {
226 metadata_values_all.remove(k);
227 }
228 }
229 }
230
231 metadata_values_all.add(metadata_value);
232 }
233 }
234
235 return metadata_values_all;
236 }
237
238
239 static public void loadMetadataXMLFiles(File directory)
240 {
241 // Make sure the directory (import) exists
242 if (directory.exists() == false) {
243 return;
244 }
245
246 // Look recursively at each subfile of the directory for metadata.xml files
247 File[] directory_files = directory.listFiles();
248 for (int i = 0; i < directory_files.length; i++) {
249 File child_file = directory_files[i];
250 if (child_file.isDirectory()) {
251 loadMetadataXMLFiles(child_file);
252 }
253 else if (child_file.getName().equals("metadata.xml")) {
254 loadMetadataXMLFile(child_file);
255 }
256 }
257 }
258
259
260 static private MetadataXMLFile loadMetadataXMLFile(File metadata_xml_file_file)
261 {
262 MetadataXMLFile metadata_xml_file = new MetadataXMLFile(metadata_xml_file_file.getAbsolutePath());
263 if (metadata_xml_files.contains(metadata_xml_file)) {
264 // This metadata.xml file has already been loaded, so return the loaded object
265 return (MetadataXMLFile) metadata_xml_files.get(metadata_xml_files.indexOf(metadata_xml_file));
266 }
267
268 metadata_xml_file.skimFile();
269 metadata_xml_files.add(metadata_xml_file);
270 return metadata_xml_file;
271 }
272
273
274 static public void removeMetadata(CollectionTreeNode file_node, ArrayList metadata_values)
275 {
276 removeMetadata(new CollectionTreeNode[] { file_node }, metadata_values);
277 }
278
279
280 static public void removeMetadata(CollectionTreeNode[] file_nodes, MetadataValue metadata_value)
281 {
282 ArrayList metadata_values = new ArrayList();
283 metadata_values.add(metadata_value);
284 removeMetadata(file_nodes, metadata_values);
285 }
286
287
288 static public void removeMetadata(CollectionTreeNode[] file_nodes, ArrayList metadata_values)
289 {
290 // Check the list of metadata values is non-empty
291 if (metadata_values.isEmpty()) {
292 return;
293 }
294
295 // Remove the metadata from each file node in turn
296 for (int i = 0; i < file_nodes.length; i++) {
297 File current_file = file_nodes[i].getFile();
298 DebugStream.println("Removing metadata from " + current_file.getAbsolutePath());
299
300 // Find which metadata.xml file needs editing
301 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
302 String current_file_directory_path = current_file_directory.getAbsolutePath();
303 for (int j = 0; j < metadata_xml_files.size(); j++) {
304 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
305
306 // This metadata.xml file is only potentially applicable if it is above or at the same level as the file
307 if (current_file_directory_path.startsWith(metadata_xml_file.getParentFile().getAbsolutePath())) {
308 metadata_xml_file.removeMetadata(current_file, metadata_values);
309 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
310 modified_metadata_xml_files.add(metadata_xml_file);
311 }
312 }
313 }
314 }
315
316 // Let any listeners know that the metadata has changed
317 fireMetadataChangedEvent(file_nodes);
318 }
319
320
321 static public void removeMetadataChangedListener(MetadataChangedListener metadata_changed_listener)
322 {
323 metadata_changed_listeners.remove(metadata_changed_listener);
324 }
325
326
327 static public void replaceMetadata(CollectionTreeNode[] file_nodes, MetadataValue old_metadata_value, MetadataValue new_metadata_value)
328 {
329 // Replace the metadata in each file node in turn
330 for (int i = 0; i < file_nodes.length; i++) {
331 File current_file = file_nodes[i].getFile();
332 DebugStream.println("Replacing metadata in " + current_file.getAbsolutePath());
333
334 // Find which metadata.xml file needs editing
335 File current_file_directory = (current_file.isDirectory() ? current_file : current_file.getParentFile());
336 String current_file_directory_path = current_file_directory.getAbsolutePath();
337 for (int j = 0; j < metadata_xml_files.size(); j++) {
338 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(j);
339
340 // This metadata.xml file is only applicable if it is at the same level as the file
341 if (current_file_directory_path.equals(metadata_xml_file.getParentFile().getAbsolutePath())) {
342 metadata_xml_file.replaceMetadata(current_file, old_metadata_value, new_metadata_value);
343 if (!modified_metadata_xml_files.contains(metadata_xml_file)) {
344 modified_metadata_xml_files.add(metadata_xml_file);
345 }
346 }
347 }
348 }
349
350 // Let any listeners know that the metadata has changed
351 fireMetadataChangedEvent(file_nodes);
352 }
353
354
355 /** Ensures that all the metadata is written to metadata.xml files. */
356 static public void saveMetadataXMLFiles()
357 {
358 // Save the file currently loaded into memory out to disk
359 MetadataXMLFile.saveLoadedFile();
360
361 // If the collection is stored on a remote server, upload all the modified files now
362 if (Gatherer.isGsdlRemote) {
363 if (modified_metadata_xml_files.isEmpty()) {
364 DebugStream.println("No modified metadata.xml files to upload.");
365 return;
366 }
367
368 // Upload the files modified since last time, then reset the list
369 RemoteGreenstoneServer.uploadCollectionFiles(CollectionManager.getLoadedCollectionName(), (File[]) modified_metadata_xml_files.toArray(new File[0]));
370 modified_metadata_xml_files.clear();
371 }
372 }
373
374
375 static public void unloadMetadataXMLFile(File metadata_xml_file_file)
376 {
377 DebugStream.println("Unloading metadata.xml file " + metadata_xml_file_file);
378
379 // Find the metadata.xml file in the list of loaded files, and remove it
380 for (int i = 0; i < metadata_xml_files.size(); i++) {
381 MetadataXMLFile metadata_xml_file = (MetadataXMLFile) metadata_xml_files.get(i);
382 if (metadata_xml_file_file.getAbsolutePath().equals(metadata_xml_file.getAbsolutePath())) {
383 metadata_xml_files.remove(i);
384 break;
385 }
386 }
387 }
388}
Note: See TracBrowser for help on using the repository browser.