001: /*
002: * soapUI, copyright (C) 2004-2007 eviware.com
003: *
004: * soapUI is free software; you can redistribute it and/or modify it under the
005: * terms of version 2.1 of the GNU Lesser General Public License as published by
006: * the Free Software Foundation.
007: *
008: * soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009: * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
010: * See the GNU Lesser General Public License for more details at gnu.org.
011: */
012:
013: package com.eviware.soapui.support.components;
014:
015: import java.awt.BorderLayout;
016: import java.awt.Color;
017: import java.awt.Component;
018: import java.awt.Font;
019: import java.awt.Toolkit;
020: import java.awt.datatransfer.StringSelection;
021: import java.awt.event.ActionEvent;
022: import java.beans.PropertyChangeEvent;
023: import java.beans.PropertyChangeListener;
024: import java.lang.reflect.InvocationTargetException;
025: import java.util.ArrayList;
026: import java.util.List;
027:
028: import javax.swing.AbstractAction;
029: import javax.swing.Action;
030: import javax.swing.BorderFactory;
031: import javax.swing.DefaultCellEditor;
032: import javax.swing.JComboBox;
033: import javax.swing.JComponent;
034: import javax.swing.JPanel;
035: import javax.swing.JScrollPane;
036: import javax.swing.JTable;
037: import javax.swing.JTextField;
038: import javax.swing.TransferHandler;
039: import javax.swing.border.TitledBorder;
040: import javax.swing.table.AbstractTableModel;
041: import javax.swing.table.DefaultTableCellRenderer;
042: import javax.swing.table.TableCellEditor;
043: import javax.swing.table.TableModel;
044:
045: import org.apache.commons.beanutils.BeanUtils;
046: import org.apache.commons.beanutils.PropertyUtils;
047:
048: import com.eviware.soapui.SoapUI;
049: import com.eviware.soapui.support.PropertyChangeNotifier;
050:
051: /**
052: * Table for displaying property name/value pairs
053: *
054: * @author Ole.Matzura
055: */
056:
057: public class JPropertiesTable<T> extends JPanel {
058: public final static Object[] BOOLEAN_OPTIONS = new Object[] {
059: Boolean.TRUE, Boolean.FALSE };
060:
061: private PropertiesTableModel<T> tableModel;
062: private JTable table;
063:
064: public JPropertiesTable(String title) {
065: this (title, null);
066: }
067:
068: public JPropertiesTable(String title, T propertyObject) {
069: super (new BorderLayout());
070:
071: tableModel = new PropertiesTableModel<T>(propertyObject);
072: table = new PTable(tableModel);
073:
074: table.getColumnModel().getColumn(0).setHeaderValue("Property");
075: table.getColumnModel().getColumn(1).setHeaderValue("Value");
076: table.getColumnModel().getColumn(0).setCellRenderer(
077: new PropertiesTableCellRenderer());
078: table.getColumnModel().getColumn(1).setCellRenderer(
079: new PropertiesTableCellRenderer());
080:
081: add(new JScrollPane(table), BorderLayout.CENTER);
082: if (title != null) {
083: TitledBorder titledBorder = BorderFactory
084: .createTitledBorder(BorderFactory
085: .createEmptyBorder(), title);
086: titledBorder.setTitleFont(titledBorder.getTitleFont()
087: .deriveFont(Font.PLAIN, 11));
088: setBorder(titledBorder);
089: }
090:
091: table.setBackground(Color.WHITE);
092: setPreferredSize(table.getPreferredSize());
093: }
094:
095: @Override
096: public void removeNotify() {
097: getTableModel().release();
098: super .removeNotify();
099: }
100:
101: @Override
102: public void addNotify() {
103: getTableModel().attach();
104: super .addNotify();
105: }
106:
107: public void setPropertyObject(T propertyObject) {
108: if (table.isEditing())
109: table.getCellEditor().stopCellEditing();
110:
111: tableModel.setPropertyObject(propertyObject);
112: }
113:
114: public PropertiesTableModel getTableModel() {
115: return tableModel;
116: }
117:
118: public PropertyDescriptor addProperty(String caption, String name) {
119: return addProperty(caption, name, false);
120: }
121:
122: public PropertyDescriptor addProperty(String caption, String name,
123: boolean editable) {
124: return addProperty(caption, name, editable, null);
125: }
126:
127: public PropertyDescriptor addProperty(String caption, String name,
128: boolean editable, PropertyFormatter formatter) {
129: return tableModel.addProperty(caption, name, editable,
130: formatter);
131: }
132:
133: public static final class PropertiesTableModel<T> extends
134: AbstractTableModel implements PropertyChangeListener {
135: private List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
136: private T propertyObject;
137: private boolean attached;
138:
139: public PropertiesTableModel(T propertyObject) {
140: this .propertyObject = propertyObject;
141: }
142:
143: public void attach() {
144: if (!attached
145: && propertyObject instanceof PropertyChangeNotifier) {
146: ((PropertyChangeNotifier) propertyObject)
147: .addPropertyChangeListener(this );
148: attached = true;
149: }
150: }
151:
152: public void setPropertyObject(T propertyObject) {
153: release();
154: this .propertyObject = propertyObject;
155: attach();
156: fireTableDataChanged();
157: }
158:
159: public PropertyDescriptor addProperty(String caption,
160: String name, boolean editable,
161: PropertyFormatter formatter) {
162: PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
163: caption, name, editable, formatter);
164: properties.add(propertyDescriptor);
165: return propertyDescriptor;
166: }
167:
168: public PropertyDescriptor addProperty(String caption,
169: String name, Object[] options) {
170: PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
171: caption, name, options);
172: properties.add(propertyDescriptor);
173: return propertyDescriptor;
174: }
175:
176: public int getRowCount() {
177: return properties.size();
178: }
179:
180: public int getColumnCount() {
181: return 2;
182: }
183:
184: public boolean isCellEditable(int rowIndex, int columnIndex) {
185: if (columnIndex == 0 || propertyObject == null)
186: return false;
187: return properties.get(rowIndex).isEditable()
188: && PropertyUtils.isWriteable(propertyObject,
189: properties.get(rowIndex).getName());
190: }
191:
192: public void setValueAt(Object aValue, int rowIndex,
193: int columnIndex) {
194: try {
195: if (propertyObject != null && columnIndex == 1
196: && properties.get(rowIndex).isEditable()) {
197: BeanUtils.setProperty(propertyObject, properties
198: .get(rowIndex).getName(), aValue);
199: }
200: } catch (IllegalAccessException e) {
201: SoapUI.logError(e);
202: } catch (InvocationTargetException e) {
203: SoapUI.logError(e);
204: }
205: }
206:
207: public Object getValueAt(int rowIndex, int columnIndex) {
208: if (propertyObject == null)
209: return null;
210:
211: try {
212: PropertyDescriptor propertyDescriptor = properties
213: .get(rowIndex);
214: switch (columnIndex) {
215: case 0:
216: return propertyDescriptor.getCaption();
217: case 1: {
218: Object value = PropertyUtils.getSimpleProperty(
219: propertyObject, propertyDescriptor
220: .getName());
221: return propertyDescriptor.getFormatter().format(
222: propertyDescriptor.getName(), value);
223: }
224: }
225: } catch (IllegalAccessException e) {
226: SoapUI.logError(e);
227: } catch (InvocationTargetException e) {
228: SoapUI.logError(e);
229: } catch (NoSuchMethodException e) {
230: SoapUI.logError(e);
231: }
232:
233: return null;
234: }
235:
236: public PropertyDescriptor getPropertyDescriptorAt(int row) {
237: return properties.get(row);
238: }
239:
240: public void propertyChange(PropertyChangeEvent evt) {
241: fireTableDataChanged();
242: }
243:
244: public void release() {
245: if (propertyObject instanceof PropertyChangeNotifier
246: && attached) {
247: ((PropertyChangeNotifier) propertyObject)
248: .removePropertyChangeListener(this );
249: attached = false;
250: }
251: }
252: }
253:
254: public static class PropertyDescriptor {
255: private final String caption;
256: private final String name;
257: private boolean editable;
258: private PropertyFormatter formatter;
259: private Object[] options;
260: private DefaultCellEditor cellEditor;
261:
262: public PropertyDescriptor(String caption, String name,
263: boolean editable, PropertyFormatter formatter) {
264: this .caption = caption;
265: this .name = name;
266: this .editable = editable;
267: this .formatter = formatter;
268:
269: JTextField textField = new JTextField();
270: textField.setBorder(BorderFactory.createEmptyBorder());
271: cellEditor = new DefaultCellEditor(textField);
272: }
273:
274: public PropertyDescriptor(String caption, String name,
275: Object[] options) {
276: this .caption = caption;
277: this .name = name;
278: this .options = options;
279: editable = true;
280:
281: JComboBox comboBox = new JComboBox(options);
282:
283: if (options[0] == null) {
284: comboBox.setEditable(true);
285: comboBox.removeItemAt(0);
286: }
287:
288: comboBox.setBorder(null);
289: cellEditor = new DefaultCellEditor(comboBox);
290: }
291:
292: public void setFormatter(PropertyFormatter formatter) {
293: this .formatter = formatter;
294: }
295:
296: public PropertyFormatter getFormatter() {
297: return formatter == null ? DefaultFormatter.getInstance()
298: : formatter;
299: }
300:
301: public String getCaption() {
302: return caption;
303: }
304:
305: public boolean isEditable() {
306: return editable;
307: }
308:
309: public Object[] getOptions() {
310: return options;
311: }
312:
313: public boolean hasOptions() {
314: return options != null;
315: }
316:
317: public String getName() {
318: return name;
319: }
320:
321: public TableCellEditor getCellEditor() {
322: return cellEditor;
323: }
324: }
325:
326: private static class PropertiesTableCellRenderer extends
327: DefaultTableCellRenderer {
328: public Component getTableCellRendererComponent(JTable table,
329: Object value, boolean isSelected, boolean hasFocus,
330: int row, int column) {
331: Component component = super .getTableCellRendererComponent(
332: table, value, isSelected, hasFocus, row, column);
333:
334: if (component instanceof JComponent) {
335: if (value != null
336: && value.toString().trim().length() > 0) {
337: ((JComponent) component).setToolTipText(value
338: .toString());
339: } else {
340: ((JComponent) component).setToolTipText(null);
341: }
342: }
343:
344: return component;
345: }
346: }
347:
348: /*
349: private class PropertiesTableCellEditor extends AbstractCellEditor implements TableCellEditor
350: {
351: private JTextField textField;
352: private JComboBox comboBox;
353: private JComponent current;
354:
355: public PropertiesTableCellEditor()
356: {
357: textField = new JTextField();
358: comboBox = new JComboBox();
359: comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
360: }
361:
362: public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
363: {
364: PropertyDescriptor descriptor = tableModel.getPropertyDescriptorAt( row );
365:
366: if( descriptor.hasOptions())
367: {
368: comboBox.setModel( new DefaultComboBoxModel( descriptor.getOptions() ));
369: comboBox.setSelectedItem( value );
370: current = comboBox;
371: }
372: else
373: {
374: textField.setText( value == null ? "" : value.toString() );
375: current = textField;
376: }
377:
378: current.setBorder( null );
379: current.setBackground( Color.WHITE );
380:
381: return current;
382: }
383:
384: public Object getCellEditorValue()
385: {
386: return current == comboBox ? comboBox.getSelectedItem() : textField.getText();
387: }
388: }
389: */
390:
391: /**
392: * Formatter used for displaying property values
393: *
394: * @author Ole.Matzura
395: */
396:
397: public interface PropertyFormatter {
398: public Object format(String propertyName, Object value);
399: }
400:
401: private static class DefaultFormatter implements PropertyFormatter {
402: private static PropertyFormatter instance;
403:
404: public static PropertyFormatter getInstance() {
405: if (instance == null)
406: instance = new DefaultFormatter();
407:
408: return instance;
409: }
410:
411: public Object format(String propertyName, Object value) {
412: return value;
413: }
414: }
415:
416: public void addProperty(String caption, String name,
417: Object[] options) {
418: tableModel.addProperty(caption, name, options);
419: }
420:
421: private class PTable extends JTable {
422: public PTable(TableModel tableModel) {
423: super (tableModel);
424:
425: // setAutoStartEditOnKeyStroke( true );
426:
427: getActionMap().put(
428: TransferHandler.getCopyAction().getValue(
429: Action.NAME), new AbstractAction() {
430: public void actionPerformed(ActionEvent e) {
431: int row = getSelectedRow();
432: if (row == -1)
433: return;
434:
435: StringSelection selection = new StringSelection(
436: getValueAt(row, 1).toString());
437: Toolkit.getDefaultToolkit()
438: .getSystemClipboard().setContents(
439: selection, selection);
440: }
441: });
442:
443: putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
444: /*
445: addFocusListener( new FocusAdapter() {
446:
447: public void focusLost(FocusEvent e)
448: {
449: if( isEditing() && getCellEditor() != null )
450: getCellEditor().stopCellEditing();
451: }} );*/
452: }
453:
454: public TableCellEditor getCellEditor(int row, int column) {
455: if (column == 0)
456: return super.getCellEditor(row, column);
457: else
458: return tableModel.getPropertyDescriptorAt(row)
459: .getCellEditor();
460: }
461: }
462: }
|