source: trunk/gli/src/org/greenstone/gatherer/gui/table/GTableModel.java@ 4627

Last change on this file since 4627 was 4627, checked in by mdewsnip, 21 years ago

Changed new model builder so it ignored hidden metadata, and removed some debug comments from the old builder.

  • Property svn:keywords set to Author Date Id Revision
File size: 23.1 KB
Line 
1package org.greenstone.gatherer.gui.table;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * Author: John Thompson, Greenstone Digital Library, University of Waikato
10 *
11 * Copyright (C) 1999 New Zealand Digital Library Project
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 *########################################################################
27 */
28import java.awt.*;
29import java.io.File;
30import java.util.*;
31import javax.swing.*;
32import javax.swing.table.*;
33import org.greenstone.gatherer.Gatherer;
34import org.greenstone.gatherer.file.FileNode;
35import org.greenstone.gatherer.gui.WarningDialog;
36import org.greenstone.gatherer.msm.ElementWrapper;
37import org.greenstone.gatherer.msm.Metadata;
38import org.greenstone.gatherer.msm.MSMAction;
39import org.greenstone.gatherer.msm.MSMEvent;
40import org.greenstone.gatherer.msm.MSMListener;
41import org.greenstone.gatherer.msm.MSMUtils;
42import org.greenstone.gatherer.util.ArrayTools;
43/** Provides the model for a GTable component, filling it with metadata values for the choosen files or folders. The model also provides several different view of this data; assigned folder metadata, assigned file metadata, all assigned metadata, unassigned metadata, and all metadata. It also differentiates between metadata that is common to all of the files or folders, and that which isn't. The building of the actual model is done on a separate thread so that the gui remains responsive, and the gui is intermitantly updated by this thread. Updating of the model is triggered by events recieved from the metadata set manager in terms of new or obsolete metadata. A new model is rebuilt whenever the user selects a different group of files or folders.
44 * @author John Thompson, Greenstone Digital Library, University of Waikato
45 * @version 2.3b
46 */
47public class GTableModel
48 extends AbstractTableModel
49 implements MSMListener {
50 /** The source for the data currently shown in the table. May be any of the other three metadata lists, or the union of all of them. */
51 private ArrayList current_metadata = new ArrayList();
52 /** The metadata currently assigned at the file level. */
53 private ArrayList file_metadata = null; // new ArrayList();
54 /** The metadata currently assigned at the folder level. */
55 private ArrayList inherited_metadata = null; // new ArrayList();
56 /** The metadata currently unassigned. */
57 private ArrayList unassigned_metadata = null; // new ArrayList();
58 /** The file nodes this model is built upon. */
59 private FileNode[] file_nodes;
60 /** The current view. */
61 //private int current_view;
62
63 //private JToggleButton assigned_metadata_view;
64 //private JToggleButton unassigned_metadata_view;
65
66 /** An icon showing the current state of the table build. */
67 //private JProgressBar activity_bar;
68 /** The table this model is being displayed in. */
69 private JTable table;
70
71 private ModelBuilder builder;
72
73 static final public int SHOW_ALL = 0;
74 static final public int SHOW_ASSIGNED = 1;
75 static final public int SHOW_FILE = 2;
76 static final public int SHOW_INHERITED = 3;
77 static final public int SHOW_UNASSIGNED = 4;
78 static final private String[] COLUMN_NAMES = {"", Gatherer.dictionary.get("Metadata.Element"), Gatherer.dictionary.get("Metadata.Value")};
79
80 public GTableModel(JTable table) {
81 this.builder = null;
82 this.file_metadata = current_metadata;
83 this.inherited_metadata = current_metadata;
84 this.table = table;
85 this.unassigned_metadata = current_metadata;
86 //changeView();
87 // Register this model with the msm manager.
88 if(Gatherer.c_man.ready()) {
89 Gatherer.c_man.getCollection().msm.addMSMListener(this);
90 }
91 }
92
93 public GTableModel(JTable table, FileNode[] file_nodes) {
94 this(table);
95 this.file_nodes = file_nodes;
96 if(file_nodes != null && file_nodes.length > 0) {
97 // Create model builder
98 builder = new ModelBuilder();
99 builder.start();
100 }
101 }
102
103 /** The default constructor creates a new empty model with the current view set to an emtpy 'all metadata'. */
104 /*
105 public GTableModel(JTable table, JToggleButton assigned_metadata_view, JToggleButton unassigned_metadata_view, JProgressBar activity_bar) {
106 this.activity_bar = activity_bar;
107 this.assigned_metadata_view = assigned_metadata_view;
108 this.builder = null;
109 this.table = table;
110 this.unassigned_metadata_view = unassigned_metadata_view;
111 changeView();
112 // Register this model with the msm manager.
113 if(Gatherer.c_man.ready()) {
114 Gatherer.c_man.getCollection().msm.addMSMListener(this);
115 }
116 }
117 */
118
119 /** This constuctor starts by creating an empty model then, using a separate model builder thread, builds a model from the given files. */
120 /*
121 public GTableModel(JTable table, JToggleButton assigned_metadata_view, JToggleButton unassigned_metadata_view, JProgressBar activity_bar, FileNode[] file_nodes) {
122 this(table, assigned_metadata_view, unassigned_metadata_view, activity_bar);
123 this.file_nodes = file_nodes;
124 if(file_nodes != null && file_nodes.length > 0) {
125 // Create model builder
126 builder = new ModelBuilder();
127 builder.start();
128 }
129 }
130 */
131
132 public void changeView() {
133 changeView(SHOW_ALL);
134 }
135
136 /*
137 public void changeView() {
138 boolean show_assigned = assigned_metadata_view.isSelected();
139 boolean show_unassigned = unassigned_metadata_view.isSelected();
140 if(show_assigned && show_unassigned) {
141 changeView(SHOW_ALL);
142 }
143 else if(show_assigned) {
144 changeView(SHOW_ASSIGNED);
145 }
146 else { // Show unassigned
147 changeView(SHOW_UNASSIGNED);
148 }
149 }
150 */
151
152 /** Change the current view by changing the model that the table is currently based on. */
153 public void changeView(int view) {
154 //current_view = view;
155 /*
156 switch(view) {
157 case SHOW_ASSIGNED:
158 ///ystem.err.println("Show assigned");
159 current_metadata = new ArrayList();
160 current_metadata.addAll(file_metadata);
161 current_metadata.addAll(inherited_metadata);
162 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR);
163 break;
164 case SHOW_FILE:
165 ///ystem.err.println("Show file");
166 current_metadata = file_metadata;
167 break;
168 case SHOW_INHERITED:
169 ///ystem.err.println("Show inherited");
170 current_metadata = inherited_metadata;
171 break;
172 case SHOW_UNASSIGNED:
173 ///ystem.err.println("Show unassigned");
174 current_metadata = unassigned_metadata;
175 break;
176 default:
177 // SHOW_ALL
178 System.err.println("Show all");
179 // Create a new array from the three other arrays.
180 current_metadata = new ArrayList();
181 current_metadata.addAll(file_metadata);
182 current_metadata.addAll(inherited_metadata);
183 current_metadata.addAll(unassigned_metadata);
184 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR);
185 }
186 // Inform everyone that the model has changed.
187 fireTableDataChanged();
188 */
189 }
190
191 /** Called whenever an element in a set is added, edited or removed from the metadata set manager. We listen for this just incase the name of one of the elements currently shown changes. After that the table will be up to date, as the element references are otherwise live. */
192 public void elementChanged(MSMEvent event) {
193 MSMAction profile = event.getProfile();
194 if(profile != null) {
195 // Retrieve the elements new name (thats hopefully what will be returned by getValue()).
196 String name = profile.getTarget();
197 int row = 0;
198 for( ; row < current_metadata.size(); row++) {
199 if(getValueAt(row, 0).equals(name)) {
200 fireTableCellUpdated(row, 0);
201 }
202 }
203 name = null;
204 }
205 profile = null;
206 event = null;
207 }
208
209 /** Returns the number of columns in this table. */
210 public int getColumnCount() {
211 return COLUMN_NAMES.length;
212 }
213
214 /** Returns the data class of the specified column. */
215 public Class getColumnClass(int col) {
216 if(col == 2) {
217 return JButton.class;
218 }
219 return String.class;
220 }
221
222 /** Retrieves the name of the specified column. */
223 public String getColumnName(int col) {
224 return COLUMN_NAMES[col];
225 }
226
227 /* Called to retrieve the Metadata record that matches a certain row. Usually caused by the user selectiong a row in the table. It synchronized so that the model doesn't up and change while we're trying to retrieve the indicated element. */
228 public synchronized Metadata getMetadataAtRow(int row) {
229 if(0 <= row && row < current_metadata.size()) {
230 return (Metadata) current_metadata.get(row);
231 }
232 return null;
233 }
234
235 /** Returns the number of rows in this table. */
236 public int getRowCount() {
237 return current_metadata.size();
238 }
239
240 /** Returns the cell value at a given row and column as an Object. */
241 public Object getValueAt(int row, int col) {
242 Object result = "";
243 if(0 <= row && row < current_metadata.size()) {
244 Metadata m = (Metadata) current_metadata.get(row);
245 if(0 <= col && col < COLUMN_NAMES.length) {
246 switch(col) {
247 case 0:
248 if(!m.isFileLevel()) {
249 result = m.getFile();
250 }
251 break;
252 case 1:
253 ElementWrapper element = m.getElement();
254 if(element != null) {
255 result = element.toString();
256 }
257 break;
258 case 2:
259 result = m.getValue();
260 break;
261 case 3:
262 result = (m.isFileLevel() ? "true" : "false");
263 break;
264 }
265 }
266 }
267 return result;
268 }
269
270 /** Allows access to this models current view. */
271 public int getView() {
272 return SHOW_ALL; //current_view;
273 }
274
275 /** Determine if the given metadata is common to all selected file nodes given the context of the current view. */
276 public boolean isCommon(int row) {
277 if(0 <= row && row < current_metadata.size()) {
278 Metadata entry = (Metadata) current_metadata.get(row);
279 // System.err.println("Of the " + file_nodes.length + " selected files, " + entry + " is attached to " + entry.getCount() + " of them.");
280 // System.err.println("There are " + file_nodes.length + " selected files.");
281 // System.err.println("This metadata is attached to " + entry.getCount() + " of them.");
282 if(entry.getCount() == file_nodes.length) {
283 return true;
284 }
285 }
286 return false;
287 }
288
289 /** Whenever there is a metadata change we must determine if there are any changes to our various lists of metadata, and also if there is any need to refresh the table currently being viewed. */
290 public void metadataChanged(MSMEvent event) {
291 FileNode file_node = event.getRecord();
292 ///ystem.err.println("File node effected");
293 // First check whether this record is one of those that we have showing.
294 if(file_nodes != null && file_node != null && ArrayTools.contains(file_nodes, file_node)) {
295 // Brute force solution... rebuild the table.
296 if(file_nodes != null && file_nodes.length > 0) {
297 current_metadata.clear();
298 // Create model builder
299 builder = new ModelBuilder();
300 builder.start();
301 }
302 }
303 }
304
305 /** Called whenever a set is added or removed from the metadata set manager. */
306 public void setChanged(MSMEvent event) {
307 // No, don't care. Still don't care. Couldn't care less.
308 }
309
310 public Rectangle setSelectedMetadata(Metadata data) {
311 Rectangle bounds = null;
312 if (builder != null) {
313 if(builder.complete) {
314 for(int i = 0; i < getRowCount(); i++) {
315 if(getMetadataAtRow(i).equals(data)) {
316 // System.err.println("Found matching metadata at row " + i + " (of " + getRowCount() + " rows)");
317 // bounds = table.getCellRect(i, 0, true);
318 // table.scrollRectToVisible(bounds);
319 SelectionTask task = new SelectionTask(i);
320 SwingUtilities.invokeLater(task);
321 break;
322 }
323 }
324 }
325 else {
326 builder.selected_metadata = data;
327 }
328 }
329 return bounds;
330 }
331
332 private class SelectionTask
333 extends Thread {
334 private int selected_row;
335 SelectionTask(int selected_row) {
336 this.selected_row = selected_row;
337 }
338 public void run() {
339 Rectangle bounds = table.getCellRect(selected_row, 0, true);
340 table.scrollRectToVisible(bounds);
341 table.setRowSelectionInterval(selected_row, selected_row);
342 ///ystem.err.println("Done setRowSelectionInterval...");
343 }
344 }
345
346 public int size() {
347 return file_nodes.length;
348 }
349
350 /** Called when the value tree of a certain element has changed significantly but, although we display metadata values, we only care about those coming through metadataChanged() events. */
351 public void valueChanged(MSMEvent event) {
352 // Don't care. Tra-la-la-la-la.
353 }
354
355 /** Alphabetically inserts the new_metadata in the target vector. */
356 private int add(ArrayList target, Metadata new_metadata) {
357 int i = 0;
358 while(i < target.size()) {
359 Metadata current = (Metadata)target.get(i);
360 if(current.compareTo(new_metadata) > 0) {
361 target.add(i, new_metadata);
362 return i;
363 }
364 i++;
365 }
366 target.add(new_metadata);
367 return i;
368 }
369
370 /** Remove a certain piece of data from a certain vector. */
371 private void remove(ArrayList target, Metadata old_metadata) {
372 if(target != null) {
373 for(int i = 0; i < target.size(); i++) {
374 Metadata current_metadata = (Metadata) target.get(i);
375 if(current_metadata.equals(old_metadata)) {
376 target.remove(current_metadata);
377 return;
378 }
379 }
380 }
381 }
382
383
384 private class ModelBuilder
385 extends Thread {
386
387 public boolean complete = false;
388 public Metadata selected_metadata = null;;
389 private boolean had_warning = false;
390
391 public void run()
392 {
393 if (!Gatherer.c_man.ready()) {
394 System.err.println("Not ready!");
395 return;
396 }
397
398 // System.err.println("Building metadata model...");
399 Vector elements = Gatherer.c_man.getCollection().msm.getElements();
400 ArrayList known_elements = new ArrayList();
401 current_metadata.clear();
402
403 for (int i = 0; i < file_nodes.length; i++) {
404 File current_file = file_nodes[i].getFile();
405 // System.err.println("File: " + current_file);
406
407 // Get the currently assigned metadata for this file
408 ArrayList metadata = Gatherer.c_man.getCollection().gdm.getAllMetadata(current_file);
409 for (int j = 0; j < metadata.size(); j++) {
410 Metadata metadatum = (Metadata) metadata.get(j);
411 ElementWrapper element = metadatum.getElement();
412 // System.err.println(" Metadatum: " + metadatum + " (" + metadatum.getCount() + ")");
413
414 // Ignore hidden metadata
415 if (!element.toString().startsWith("hidden.")) {
416 // If a piece of metadata is at folder level, display the warning
417 if (!metadatum.isFileLevel()) {
418 showInheritedMetadataWarning();
419 }
420
421 // If the piece of metadata is in the list, increment its count
422 int index = current_metadata.indexOf(metadatum);
423 if (index >= 0) {
424 Metadata existing = (Metadata) current_metadata.get(index);
425 existing.inc();
426 }
427 // Otherwise add it to the list
428 else {
429 metadatum.setCount(1);
430 index = add(current_metadata, metadatum);
431 fireTableRowsInserted(index, index);
432
433 // Remember we have seen this element
434 if (known_elements.contains(element) == false) {
435 known_elements.add(element);
436 }
437 }
438 }
439 }
440 }
441
442 // Make sure each element has a piece of metadata (even if blank)
443 for (int i = 0; i < elements.size(); i++) {
444 ElementWrapper element = (ElementWrapper) elements.get(i);
445 // System.err.println("Element: " + element);
446
447 // If we haven't seen this metadata element, add it now
448 if (!known_elements.contains(element)) {
449 Metadata metadatum = new Metadata(element);
450 metadatum.setCount(file_nodes.length);
451
452 // Add it to the table
453 int index = add(current_metadata, metadatum);
454 fireTableRowsInserted(index, index);
455 }
456 }
457
458 // Sort the metadata and update the table
459 Gatherer.g_man.metaedit_pane.validateMetadataTable();
460 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR);
461 fireTableDataChanged();
462
463 // Processing complete
464 complete = true;
465
466 // If while processing we've been asked to select a certain metadata, do it now
467 if (selected_metadata != null) {
468 setSelectedMetadata(selected_metadata);
469 }
470 }
471
472
473 private void showInheritedMetadataWarning()
474 {
475 if (!had_warning) {
476 had_warning = true;
477 Runnable task = new Runnable() {
478 public void run() {
479 WarningDialog dialog = new WarningDialog("warning.InheritedMetadata", false);
480 dialog.display();
481 dialog.dispose();
482 dialog = null;
483 }
484 };
485 SwingUtilities.invokeLater(task);
486 }
487 }
488 }
489
490
491 private class OldModelBuilder
492 extends Thread {
493
494 public boolean had_warning = false;
495
496 public boolean complete = false;
497
498 public Metadata selected_metadata = null;;
499
500 public void run() {
501 if(Gatherer.c_man != null && Gatherer.c_man.ready()) {
502 Vector elements = Gatherer.c_man.getCollection().msm.getElements();
503 // Show some visual indication that building is occuring.
504 //assigned_metadata_view.setEnabled(false);
505 //unassigned_metadata_view.setEnabled(false);
506 //activity_bar.setMaximum(file_nodes.length + elements.size());
507 //activity_bar.setValue(0);
508 //activity_bar.setString(Gatherer.dictionary.get("MetaEdit.Building"));
509 //long start = System.currentTimeMillis();
510
511 ArrayList known_elements = new ArrayList(); // Elements that have metadata assigned.
512 for(int i = 0; i < file_nodes.length; i++) {
513 File current_file = file_nodes[i].getFile();
514
515 // The current assigned metadata for this file.
516 ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getAllMetadata(current_file);
517 for(int j = 0; j < metadatum.size(); j++) {
518 Metadata data = (Metadata) metadatum.get(j);
519 // Determine the target list by the metadata level
520 ArrayList modified_metadata;
521 if(data.isFileLevel()) {
522 modified_metadata = file_metadata;
523 }
524 else {
525 showInheritedMetadataWarning();
526 modified_metadata = inherited_metadata;
527 }
528 int index = -1;
529 // Don't show hidden metadata
530 // If the given metadata already exists in our list of modified metadata then increment its commonality count.
531 if((index = modified_metadata.indexOf(data)) != -1) {
532 data = (Metadata) modified_metadata.get(index);
533 ///ystem.err.println("Increasing count:" + element);
534 data.inc();
535 // If the table shown is stale, refresh it.
536 //if((modified_metadata == file_metadata && current_view == SHOW_FILE) || (modified_metadata == inherited_metadata && current_view == SHOW_INHERITED)) {
537 // fireTableRowsUpdated(index, index);
538 //}
539 // We may have to update a compound list
540 //else if(current_view == SHOW_ALL || current_view == SHOW_ASSIGNED) {
541 if((index = current_metadata.indexOf(data)) == -1) {
542 fireTableRowsUpdated(index, index);
543 }
544 //}
545 }
546 // Ensure the metadata's element is in our list of showable elements.
547 else if(contains(elements, data.getElement())) {
548 ///ystem.err.println("Setting count to one: " + element);
549 data.setCount(1);
550 index = add(modified_metadata, data); // Add to assigned metadata.
551 known_elements.add(data.getElement());
552 // If the table shown is stale, refresh it.
553 //if((modified_metadata == file_metadata && current_view == SHOW_FILE) || (modified_metadata == inherited_metadata && current_view == SHOW_INHERITED)) {
554 // fireTableRowsInserted(index, index);
555 //}
556 // We may have to update a compound list
557 //else if(current_view == SHOW_ALL || current_view == SHOW_ASSIGNED) {
558 if((index = current_metadata.indexOf(data)) == -1) {
559 index = add(current_metadata, data);
560 fireTableRowsInserted(index, index);
561 }
562 else {
563 fireTableRowsUpdated(index, index);
564 }
565 //}
566 }
567 else {
568 ///ystem.err.println("No.\n***** Cannot match element so it must be hidden, right? *****");
569 }
570 }
571 //activity_bar.setValue(activity_bar.getValue() + 1);
572 Gatherer.g_man.metaedit_pane.validateMetadataTable();
573 }
574 // Add entries for the currently unassigned metadata. You can determine these as the difference between elements and known_elements.
575 // System.err.println("Number of elements: " + elements.size());
576 // System.err.println("Number of known elements: " + known_elements.size());
577 for(int k = 0; k < elements.size(); k++) {
578 // For each key.
579 ElementWrapper element = (ElementWrapper) elements.get(k);
580 // System.err.println("Element " + k + ": " + element);
581 if(!known_elements.contains(element)) {
582 Metadata data = new Metadata(element);
583 int index = add(unassigned_metadata, data);
584 // Inform everyone that the model has changed, but only if it affects the current view.
585 //if(current_view == SHOW_UNASSIGNED) {
586 // fireTableRowsInserted(index, index);
587 //}
588 //else if(current_view == SHOW_ALL) {
589 if((index = current_metadata.indexOf(data)) == -1) {
590 // System.err.println("Adding " + data + " to " + current_metadata);
591 index = add(current_metadata, data);
592 fireTableRowsInserted(index, index);
593 }
594 else {
595 fireTableRowsUpdated(index, index);
596 }
597 //}
598 }
599 //activity_bar.setValue(activity_bar.getValue() + 1);
600 Gatherer.g_man.metaedit_pane.validateMetadataTable();
601 }
602 //long end = System.currentTimeMillis();
603 ///ystem.err.println("Took " + (end - start) + "ms to build table.");
604 //assigned_metadata_view.setEnabled(true);
605 //unassigned_metadata_view.setEnabled(true);
606 //activity_bar.setValue(activity_bar.getMaximum());
607 //activity_bar.setString(Gatherer.dictionary.get("MetaEdit.Ready"));
608 Collections.sort(current_metadata, MSMUtils.METADATA_COMPARATOR);
609 fireTableDataChanged();
610 }
611 else {
612
613 }
614 // Finally complete (even if we didn't do anything).
615 complete = true;
616 // If in the in between we've been asked to select a certain metadata, we action that now.
617 if(getRowCount() > 0 && selected_metadata != null) {
618 setSelectedMetadata(selected_metadata);
619 }
620 }
621
622 /** For some reason contains doesn't always work. It appear not to use equals() properly, as ElementWrappers need to compare themselves by their string content as other data members are nearly always different even between to ElementWrappers generated from the same DOM Element. */
623 private boolean contains(Vector elements, ElementWrapper element) {
624 if(element != null) {
625 for(int i = 0; i < elements.size(); i++) {
626 if(element.equals(elements.get(i))) {
627 return true;
628 }
629 }
630 }
631 return false;
632 }
633
634 private void showInheritedMetadataWarning() {
635 if(!had_warning) {
636 had_warning = true;
637 Runnable task = new Runnable() {
638 public void run() {
639 WarningDialog dialog = new WarningDialog("warning.InheritedMetadata", false);
640 dialog.display();
641 dialog.dispose();
642 dialog = null;
643 }
644 };
645 SwingUtilities.invokeLater(task);
646 }
647 }
648 }
649}
Note: See TracBrowser for help on using the repository browser.