All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.fedora.client.objecteditor.DatastreamBindingPane Maven / Gradle / Ivy

Go to download

The Fedora Client is a Java Library that allows API access to a Fedora Repository. The client is typically one part of a full Fedora installation.

The newest version!
/*
 * -----------------------------------------------------------------------------
 *
 * 

License and Copyright: The contents of this file are subject to the * Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of * the License at * http://www.fedora-commons.org/licenses.

* *

Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License.

* *

The entire file consists of original code.

*

Copyright © 2008 Fedora Commons, Inc.
*

Copyright © 2002-2007 The Rector and Visitors of the University of * Virginia and Cornell University
* All rights reserved.

* * ----------------------------------------------------------------------------- */ package fedora.client.objecteditor; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultCellEditor; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import fedora.client.Administrator; import fedora.client.objecteditor.types.DatastreamBindingRule; import fedora.client.objecteditor.types.DatastreamInputSpec; import fedora.server.types.gen.DatastreamBinding; /** * Binding pane for datastreams. * */ public class DatastreamBindingPane extends JPanel implements PotentiallyDirty { private static final long serialVersionUID = 1L; // private Datastream[] m_datastreams; private ObjectEditorFrame m_gramps; private DatastreamInputSpec m_spec; private HashMap m_ruleForKey; private HashMap m_perkyPanels; private EditingPane m_owner; private static DatastreamBindingComparator s_dsBindingComparator= new DatastreamBindingComparator(); static ImageIcon notFulfilledIcon=new ImageIcon(Administrator.cl.getResource("images/fedora/exclaim16.gif")); static ImageIcon fulfilledIcon=new ImageIcon(Administrator.cl.getResource("images/fedora/checkmark16.gif")); private JTabbedPane m_bindingTabbedPane; private ValidityListener m_validityListener; public DatastreamBindingPane(ObjectEditorFrame gramps, DatastreamBinding[] initialBindings, String bMechPID, DatastreamInputSpec spec, ValidityListener validityListener, // ok if null EditingPane owner) { // ok if null m_validityListener=validityListener; m_gramps = gramps; m_owner=owner; // m_datastreams=currentVersions; m_spec=spec; m_perkyPanels=new HashMap(); // put rules in a hash by key so they're easy to use later m_ruleForKey=new HashMap(); for (int i=0; i0) { m_table.addRowSelectionInterval(0, 0); } JPanel middlePane=new JPanel(new BorderLayout()); middlePane.add(new JScrollPane(m_table), BorderLayout.CENTER); GridBagLayout gridbag=new GridBagLayout(); JPanel buttonPane=new JPanel(gridbag); buttonPane.setBorder(BorderFactory.createEmptyBorder(0,4,0,0)); GridBagConstraints c=new GridBagConstraints(); c.gridx=0; c.fill=GridBagConstraints.BOTH; c.anchor=GridBagConstraints.NORTH; c.weightx=1.0; c.weighty=0.0; c.insets=new Insets(0,0,3,0); m_addButton=new JButton("Add..."); gridbag.setConstraints(m_addButton, c); Administrator.constrainHeight(m_addButton); buttonPane.add(m_addButton); m_addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { m_cellEditor.stopCellEditing(); // first bring up a dialog with candidates to // choose from. String selected=getCandidateSelection(); if (selected!=null) { // if that succeeds, we need to tell the model // to update itself and fire change events String[] parts=selected.split(" - "); m_tableModel.addRow(parts[0], "Binding to " + (parts.length > 2 ? parts[2] : "")); } } }); if (rule.orderMatters()) { m_insertButton=new JButton("Insert..."); gridbag.setConstraints(m_insertButton, c); Administrator.constrainHeight(m_insertButton); buttonPane.add(m_insertButton); } m_removeButton=new JButton("Remove"); gridbag.setConstraints(m_removeButton, c); Administrator.constrainHeight(m_removeButton); buttonPane.add(m_removeButton); m_removeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { m_cellEditor.stopCellEditing(); // tell the model to update itself and fire change events m_tableModel.removeRow(m_table.getSelectedRow()); } }); if (rule.orderMatters()) { Component strut=Box.createVerticalStrut(6); gridbag.setConstraints(strut, c); buttonPane.add(strut); m_upButton=new JButton("Up"); gridbag.setConstraints(m_upButton, c); Administrator.constrainHeight(m_upButton); buttonPane.add(m_upButton); m_downButton=new JButton("Down"); gridbag.setConstraints(m_downButton, c); Administrator.constrainHeight(m_downButton); buttonPane.add(m_downButton); } c.weighty=1.0; c.fill=GridBagConstraints.VERTICAL; Component glue=Box.createVerticalGlue(); gridbag.setConstraints(glue, c); buttonPane.add(glue); JPanel bottomPane=new JPanel(new BorderLayout()); bottomPane.setBorder(BorderFactory.createEmptyBorder(4,0,0,0)); bottomPane.add(middlePane, BorderLayout.CENTER); bottomPane.add(buttonPane, BorderLayout.EAST); setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(4,4,4,4)); add(m_instructionPane, BorderLayout.NORTH); add(bottomPane, BorderLayout.CENTER); } private String getCandidateSelection() { String[] usedIDs=m_tableModel.getUsedDatastreamIDs(); String[] options=getCandidates(m_rule, usedIDs); if (options.length==0) { // bring up a dialog telling them there are no candidates // to add, so they need to add one to the object String more=""; if (usedIDs.length>0) more=" more"; JOptionPane.showInternalMessageDialog(Administrator.getDesktop(), "There are no" + more + " datastreams of the required type\n" + "for this binding. Add one to the object first.", "No candidates found", JOptionPane.INFORMATION_MESSAGE); return null; } else { StringBuffer instr=new StringBuffer(); instr.append("Choose a datastream:"); if (m_rule.getInputInstruction()!=null && m_rule.getInputInstruction().length()>0) { instr.append("\n("); instr.append(m_rule.getInputInstruction()); instr.append(')'); } return (String) JOptionPane.showInputDialog( Administrator.getDesktop(), instr.toString(), "New Binding", JOptionPane.QUESTION_MESSAGE, null, options, options[0]); } } private void selectRowLater(final int rowNum) { SwingUtilities.invokeLater(new Runnable() { public void run() { m_table.getSelectionModel().setSelectionInterval(rowNum, rowNum); } }); } public void tableChanged(TableModelEvent e) { DatastreamBindingTableModel model=(DatastreamBindingTableModel) e.getSource(); // update the shown selection of the table if (e.getType()==TableModelEvent.INSERT) { selectRowLater(e.getFirstRow()); } else if (e.getType()==TableModelEvent.DELETE) { if (m_tableModel.getRowCount()>0) { if (m_tableModel.getRowCount()==e.getFirstRow()) { // if the row that was deleted was the last one, // select the one just above it selectRowLater(e.getFirstRow()-1); } else { // otherwise, select the one that's now in its place selectRowLater(e.getFirstRow()); } } } // update the dirty flag, then send the event up to datastreambindingpane if (model.isDirty()) { m_dirty=true; } else { m_dirty=false; } fireDataChanged(); // this will indirectly result in a call to // doValidityCheck below } public boolean doValidityCheck() { // check whether the model in its current state is valid // if so or not, update the status text and button visibility // appropriately, then return whether it's valid or not boolean isValid=updateButtonsAndReturnValidity(); // do the appropriate completeness text updates if (isValid && !m_wasValid) { m_instructionPane.setText(m_instructionPane.getText().replaceAll("incomplete.", "complete.")); } else if (!isValid && m_wasValid) { m_instructionPane.setText(m_instructionPane.getText().replaceAll("complete.", "incomplete.")); } // remember this for next time so we don't have to do too much work m_wasValid=isValid; return isValid; } // check if it's valid, update buttons appropriately, and return // if it's valid private boolean updateButtonsAndReturnValidity() { Set bindingSet=getBindings(); boolean couldUseMore=(m_rule.getMax()==-1) || (bindingSet.size()0); } // Remove : always exists; enabled if there's at least one binding m_removeButton.setEnabled(bindingSet.size()>0); // Up and Down : exist if orderMatters; enabled if > 1 binding if (m_rule.orderMatters()) { int row=m_table.getSelectedRow(); // Up : enabled if there's more than one binding and // the current selection is below row 0 m_upButton.setEnabled(bindingSet.size()>1 && row>0); // Down : enabled if there's more than one binding and // the current selection is above the last row m_downButton.setEnabled(bindingSet.size()>1 && row<(bindingSet.size()-1)); } // now for returning the validity. // we already know the types are valid and there aren't too many // datastreams in the binding, because the widget doesn't allow // that. the only thing that can possibly happen that's invalid // is not enough datastreams. return (bindingSet.size()>=m_rule.getMin()); } // get a set of DatastreamBinding objects reflecting the current state // of the model public Set getBindings() { return m_tableModel.getBindings(); } public boolean isDirty() { return m_dirty; } public void undoChanges() { // undo the changes in the underlying table model by asking // the current one for an unmodified copy m_tableModel=m_tableModel.getOriginal(); m_table.setModel(m_tableModel); m_tableModel.addTableModelListener(this); m_table.getColumnModel().getColumn(0).setMinWidth(90); m_table.getColumnModel().getColumn(0).setMaxWidth(90); if (m_table.getRowCount()>0) { m_table.addRowSelectionInterval(0, 0); } m_dirty=false; // then make sure the view is updated fireDataChanged(); } private JEditorPane createInstructionPane(String bMechPID, DatastreamBindingRule rule) { StringBuffer buf=new StringBuffer(); // requires x to y datastreams... buf.append("Binding of "); if (rule.getInputLabel()!=null && rule.getInputLabel().length()>0) { buf.append(rule.getInputLabel()); } else { buf.append(rule.getKey()); } buf.append(" is incomplete. Requires "); if (rule.orderMatters() && (rule.getMax()==-1) || (rule.getMax()>1)) { buf.append("an ordered list of "); } if (rule.getMin()==0) { if (rule.getMax()==-1) { buf.append("any number of datastreams"); } else { buf.append("up to "); if (rule.getMax()==1) { buf.append("one"); } else { buf.append(rule.getMax()); } buf.append(" datastream"); if (rule.getMax()>1) { buf.append('s'); } } } else { buf.append(""); if (rule.getMin()==rule.getMax()) { if (rule.getMin()==1) { buf.append("one"); } else { buf.append(rule.getMin()); } buf.append(" datastream"); if (rule.getMax()>1) { buf.append('s'); } } else { if (rule.getMin()==1) { buf.append("one"); } else { buf.append(rule.getMin()); } if (rule.getMax()==-1) { buf.append(" or more datastreams"); } else { buf.append(" to "); buf.append(rule.getMax()); buf.append(" datastreams"); } } } // of type... String[] types=rule.getTypes(); buf.append(" of "); if (rule.accepts("*/*")) { buf.append("any type."); } else { buf.append("type "); buf.append(""); buf.append(types[0]); buf.append(""); if (types.length==2) { buf.append(" or "); buf.append(""); buf.append(types[1]); buf.append(""); } else if (types.length>2) { for (int i=1; i"); buf.append(types[i]); buf.append(""); } } } // add inputLabel if available if (rule.getInputInstruction()!=null && rule.getInputInstruction().length()>0) { buf.append(" ("); buf.append(rule.getInputInstruction()); buf.append(")"); } else { buf.append("."); } // finally, set up and return the Pane JEditorPane result=new JEditorPane("text/html", buf.toString()); result.setEditable(false); return result; } } /** * A TableCellEditor that bypasses swing's awkward "undo" behavior when * the table loses focus during editing, and sets the table model * appropriately on each editing event inside the textField, * instead of waiting for the textField to lose focus. */ class NonCancelingCellEditor extends DefaultCellEditor { private static final long serialVersionUID = 1L; private int m_row; private int m_column; private TableModel m_model; private JTextField m_textField; NonCancelingCellEditor(JTextField f, TableModel model) { super(f); m_textField=f; m_model=model; f.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { dataChanged(); } public void insertUpdate(DocumentEvent e) { dataChanged(); } public void removeUpdate(DocumentEvent e) { dataChanged(); } public void dataChanged() { // update the model for each change m_model.setValueAt(m_textField.getText(), m_row, m_column); } }); } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { m_row=row; m_column=column; m_textField.setText((String) value); return m_textField; } // Overridden to ignore cancels due to focus loss public void cancelCellEditing() { stopCellEditing(); } } class DatastreamBindingTableModel extends AbstractTableModel implements PotentiallyDirty { private static final long serialVersionUID = 1L; public DatastreamBinding[] m_bindings; public DatastreamBinding[] m_originalBindings; public String m_bindingKey; public DatastreamBindingRule m_rule; public DatastreamBindingTableModel(Set values, String bindingKey, DatastreamBindingRule rule) { m_bindingKey=bindingKey; m_rule=rule; m_bindings=new DatastreamBinding[values.size()]; m_originalBindings=new DatastreamBinding[values.size()]; Iterator iter=values.iterator(); int i=0; while (iter.hasNext()) { DatastreamBinding n=(DatastreamBinding) iter.next(); m_bindings[i]=n; m_originalBindings[i]=new DatastreamBinding(); m_originalBindings[i].setBindKeyName(new String(n.getBindKeyName())); m_originalBindings[i].setBindLabel(new String(n.getBindLabel())); m_originalBindings[i].setDatastreamID(new String(n.getDatastreamID())); m_originalBindings[i].setSeqNo(new String(n.getSeqNo())); i++; } } public String getBindingKey() { return m_bindingKey; } public Set getBindings() { HashSet set=new HashSet(); for (int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy