java-org.hwo.ui/src/org/hwo/models/TableMapper/TableMapper.java

985 lines
22 KiB
Java

package org.hwo.models.TableMapper;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import org.hwo.csv.CSV;
import org.hwo.csv.CSVRecord;
import org.hwo.interactiveobjects.IInteractiveObjectEditor;
import org.hwo.interactiveobjects.InteractiveObjectHelper;
import org.hwo.interactiveobjects.ObjectEditorUIHelper;
import org.hwo.ui.KeyStrokeHelper;
import org.hwo.ui.KeyStrokeListener;
import org.hwo.ui.KeyStrokeParameters;
import org.w3c.dom.views.AbstractView;
import static org.hwo.logging.Logging.*;
import static org.hwo.logging.LogLevel.*;
public class TableMapper extends AbstractTableModel
{
private static final long serialVersionUID = -6556060772230310664L;
public class ColumnInfo
{
private TableColumn p_annotation;
private Field p_field;
private Method p_method,
p_method_w;
private String p_label;
private boolean p_readonly;
private Class<?> p_columnclass;
private int width;
private TableColumn tableColumn;
private ColumnInfo nextColumn;
private int order;
private Integer
extractField;
protected ColumnInfo(TableColumn tc,Field field)
{
tableColumn = tc;
p_annotation = field.getAnnotation(TableColumn.class);
p_method = null;
p_field = field;
p_columnclass = field.getType();
initialize();
if (p_label.equals(""))
p_label = field.getName();
field.setAccessible(true);
}
protected ColumnInfo(TableColumn tc,Method method)
{
tableColumn = tc;
p_annotation = method.getAnnotation(TableColumn.class);
p_method = method;
if (p_method.getName().startsWith("get")){
Class<?> clz = p_method.getDeclaringClass();
String w_name = "set" + p_method.getName().substring(3);
System.err.println(w_name);
System.err.println(clz.getName());
try{
p_method_w = clz.getDeclaredMethod(w_name,new Class<?>[]{ p_method.getReturnType() });
System.err.println(String.format("TableMapper found setter %s",p_method_w.getName()));
} catch (NoSuchMethodException e){
System.err.println(String.format("Setter %s not found.",w_name));
}
};
p_field = null;
p_columnclass = method.getReturnType();
initialize();
if (p_label.equals(""))
p_label = method.getName();
method.setAccessible(true);
if (p_method_w != null)
p_method_w.setAccessible(true);
if (!p_readonly){
p_readonly = (p_method_w==null);
}
}
private void initialize()
{
p_label = p_annotation.label();
p_readonly = p_annotation.readonly();
setWidth(p_annotation.width());
order = p_annotation.order();
}
public void appendAtEnd(ColumnInfo column)
{
if (nextColumn == null)
nextColumn = column;
else
nextColumn.appendAtEnd(column);
}
public List<ColumnInfo> toList()
{
List<ColumnInfo> list = new LinkedList<TableMapper.ColumnInfo>();
appendToList(list);
return list;
}
public void appendToList(List<ColumnInfo> list)
{
list.add(this);
if (nextColumn != null)
nextColumn.appendToList(list);
}
public void removeColumn(ColumnInfo ci)
{
if (nextColumn == ci)
{
nextColumn = ci.nextColumn;
ci.nextColumn = null;
} else if (nextColumn != null)
nextColumn.removeColumn(ci);
}
public ColumnInfo findColumnByName(String name)
{
if (this.p_label.equals(name))
return this;
if (nextColumn!=null)
return nextColumn.findColumnByName(name);
else
return null;
}
public void append(ColumnInfo ci)
{
ci.nextColumn = nextColumn;
nextColumn = ci;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getColumnLabel(){
return String.format(this.p_label, extractField);
}
public String getLabel()
{
return this.p_label;
}
public void setLabel(String label)
{
this.p_label = label;
}
public boolean getReadOnly()
{
return this.p_readonly;
}
public void setReadOnly(boolean readOnly)
{
this.p_readonly = readOnly;
}
public void setColumnClass(Class<?> cl)
{
this.p_columnclass = cl;
}
public Class<?> getColumnClass()
{
return this.p_columnclass;
}
private Object fetchValue(Object instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Object v = null;
if (p_field != null)
v = p_field.get(instance);
if (p_method != null)
v = p_method.invoke(instance);
return v;
}
public Object getValue(Object instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Object v = fetchValue(instance);
if (v.getClass().isArray() && (extractField != null)){
v = ((Object[])v)[extractField];
}
if (!p_annotation.format().equals("")){
v = String.format(p_annotation.format(), v);
}
return v;
}
public void setValue(Object instance,Object value) throws IllegalAccessException,InvocationTargetException
{
if (extractField != null){
Object v = fetchValue(instance);
((Object[])v)[extractField] = value;
if (p_method_w != null){
try {
p_method_w.invoke(instance, v);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} else {
if (p_field != null)
p_field.set(instance, value);
if (p_method_w != null){
try {
p_method_w.invoke(instance, value);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public ColumnInfo getNextColumn() {
return nextColumn;
}
public void setNextColumn(ColumnInfo nextColumn) {
this.nextColumn = nextColumn;
}
public TableColumn getTableColumn() {
return tableColumn;
}
public void setTableColumn(TableColumn tableColumn) {
this.tableColumn = tableColumn;
}
public Integer getExtractField() {
return extractField;
}
public void setExtractField(Integer extractField) {
this.extractField = extractField;
}
}
private Class<?> p_class;
private ColumnInfo firstColumn;
private List<Object> p_rows;
private List<ColumnInfo> p_columns;
private boolean p_readonly; // Table readonly...
private boolean p_editoronpointer;
private TableMapperColumnModel
columnModel;
private JTable jTable;
private TableMapperObject
tableMapperObject;
private Field editorObjectField;
private Class<?> editorObjectClass;
private MouseAdapter mouseAdapter;
private boolean editorEnabled;
private boolean sortingEnabled;
private LinkedList<TableMapperListener> tableMapperListeners;
private KeyStrokeHelper keyStrokeHelper;
private TableMapperColorSourceInterface
colorSourceInterface;
public TableMapper(Class<?> clazz,JTable table)
{
this.tableMapperListeners = new LinkedList<TableMapperListener>();
this.p_class = clazz;
this.jTable = table;
this.firstColumn = null;
this.p_rows = new LinkedList<Object>();
this.p_columns = new ArrayList<TableMapper.ColumnInfo>();
this.p_readonly = false;
this.keyStrokeHelper = new KeyStrokeHelper(jTable);
this.keyStrokeHelper.registerKeyStrokeListener(new KeyStrokeListener() {
@Override
public void keyStroke(KeyStrokeParameters parameters) {
switch (parameters.getKeyCode())
{
case KeyEvent.VK_ENTER:
openEditor();
break;
}
}
});
this.keyStrokeHelper.addKeyCode(KeyEvent.VK_ENTER);
initializeMapper();
initializeColumnInfo();
table.setModel(this);
table.setColumnModel(new TableMapperColumnModel(this));
setSortingEnabled(true);
table.setAutoCreateRowSorter(true);
table.setDefaultRenderer(Object.class, new TableCellRenderer());
mouseAdapter = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
switch (e.getButton())
{
case 1:
if (e.getClickCount()==2)
{
openEditor();
}
}
}
};
table.addMouseListener(mouseAdapter);
if (InteractiveObjectHelper.isInteractiveObject(editorObjectClass))
setEditorEnabled(true);
if (ObjectEditorUIHelper.isEditable(editorObjectClass))
setEditorEnabled(true);
}
private void initializeMapper()
{
tableMapperObject = p_class.getAnnotation(TableMapperObject.class);
if (tableMapperObject != null)
{
if (tableMapperObject.editorField() != null)
{
try {
editorObjectField = p_class.getDeclaredField(tableMapperObject.editorField());
editorObjectField.setAccessible(true);
editorObjectClass = editorObjectField.getType();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else
{
editorObjectClass = p_class;
}
}
private void initializeJTable(){
if (sortingEnabled){
this.jTable.setRowSorter(new TableRowSorter<TableMapper>(this));
} else {
this.jTable.setRowSorter(null);
}
int minwidth = 0;
for (ColumnInfo ci: p_columns){
minwidth += ci.getWidth();
}
//jTable.setMinSize(new Dimension(minwidth, 24));
jTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
public boolean isSortingEnabled() {
return sortingEnabled;
}
public void setSortingEnabled(boolean sortingEnabled) {
this.sortingEnabled = sortingEnabled;
initializeJTable();
}
public TableMapperColorSourceInterface getColorSourceInterface() {
return colorSourceInterface;
}
public void setColorSourceInterface(TableMapperColorSourceInterface colorSourceInterface) {
this.colorSourceInterface = colorSourceInterface;
}
public void addListSelectionListener(ListSelectionListener listener)
{
jTable.getSelectionModel().addListSelectionListener(listener);
}
public void removeListSelectionListener(ListSelectionListener listener)
{
jTable.getSelectionModel().removeListSelectionListener(listener);
}
public void addTableMapperListener(TableMapperListener listener)
{
tableMapperListeners.add(listener);
}
public void removeTableMapperListener(TableMapperListener listener)
{
tableMapperListeners.remove(listener);
}
protected void fireValueChanged(int row,int column)
{
for (TableMapperListener listener: tableMapperListeners)
listener.ValueChanged(row, column);
}
private Object getEditorObject()
{
if (jTable.getSelectedRow()!=-1)
{
int row = jTable.convertRowIndexToModel(jTable.getSelectedRow());
Object rowObject = p_rows.get(row);
if (editorObjectField == null)
return rowObject;
try {
Object editorObject = editorObjectField.get(rowObject);
return editorObject;
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
private void centerWindowOnPointer(Object _w){
if (p_editoronpointer && Window.class.isInstance(_w)){
Window w = (Window)_w;
Point mp = MouseInfo.getPointerInfo().getLocation();
mp.x -= w.getWidth()/2;
mp.y -= w.getHeight()/2;
w.setLocation(mp);
}
}
public void openEditor()
{
System.err.println("TableMapper.openEditor()");
for (TableMapperListener listener: this.tableMapperListeners){
if (listener.editorRequest(this, getSelectedRow())){
return;
}
}
try {
Object editorObject = getEditorObject();
if (editorObject != null){
centerWindowOnPointer( ObjectEditorUIHelper.editor(editorObject) );
if (ObjectEditorUIHelper.edit(editorObject)){
fireTableDataChanged();
}
}
return;
} catch (NoClassDefFoundError e){
};
Object editorObject = getEditorObject();
if (editorObject!=null){
IInteractiveObjectEditor oe = InteractiveObjectHelper.getEditor(editorObject);
if (oe != null){
centerWindowOnPointer(oe);
oe.setVisible(true);
}
InteractiveObjectHelper.showEditor(editorObject);
}
}
public List<ColumnInfo> getColumnInfo()
{
return p_columns;
}
public void setRows(List rows)
{
this.p_rows = rows;
fireTableDataChanged();
}
public void setRows(Object[] rows)
{
this.p_rows = new ArrayList<>();
this.p_rows.addAll(Arrays.asList(rows));
fireTableDataChanged();
}
public List getRows()
{
return this.p_rows;
}
public <T> List<T> getRows(Class<T> clazz)
{
return (List<T>) p_rows;
}
public void clear()
{
this.p_rows.clear();
fireTableDataChanged();
}
public boolean isEditorEnabled()
{
return editorEnabled;
}
public void setEditorEnabled(boolean enabled)
{
if (enabled)
jTable.addMouseListener(mouseAdapter);
else
jTable.removeMouseListener(mouseAdapter);
editorEnabled = enabled;
}
private void addColumnInfo(ColumnInfo ci)
{
if (ci.getTableColumn().firstColumn())
p_columns.add(0,ci);
else
p_columns.add(ci);
/*
if (firstColumn == null)
firstColumn = ci;
else
{
if (ci.getTableColumn().firstColumn())
{
ci.setNextColumn(firstColumn);
firstColumn = ci;
} else
firstColumn.appendAtEnd(ci);
}
*/
}
private void addFieldsOfClass(Class<?> clazz)
{
if (clazz.getSuperclass() != null)
addFieldsOfClass(clazz.getSuperclass());
for (Field field: clazz.getDeclaredFields())
{
if (field.getAnnotation(TableColumn.class)!=null)
{
int fl = field.getAnnotation(TableColumn.class).fieldlength();
if (fl > 0){
for (int i=0;i<fl;i++){
ColumnInfo ci = new ColumnInfo(field.getAnnotation(TableColumn.class),field);
ci.setExtractField(i);
ci.setOrder(ci.getOrder() + i);
addColumnInfo(ci);
}
} else {
ColumnInfo ci = new ColumnInfo(field.getAnnotation(TableColumn.class),field);
addColumnInfo(ci);
}
}
}
for (Method method: clazz.getDeclaredMethods())
{
if (method.getAnnotation(TableColumn.class)!=null)
{
int fl = method.getAnnotation(TableColumn.class).fieldlength();
if (fl > 0){
for (int i=0;i<fl;i++){
ColumnInfo ci = new ColumnInfo(method.getAnnotation(TableColumn.class),method);
ci.setExtractField(i);
ci.setOrder(ci.getOrder() + i);
addColumnInfo(ci);
}
} else {
ColumnInfo ci = new ColumnInfo(method.getAnnotation(TableColumn.class),method);
addColumnInfo(ci);
}
}
}
}
private int findColumnIndexByName(String name)
{
for (int i=0;i<p_columns.size();i++)
{
if (p_columns.get(i).p_label.equals(name))
return i;
}
return -1;
}
private void reorderColumns()
{
Collections.sort(p_columns,new Comparator<ColumnInfo>() {
@Override
public int compare(ColumnInfo o1, ColumnInfo o2) {
return o1.getOrder()-o2.getOrder();
}
});
/* List<ColumnInfo> lc = new ArrayList<TableMapper.ColumnInfo>(p_columns);
for (ColumnInfo ci:lc)
{
if (!ci.getTableColumn().after().equals(""))
{
int indColumn = p_columns.indexOf(ci);
int indBefore = findColumnIndexByName(ci.getTableColumn().after());
if (indBefore != -1)
{
if (indBefore < indColumn)
{
List<ColumnInfo> subset = new ArrayList<TableMapper.ColumnInfo>(p_columns.subList(indColumn, p_columns.size()));
p_columns.removeAll(subset);
p_columns.addAll(indBefore+1, subset);
} else
{
List<ColumnInfo> subset = new ArrayList<TableMapper.ColumnInfo>(p_columns.subList(indColumn, indBefore));
p_columns.removeAll(subset);
p_columns.addAll(indColumn+1, subset);
}
}
}
}
*/
}
private void initializeColumnInfo()
{
addFieldsOfClass(p_class);
reorderColumns();
for (ColumnInfo ci: p_columns){
System.err.println("TableMapper Column: " + ci.getLabel());
}
}
public void setReadOnly(boolean readOnly)
{
this.p_readonly = readOnly;
}
public boolean getReadOnly()
{
return this.p_readonly;
}
public void addRow(Object row,Object before){
if (before != null){
int ind = this.p_rows.indexOf(before);
this.p_rows.add(ind, row);
fireTableRowsInserted(ind, ind);
} else {
this.p_rows.add(row);
fireTableRowsInserted(this.p_rows.indexOf(row),this.p_rows.indexOf(row));
};
}
public void addRows(Object[] rows,Object before){
int ind = (before != null) ? this.p_rows.indexOf(before) : p_rows.size();
int n = ind;
for (Object row: rows){
this.p_rows.add(n++, row);
}
fireTableDataChanged();
}
public void addRow(Object row)
{
addRow(row,null);
/*
this.p_rows.add(row);
//fireTableRowsInserted(getRowCount()-1, getRowCount()-1);
fireTableDataChanged();
if (TableMapperValue.class.isInstance(row)){
TableMapperValue tmv = (TableMapperValue)row;
tmv.assignTableMapper(this);
}
*/
}
public void removeRow(Object row)
{
int index = this.p_rows.indexOf(row);
if (index != -1)
{
if (TableMapperValue.class.isInstance(row)){
TableMapperValue tmv = (TableMapperValue)row;
tmv.assignTableMapper(this);
}
this.p_rows.remove(index);
fireTableRowsDeleted(index, index);
};
}
public Object getRow(int index)
{
return this.p_rows.get(index);
}
public <T> T getRow(int index,Class<T> clazz)
{
return (T)this.p_rows.get(index);
}
public Object appendNewInstance()
{
try
{
Object instance = this.p_class.newInstance();
if (instance != null)
addRow(instance);
return instance;
} catch (Exception ex)
{
return null;
}
}
@Override
public int getColumnCount() {
return this.p_columns.size();
}
@Override
public int getRowCount() {
return this.p_rows.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if ( (rowIndex < this.p_rows.size()) && (columnIndex < this.p_columns.size()))
{
try
{
return this.p_columns.get(columnIndex).getValue(this.p_rows.get(rowIndex));
} catch (Exception ex)
{
System.err.println("Exception: " + ex);
ex.printStackTrace();
}
}
return "";
}
@Override
public void setValueAt(Object aValue,
int rowIndex,
int columnIndex)
{
try
{
this.p_columns.get(columnIndex).setValue(this.p_rows.get(rowIndex), aValue);
fireValueChanged(rowIndex, columnIndex);
} catch (IllegalAccessException ex)
{
System.err.println("IllegalAccessException: " + ex);
ex.printStackTrace();
} catch (InvocationTargetException ex)
{
System.err.println("IllegalAccessException: " + ex);
ex.printStackTrace();
}
}
@Override
public boolean isCellEditable(int rowIndex,int columnIndex)
{
if (this.p_readonly)
return false;
return !this.p_columns.get(columnIndex).getReadOnly();
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
Class<?> c = this.p_columns.get(columnIndex).getColumnClass();
return c;
}
@Override
public String getColumnName(int columnIndex)
{
return this.p_columns.get(columnIndex).getLabel();
}
public <T> T getSelectedRow(Class<T> c){
return (T)getSelectedRow();
}
public Object getSelectedRow()
{
if (jTable.getSelectedRow()!=-1)
{
return this.p_rows.get(jTable.convertRowIndexToModel(jTable.getSelectedRow()));
}
return null;
}
public Object[] getSelectedRows()
{
int[] idx = jTable.getSelectedRows();
Object[] result = new Object[ idx.length ];
for (int i=0;i<result.length;i++){
result[i] = this.p_rows.get(jTable.convertRowIndexToModel(idx[i]));
}
return result;
}
public <T> T[] getSelectedRows(T[] template){
Object[] l = getSelectedRows();
T[] result = Arrays.copyOf(template, l.length);
for (int i=0;i<l.length;i++){
result[i] = (T)l[i];
}
return result;
}
public boolean exportToFile(File exportFile)
{
CSV csv = new CSV();
List<CSVRecord> cells = csv.getRecords();
CSVRecord header = new CSVRecord();
for (int i=0;i<getColumnCount();i++)
header.appendValue(p_columns.get(i).getLabel());
cells.add(header);
for (int row=0;row<getRowCount();row++)
{
CSVRecord record = new CSVRecord();
for (int column=0;column<getColumnCount();column++)
if (getValueAt(row, column)!=null)
record.appendValue(getValueAt(row, column).toString());
else
record.appendValue("");
cells.add(record);
}
csv.saveToFile(exportFile);
return true;
}
public boolean hasEditorOnPointer(){
return this.p_editoronpointer;
}
public void setEditorOnPointer(boolean b){
this.p_editoronpointer = b;
}
public void signalRowChanged(Object row){
if (p_rows.contains(row)){
fireTableRowsUpdated(p_rows.indexOf(row), p_rows.indexOf(row));
}
}
class TableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (colorSourceInterface != null){
c.setForeground( colorSourceInterface.getColor(getRow(row), column, hasFocus, isSelected) );
c.setBackground( colorSourceInterface.getBackgroundColor(getRow(row), column, hasFocus, isSelected) );
}
return c;
}
}
};