/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.ui.debug; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dialog.ModalityType; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import java.util.WeakHashMap; import javax.swing.Box; import javax.swing.DefaultCellEditor; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import org.andork.awt.GridBagWizard; import org.andork.collect.CollectionUtils; import org.andork.collect.LinkedHashSetMultiMap; import org.andork.collect.LinkedListMultiMap; import org.andork.collect.MultiMap; import org.andork.format.Format; import org.andork.reflect.CompactTypeFormatter; import org.andork.reflect.ReflectionUtils; import org.andork.reflect.TypeFormatter; import org.andork.swing.RendererButtonModel; import org.andork.swing.selector.DefaultSelector; import org.andork.swing.table.CellEditorBoundsOverridingTableUI; import org.andork.swing.table.CheckboxTableCellRenderer; import org.andork.swing.table.TableCellRendererButtonModelContext; import org.andork.swing.table.TableCellRendererRetargeter; import org.andork.swing.table.TableCellRendererWithButtons; import org.andork.ui.debug.ObjectTreeTableModel.ArrayElementStorage; import org.andork.ui.debug.ObjectTreeTableModel.ElementStorage; import org.andork.ui.debug.ObjectTreeTableModel.ListElementStorage; import org.andork.ui.debug.ObjectTreeTableModel.ListlikeAspect; import org.andork.ui.debug.ObjectTreeTableModel.MapEntryStorage; import org.andork.ui.debug.ObjectTreeTableModel.MaplikeAspect; import org.andork.ui.debug.ObjectTreeTableModel.Node; import org.andork.util.ArrayUtils; import org.jdesktop.swingx.JXTreeTable; import org.jdesktop.swingx.table.DatePickerCellEditor; import org.jdesktop.swingx.treetable.TreeTableModel; @SuppressWarnings("serial") public class ObjectTreeTable extends JXTreeTable { protected class ArbitraryInstantiator implements Instantiator<Object> { @Override public String getDescription() { return "Enter class name..."; } @Override public Class<?> getInstantiatingType() { return null; } @Override public Object newInstance(final Node target) throws Exception { String className = ClassChooserDialog.showDialog("Select type to create", ObjectTreeTable.this); if (className != null) { try { Class<?> clazz = Class.forName(className); final List<Instantiator<?>> instantiators = new ArrayList<Instantiator<?>>( ObjectTreeTable.this.instantiators.get(clazz)); Collections.sort(instantiators, new Comparator<Instantiator<?>>() { @Override public int compare(Instantiator<?> first, Instantiator<?> second) { int result = first.getInstantiatingType().getSimpleName().compareTo( second.getInstantiatingType().getSimpleName()); if (result != 0) { return result; } return first.getDescription().compareTo(second.getDescription()); } }); if (instantiators.size() > 1 || !instantiators.isEmpty() && instantiators.get(0).getInstantiatingType() != clazz) { final JDialog dialog = new JDialog(SwingUtilities.getWindowAncestor(ObjectTreeTable.this)); dialog.setTitle("New Instance"); dialog.setModalityType(ModalityType.DOCUMENT_MODAL); JPanel buttonPanel = new JPanel(); buttonPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); dialog.getContentPane().add(buttonPanel, BorderLayout.CENTER); dialog.setResizable(false); GridBagWizard gbw = GridBagWizard.create(buttonPanel); gbw.defaults().fillx(1.0); gbw.put(new JLabel("<html>Select instantiator for " + className + ":</html>")).xy(0, 0); gbw.put(Box.createVerticalStrut(10)).belowLast(); final JButton[] buttons = new JButton[instantiators.size()]; int i = 0; for (Instantiator<?> instantiator : instantiators) { buttons[i] = new JButton(instantiator.getDescription()); gbw.put(buttons[i]).belowLast(); i++; } gbw.put(Box.createVerticalStrut(10)).belowLast(); final JButton cancelButton = new JButton("Cancel"); gbw.put(cancelButton).belowLast(); class SelectionHandler implements ActionListener { Object newInstance; @Override public void actionPerformed(ActionEvent e) { dialog.setVisible(false); if (e.getSource() != cancelButton) { int index = ArrayUtils.strictIndexOf(buttons, e.getSource()); Instantiator<?> instantiator = instantiators.get(index); try { newInstance = instantiator.newInstance(target); } catch (Exception e1) { handleInstantiationExecption(e1); } } } } ; SelectionHandler selectionHandler = new SelectionHandler(); cancelButton.addActionListener(selectionHandler); for (JButton button : buttons) { button.addActionListener(selectionHandler); } dialog.pack(); dialog.setLocationRelativeTo(dialog.getOwner()); dialog.setVisible(true); return selectionHandler.newInstance; } else if (instantiators.size() == 1) { return instantiators.get(0).newInstance(target); } return target.newInstance(Class.forName(className)); } catch (Exception ex) { throw new RuntimeException("Unable to instantiate " + className, ex); } } return null; } } protected class ComboBoxCellRenderer extends DefaultListCellRenderer { /** * */ private static final long serialVersionUID = 2500495712143382316L; @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { value = formatNodeValue(value); return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); } } protected static abstract class DefaultAbstractFormat<T> implements Format<T> { @Override public String format(T t) { return String.valueOf(t); } } protected abstract class DefaultInstantiator<T> implements Instantiator<T> { @Override public String getDescription() { try { return "new " + typeFormatter.format(getClass().getMethod("newInstance", Node.class).getGenericReturnType()) + "()"; } catch (Exception e) { e.printStackTrace(); return super.toString(); } } @Override public Class<?> getInstantiatingType() { Type type = getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { return ReflectionUtils.getRawType(((ParameterizedType) type).getActualTypeArguments()[0]); } return Object.class; } @Override public abstract T newInstance(Node target); @Override public String toString() { return getDescription(); } } protected static interface InstantiationListener { public void objectInstantiated(Object instantiatedObject); } protected static interface Instantiator<T> { public String getDescription(); public abstract Class<?> getInstantiatingType(); public T newInstance(Node target) throws Exception; } protected static class InstantiatorMenuItem extends JMenuItem { /** * */ private static final long serialVersionUID = -2431080775522231846L; public final Instantiator<?> instantiator; public InstantiatorMenuItem(Instantiator<?> instantiator) { super(instantiator.getDescription()); this.instantiator = instantiator; } } protected class NodeCellRenderer extends DefaultTreeCellRenderer { /** * */ private static final long serialVersionUID = -1259374748969694909L; @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { if (value instanceof Node) { value = ((Node) value).toString(typeFormatter); } Component comp = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (comp instanceof JLabel) { ((JLabel) comp).setIcon(null); } return comp; } } protected static class NullableFormat<T> implements Format<T> { Format<T> wrapped; protected NullableFormat(Format<T> wrapped) { this.wrapped = wrapped; } @Override public String format(T t) { return wrapped.format(t); } @Override public T parse(String s) throws Exception { if (s == null || "".equals(s) || "null".equals(s)) { return null; } return wrapped.parse(s); } } protected class Retargeter extends TableCellRendererRetargeter { protected MouseEvent lastReleaseEvent; @Override public void mouseReleased(MouseEvent e) { lastReleaseEvent = e; super.mouseReleased(e); } @Override protected boolean shouldRetarget(MouseEvent e) { return columnAtPoint(e.getPoint()) == 1; } } protected class ValueCellRenderer extends TableCellRendererWithButtons { /** * */ private static final long serialVersionUID = -4953570165407741136L; JButton insertChildButton; JButton insertSiblingButton; JButton removeButton; JButton nullifyButton; JButton newifyButton; RendererButtonModel.RendererContext rendererButtonModelContext; public ValueCellRenderer() { this(new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { return super.getTableCellRendererComponent(table, formatNodeValue(value), isSelected, hasFocus, row, column); } }); } public ValueCellRenderer(TableCellRenderer wrapped) { super(wrapped); initListeners(); } protected javax.swing.ButtonModel createButtonModel() { return new RendererButtonModel(rendererButtonModelContext); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Node node = getNodeAt(row); if (node.getStorage() instanceof ElementStorage) { ListlikeAspect listAspect = (ListlikeAspect) node.getParent().getAspect(); insertSiblingButton.setVisible(listAspect.canAddOrRemoveChildren()); removeButton.setVisible(listAspect.canAddOrRemoveChildren() && node.getParent().getChildCount() > 0); } else if (node.getStorage() instanceof MapEntryStorage) { insertChildButton.setVisible(false); insertSiblingButton.setVisible(false); removeButton.setVisible(((MaplikeAspect) node.getParent().getAspect()).canAddOrRemoveChildren()); } else { insertChildButton.setVisible(false); insertSiblingButton.setVisible(false); removeButton.setVisible(false); } if (node.getAspect() instanceof ListlikeAspect) { insertChildButton.setVisible(((ListlikeAspect) node.getAspect()).canAddOrRemoveChildren()); } else if (node.getAspect() instanceof MaplikeAspect) { insertChildButton.setVisible(((MaplikeAspect) node.getAspect()).canAddOrRemoveChildren()); } else { insertChildButton.setVisible(false); } newifyButton.setVisible(shouldShowNewifyButton(node)); nullifyButton.setVisible(shouldShowNullifyButton(node)); return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } @Override protected JButton[] initButtons() { rendererButtonModelContext = new TableCellRendererButtonModelContext(retargeter, this); newifyButton = new JButton(newIcon); nullifyButton = new JButton(nullifyIcon); insertChildButton = new JButton(insertChildIcon); insertSiblingButton = new JButton(insertSiblingIcon); removeButton = new JButton(removeNodeIcon); JButton[] buttons = new JButton[] { insertChildButton, insertSiblingButton, removeButton, newifyButton, nullifyButton }; for (JButton button : buttons) { button.setOpaque(false); button.setMargin(new Insets(1, 1, 1, 1)); button.setModel(createButtonModel()); add(button); } return buttons; } protected void initListeners() { newifyButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeEditor(); Node node = getNodeAt(rendererRow); try { handleNewButtonPressed(e, node); } catch (Exception ex) { ex.printStackTrace(); } } }); nullifyButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeEditor(); Node node = getNodeAt(rendererRow); try { handleNullifyButtonPressed(node); } catch (Exception ex) { ex.printStackTrace(); } } }); removeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Node node = getNodeAt(rendererRow); handleRemoveButtonPressed(node); } }); insertChildButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Node node = getNodeAt(rendererRow); handleInsertChildButtonPressed(node); } }); insertSiblingButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Node node = getNodeAt(rendererRow); handleInsertSiblingButtonPressed(node); } }); } } /** * */ private static final long serialVersionUID = 736807295352614488L; private static ImageIcon insertChildIcon = new ImageIcon(ObjectTreeTable.class.getResource("insert-child.png")); private static ImageIcon insertSiblingIcon = new ImageIcon(ObjectTreeTable.class.getResource("insert-sibling.png")); private static ImageIcon removeNodeIcon = new ImageIcon(ObjectTreeTable.class.getResource("remove.png")); private static ImageIcon nullifyIcon = new ImageIcon(ObjectTreeTable.class.getResource("nullify.png")); private static ImageIcon newIcon = new ImageIcon(ObjectTreeTable.class.getResource("new.png")); private static Constructor<?> findNullaryConstructor(Class<?> type) { for (Constructor<?> constructor : type.getDeclaredConstructors()) { if (constructor.getDeclaringClass().equals(type) && constructor.getParameterTypes().length == 0) { return constructor; } } return null; } protected static void put(MultiMap<Class<?>, Instantiator<?>> map, Class<?> c, Instantiator<?> instantiator) { while (c != null && c != Object.class) { map.put(c, instantiator); for (Class<?> iface : c.getInterfaces()) { put(map, iface, instantiator); } c = c.getSuperclass(); } } protected static void put(MultiMap<Class<?>, Instantiator<?>> map, Instantiator<?> instantiator) { put(map, instantiator.getInstantiatingType(), instantiator); } protected NodeCellRenderer nodeCellRenderer; protected ValueCellRenderer defaultValueCellRenderer; protected ValueCellRenderer booleanValueCellRenderer; protected final Map<Class<?>, Format<?>> leafFormats = CollectionUtils.newHashMap(); protected final LinkedListMultiMap<Class<?>, Object> enumlikeTypes = LinkedListMultiMap.newInstance(); protected final LinkedHashSetMultiMap<Class<?>, Instantiator<?>> instantiators = LinkedHashSetMultiMap .newInstance(); protected TypeFormatter typeFormatter = new CompactTypeFormatter(); protected ArbitraryInstantiator arbitraryInstantiator = new ArbitraryInstantiator(); protected Retargeter retargeter = new Retargeter(); public ObjectTreeTable() { this(new ObjectTreeTableModel()); } public ObjectTreeTable(ObjectTreeTableModel model) { super(model); model.setTypeFormatter(typeFormatter); init(); } @SuppressWarnings({ "rawtypes", "unchecked" }) protected Object formatNodeValue(Object value) { if (value == null) { return "null"; } else { Format format = leafFormats.get(value.getClass()); if (format != null) { return "<html>" + format.format(value) + " <font color=\"gray\">(" + typeFormatter.format(value.getClass()) + String.format(" @%x)</font></html>", System.identityHashCode(value)); } else if (value.getClass().isEnum()) { return "<html>" + value + " <font color=\"gray\">(" + typeFormatter.format(value.getClass()) + ")</font></html>"; } else { return "<html>" + typeFormatter.format(value.getClass()) + String.format(" <font color=\"gray\"> @%x</font></html>", System.identityHashCode(value)); } } } @Override public TableCellEditor getCellEditor(int row, int column) { Node node = getNodeAt(row); Class<?> valueClass = node.getValue() == null ? null : node.getValue().getClass(); Class<?> storageClass = node.getStorageClass(); if (node.getValue() instanceof Enum) { DefaultSelector<Object> selector = new DefaultSelector<Object>(); selector.setAvailableValues(Arrays.asList(valueClass.getEnumConstants())); selector.comboBox().setRenderer(new ComboBoxCellRenderer()); return new DefaultCellEditor(selector.comboBox()); } else if (storageClass.isEnum()) { DefaultSelector<Object> selector = new DefaultSelector<Object>(); selector.setAvailableValues(Arrays.asList(storageClass.getEnumConstants())); selector.comboBox().setRenderer(new ComboBoxCellRenderer()); return new DefaultCellEditor(selector.comboBox()); } else if (enumlikeTypes.containsKey(valueClass)) { DefaultSelector<Object> selector = new DefaultSelector<Object>(); selector.setAvailableValues(enumlikeTypes.get(valueClass)); selector.comboBox().setRenderer(new ComboBoxCellRenderer()); return new DefaultCellEditor(selector.comboBox()); } else if (enumlikeTypes.containsKey(storageClass)) { DefaultSelector<Object> selector = new DefaultSelector<Object>(); selector.setAvailableValues(enumlikeTypes.get(storageClass)); selector.comboBox().setRenderer(new ComboBoxCellRenderer()); return new DefaultCellEditor(selector.comboBox()); } else if (valueClass == boolean.class || valueClass == Boolean.class || storageClass == boolean.class || storageClass == Boolean.class) { return new DefaultCellEditor(new JCheckBox()); } else if (valueClass == Date.class || storageClass == Date.class) { DateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); DatePickerCellEditor datePicker = new DatePickerCellEditor(format); datePicker.setClickCountToStart(0); return datePicker; } return super.getCellEditor(row, column); } @Override public TableCellRenderer getCellRenderer(int row, int column) { if (column == 1) { Node node = getNodeAt(row); if (node.getValue() instanceof Boolean) { return booleanValueCellRenderer; } return defaultValueCellRenderer; } return super.getCellRenderer(row, column); } private Node getNodeAt(int row) { return (Node) super.getValueAt(row, 0); } @Override public ObjectTreeTableModel getTreeTableModel() { return (ObjectTreeTableModel) super.getTreeTableModel(); } protected void handleInsertChildButtonPressed(Node node) { removeEditor(); try { if (node.getAspect() instanceof ListlikeAspect) { ((ListlikeAspect) node.getAspect()).addNewElement(node.getChildCount(), null); expandPath(new TreePath(node.getPath())); TreePath childPath = new TreePath(node.getChild(node.getChildCount() - 1).getPath()); expandPath(childPath); int row = getRowForPath(childPath); getSelectionModel().setSelectionInterval(row, row); } else if (node.getAspect() instanceof MaplikeAspect) { ((MaplikeAspect) node.getAspect()).putNewEntry(null, null); TreePath childPath = new TreePath(node.getChild(node.getChildCount() - 1).getPath()); expandPath(childPath); int row = getRowForPath(childPath); getSelectionModel().setSelectionInterval(row, row); } } catch (Exception ex) { ex.printStackTrace(); } } protected void handleInsertSiblingButtonPressed(Node node) { removeEditor(); try { int index = node.getParent().indexOf(node); ((ListlikeAspect) node.getParent().getAspect()).addNewElement(index, null); TreePath childPath = new TreePath(node.getParent().getChild(index).getPath()); expandPath(childPath); int row = getRowForPath(childPath); getSelectionModel().setSelectionInterval(row, row); } catch (Exception ex) { ex.printStackTrace(); } } protected void handleInstantiationExecption(Exception e) { JOptionPane.showMessageDialog(ObjectTreeTable.this, e.getLocalizedMessage(), "Instantiation failed", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } protected void handleNewButtonPressed(ActionEvent e, final Node node) throws Exception { // node.setValueToNewInstance(); // expandPath(new TreePath(node.getPath())); showInstantiatorPopupMenu(node, this, retargeter.lastReleaseEvent.getX(), retargeter.lastReleaseEvent.getY(), new InstantiationListener() { @Override public void objectInstantiated(Object instantiatedObject) { node.setValue(instantiatedObject); expandPath(new TreePath(node.getPath())); } }); } protected void handleNullifyButtonPressed(Node node) { node.setValue(null); } protected void handleParseException(Exception e) { e.printStackTrace(); } protected void handleRemoveButtonPressed(Node node) { removeEditor(); if (node.getStorage() instanceof ListElementStorage || node.getStorage() instanceof ArrayElementStorage) { ((ListlikeAspect) node.getParent().getAspect()).removeElement(node.getParent().indexOf(node)); } else if (node.getStorage() instanceof MapEntryStorage) { Node mapNode = node.getParent(); ((MaplikeAspect) mapNode.getAspect()).removeEntry(node); } } protected void init() { initFormats(); initInstantiators(); setRowHeight(getRowHeight() + 1); setOpenIcon(null); setClosedIcon(null); setLeafIcon(null); getTableHeader().setReorderingAllowed(false); retargeter = new Retargeter(); addMouseListener(retargeter); addMouseMotionListener(retargeter); nodeCellRenderer = new NodeCellRenderer(); // setTreeCellRenderer(nodeCellRenderer); defaultValueCellRenderer = new ValueCellRenderer(); booleanValueCellRenderer = new ValueCellRenderer(new CheckboxTableCellRenderer(new JCheckBox())); setUI(new CellEditorBoundsOverridingTableUI()); } protected void initFormats() { Format<Boolean> booleanFormat = new DefaultAbstractFormat<Boolean>() { @Override public Boolean parse(String s) throws Exception { return Boolean.valueOf(s); } }; leafFormats.put(boolean.class, booleanFormat); leafFormats.put(Boolean.class, new NullableFormat<Boolean>(booleanFormat)); Format<Byte> byteFormat = new DefaultAbstractFormat<Byte>() { @Override public Byte parse(String s) throws Exception { return Byte.valueOf(s); } }; leafFormats.put(byte.class, byteFormat); leafFormats.put(Byte.class, new NullableFormat<Byte>(byteFormat)); Format<Character> charFormat = new DefaultAbstractFormat<Character>() { @Override public Character parse(String s) throws Exception { return s.charAt(0); } }; leafFormats.put(char.class, charFormat); leafFormats.put(Character.class, new NullableFormat<Character>(charFormat)); Format<Short> shortFormat = new DefaultAbstractFormat<Short>() { @Override public Short parse(String s) throws Exception { return Short.valueOf(s); } }; leafFormats.put(short.class, shortFormat); leafFormats.put(Short.class, new NullableFormat<Short>(shortFormat)); Format<Integer> intFormat = new DefaultAbstractFormat<Integer>() { @Override public Integer parse(String s) throws Exception { return Integer.valueOf(s); } }; leafFormats.put(int.class, intFormat); leafFormats.put(Integer.class, new NullableFormat<Integer>(intFormat)); Format<Float> floatFormat = new DefaultAbstractFormat<Float>() { @Override public Float parse(String s) throws Exception { return Float.valueOf(s); } }; leafFormats.put(float.class, floatFormat); leafFormats.put(Float.class, new NullableFormat<Float>(floatFormat)); Format<Long> longFormat = new DefaultAbstractFormat<Long>() { @Override public Long parse(String s) throws Exception { return Long.valueOf(s); } }; leafFormats.put(long.class, longFormat); leafFormats.put(Long.class, new NullableFormat<Long>(longFormat)); Format<Double> doubleFormat = new DefaultAbstractFormat<Double>() { @Override public Double parse(String s) throws Exception { return Double.valueOf(s); } }; leafFormats.put(double.class, doubleFormat); leafFormats.put(Double.class, new NullableFormat<Double>(doubleFormat)); Format<BigInteger> bigIntegerFormat = new DefaultAbstractFormat<BigInteger>() { @Override public BigInteger parse(String s) throws Exception { return new BigInteger(s); } }; leafFormats.put(BigInteger.class, bigIntegerFormat); Format<BigDecimal> bigDecimalFormat = new DefaultAbstractFormat<BigDecimal>() { @Override public BigDecimal parse(String s) throws Exception { return new BigDecimal(s); } }; leafFormats.put(BigDecimal.class, bigDecimalFormat); Format<String> stringFormat = new DefaultAbstractFormat<String>() { @Override public String parse(String s) throws Exception { return String.valueOf(s); } }; leafFormats.put(String.class, stringFormat); final Format<Date> dateFormat = new Format<Date>() { SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); @Override public String format(Date t) { return format.format(t); } @Override public Date parse(String s) throws Exception { return format.parse(s); } }; leafFormats.put(Date.class, dateFormat); leafFormats.put(Calendar.class, new Format<Calendar>() { @Override public String format(Calendar t) { return dateFormat.format(t.getTime()); } @Override public Calendar parse(String s) throws Exception { Date date = dateFormat.parse(s); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar; } }); } protected void initInstantiators() { put(instantiators, new DefaultInstantiator<ArrayList<?>>() { @Override public ArrayList<?> newInstance(Node target) { return new ArrayList<Object>(); } }); put(instantiators, new DefaultInstantiator<LinkedList<?>>() { @Override public LinkedList<?> newInstance(Node target) { return new LinkedList<Object>(); } }); put(instantiators, new DefaultInstantiator<Vector<?>>() { @Override public Vector<?> newInstance(Node target) { return new Vector<Object>(); } }); put(instantiators, new DefaultInstantiator<HashSet<?>>() { @Override public HashSet<?> newInstance(Node target) { return new HashSet<Object>(); } }); put(instantiators, new DefaultInstantiator<TreeSet<?>>() { @Override public TreeSet<?> newInstance(Node target) { return new TreeSet<Object>(); } }); put(instantiators, new DefaultInstantiator<LinkedHashSet<?>>() { @Override public LinkedHashSet<?> newInstance(Node target) { return new LinkedHashSet<Object>(); } }); put(instantiators, new DefaultInstantiator<HashMap<?, ?>>() { @Override public HashMap<?, ?> newInstance(Node target) { return new HashMap<Object, Object>(); } }); put(instantiators, new DefaultInstantiator<LinkedHashMap<?, ?>>() { @Override public LinkedHashMap<?, ?> newInstance(Node target) { return new LinkedHashMap<Object, Object>(); } }); put(instantiators, new DefaultInstantiator<Hashtable<?, ?>>() { @Override public Hashtable<?, ?> newInstance(Node target) { return new Hashtable<Object, Object>(); } }); put(instantiators, new DefaultInstantiator<TreeMap<?, ?>>() { @Override public TreeMap<?, ?> newInstance(Node target) { return new TreeMap<Object, Object>(); } }); put(instantiators, new DefaultInstantiator<WeakHashMap<?, ?>>() { @Override public WeakHashMap<?, ?> newInstance(Node target) { return new WeakHashMap<Object, Object>(); } }); put(instantiators, new DefaultInstantiator<Date>() { @Override public String getDescription() { return "new Date()"; } @Override public Date newInstance(Node target) { return new Date(); } }); put(instantiators, new DefaultInstantiator<Calendar>() { @Override public String getDescription() { return "Calendar.getInstance()"; } @Override public Calendar newInstance(Node target) { return Calendar.getInstance(); } }); put(instantiators, new DefaultInstantiator<Boolean>() { @Override public String getDescription() { return "new Boolean(false)"; } @Override public Boolean newInstance(Node target) { return new Boolean(false); } }); put(instantiators, new DefaultInstantiator<Boolean>() { @Override public String getDescription() { return "new Boolean(true)"; } @Override public Boolean newInstance(Node target) { return new Boolean(true); } }); put(instantiators, new DefaultInstantiator<Boolean>() { @Override public String getDescription() { return "Boolean.FALSE"; } @Override public Boolean newInstance(Node target) { return Boolean.FALSE; } }); put(instantiators, new DefaultInstantiator<Boolean>() { @Override public String getDescription() { return "Boolean.TRUE"; } @Override public Boolean newInstance(Node target) { return Boolean.TRUE; } }); put(instantiators, new DefaultInstantiator<Byte>() { @Override public String getDescription() { return "new Byte((byte) 0)"; } @Override public Byte newInstance(Node target) { return new Byte((byte) 0); } }); put(instantiators, new DefaultInstantiator<Short>() { @Override public String getDescription() { return "new Short((short) 0)"; } @Override public Short newInstance(Node target) { return new Short((short) 0); } }); put(instantiators, new DefaultInstantiator<Character>() { @Override public String getDescription() { return "new Character((char) 0)"; } @Override public Character newInstance(Node target) { return new Character((char) 0); } }); put(instantiators, new DefaultInstantiator<Integer>() { @Override public String getDescription() { return "new Integer(0)"; } @Override public Integer newInstance(Node target) { return new Integer(0); } }); put(instantiators, new DefaultInstantiator<Float>() { @Override public String getDescription() { return "new Float(0f)"; } @Override public Float newInstance(Node target) { return new Float(0f); } }); put(instantiators, new DefaultInstantiator<Long>() { @Override public String getDescription() { return "new Long(0L)"; } @Override public Long newInstance(Node target) { return new Long(0L); } }); put(instantiators, new DefaultInstantiator<Double>() { @Override public String getDescription() { return "new Double(0.0)"; } @Override public Double newInstance(Node target) { return new Double(0.0); } }); } @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component comp = super.prepareRenderer(renderer, row, column); if (comp instanceof ValueCellRenderer) { ValueCellRenderer vcr = (ValueCellRenderer) comp; if (column == 1) { Node node = getNodeAt(row); if (node.getValue() == null) { vcr.getContent().setForeground(Color.GRAY); } else if (!isRowSelected(row)) { vcr.getContent().setForeground(Color.BLACK); } } } return comp; } public void promptForNewRootClass() { if (getTreeTableModel().getRootObject() == null) { getTreeTableModel().setRoot(new Object()); } Object newInstance = null; try { newInstance = arbitraryInstantiator.newInstance((Node) getTreeTableModel().getRoot()); } catch (Exception e) { handleInstantiationExecption(e); } if (newInstance != null) { getTreeTableModel().setRoot(newInstance); } } @Override public void setTreeTableModel(TreeTableModel treeModel) { if (!(treeModel instanceof ObjectTreeTableModel)) { throw new IllegalArgumentException("treeModel must be an instanceof ObjectTreeTableModel"); } ((ObjectTreeTableModel) treeModel).setTypeFormatter(typeFormatter); super.setTreeTableModel(treeModel); } @SuppressWarnings("rawtypes") @Override public void setValueAt(Object aValue, int row, int column) { if (aValue != null) { Node node = getNodeAt(row); if (node.getValue() != null) { Class<?> valueClass = node.getValue().getClass(); if (valueClass.isAssignableFrom(aValue.getClass())) { super.setValueAt(aValue, row, column); return; } else { Format format = leafFormats.get(valueClass); if (format != null) { try { aValue = format.parse(aValue.toString()); super.setValueAt(aValue, row, column); return; } catch (Exception e) { } } } } Class<?> storageClass = node.getStorageClass(); if (!storageClass.isAssignableFrom(aValue.getClass())) { Format format = leafFormats.get(storageClass); if (format != null) { try { aValue = format.parse(aValue.toString()); } catch (Exception e) { handleParseException(e); return; } } } } super.setValueAt(aValue, row, column); } protected boolean shouldShowNewifyButton(Node node) { return node.isMutable(); } protected boolean shouldShowNullifyButton(Node node) { return node.getValue() != null && node.isMutable() && !node.isPrimitive(); } protected void showInstantiatorPopupMenu(final Node node, final Class<?> storageClass, Component invoker, int x, int y, final InstantiationListener instantiationListener) { ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Instantiator<?> instantiator = ((InstantiatorMenuItem) e.getSource()).instantiator; try { Object instance = instantiator.newInstance(node); if (instance != null) { instantiationListener.objectInstantiated(instance); } } catch (Exception e1) { handleInstantiationExecption(e1); } } }; JPopupMenu menu = new JPopupMenu(); for (Instantiator<?> instantiator : instantiators.get(storageClass)) { InstantiatorMenuItem item = new InstantiatorMenuItem(instantiator); item.addActionListener(al); menu.add(item); } if (menu.getComponentCount() == 0 && !storageClass.isInterface() && (storageClass.isArray() || !Modifier.isAbstract(storageClass.getModifiers()))) { Instantiator<Object> defaultInstantiator = new Instantiator<Object>() { @Override public String getDescription() { return "new " + typeFormatter.format(storageClass); } @Override public Class<?> getInstantiatingType() { return Object.class; } @Override public Object newInstance(Node node) throws Exception { return node.newInstance(storageClass); } }; InstantiatorMenuItem defaultItem = new InstantiatorMenuItem(defaultInstantiator); defaultItem.addActionListener(al); menu.add(defaultItem); } InstantiatorMenuItem arbitraryItem = new InstantiatorMenuItem(arbitraryInstantiator); arbitraryItem.addActionListener(al); menu.add(arbitraryItem); if (menu.getComponentCount() == 0) { JMenuItem notice = new JMenuItem("No instantiators available"); menu.add(notice); } menu.show(invoker, x, y); } protected void showInstantiatorPopupMenu(final Node node, Component invoker, int x, int y, final InstantiationListener instantiationListener) { showInstantiatorPopupMenu(node, node.getStorageClass(), invoker, x, y, instantiationListener); } }