Close

Java Swing - Create JTable model by using JavaBean, reflection and an annotation for column properties

[Updated: Feb 2, 2018, Created: Jul 22, 2017]

This is an improvement of the last example. In the following example we will extend TableModel and map rows/columns based on JavaBean properties of the provided object and Java reflection. We will also use a new annotation DisplayAs that will be used to specify various column properties such as 'display name' and 'index'.

Creating the TableModel

public abstract class ObjectTableModel<T> extends AbstractTableModel {
    private List<T> objectRows = new ArrayList<>();

    public List<T> getObjectRows() {
        return objectRows;
    }

    public void setObjectRows(List<T> objectRows) {
        this.objectRows = objectRows;
    }

    @Override
    public int getRowCount() {
        return objectRows.size();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        T t = objectRows.get(rowIndex);
        return getValueAt(t, columnIndex);
    }

    public abstract Object getValueAt(T t, int columnIndex);

    @Override
    public abstract String getColumnName(int column);

    public abstract String getFieldName(int column);
}
public class DisplayableObjectTableModel<T> extends ObjectTableModel<T> {
    private Map<Integer, ColumnInfo> columnInfoMap;

    public DisplayableObjectTableModel(Class<T> tClass) {
        init(tClass);
    }

    private void init(Class<T> tClass) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(tClass);
            this.columnInfoMap = new HashMap<>();
            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                Method m = pd.getReadMethod();
                DisplayAs displayAs = m.getAnnotation(DisplayAs.class);
                if (displayAs == null) {
                    continue;
                }
                ColumnInfo columnInfo = new ColumnInfo();
                columnInfo.displayName = displayAs.value();
                columnInfo.index = displayAs.index();
                columnInfo.method = m;
                columnInfo.propertyName = pd.getName();
                columnInfoMap.put(columnInfo.index, columnInfo);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getValueAt(T t, int columnIndex) {
        try {
            return columnInfoMap.get(columnIndex)
                    .method.invoke(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getColumnCount() {
        return columnInfoMap.size();
    }

    @Override
    public String getColumnName(int column) {
        ColumnInfo columnInfo = columnInfoMap.get(column);
        if (columnInfo == null) {
            throw new RuntimeException("No column found for index " + column);
        }
        return columnInfo.displayName;
    }

    @Override
    public String getFieldName(int column) {
        ColumnInfo columnInfo = columnInfoMap.get(column);
        return columnInfo.propertyName;
    }

    public Class<?> getColumnClass(int columnIndex) {
        ColumnInfo columnInfo = columnInfoMap.get(columnIndex);
        return columnInfo.method.getReturnType();
    }

    private static class ColumnInfo {
        private Method method;
        private int index;
        private String displayName;
        public String propertyName;
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DisplayAs {
  String value();
  int index();
}

The Example Bean

public class Employee {
  private String name;
  private String dept;
  private String phone;
  private String cellPhone;

  @DisplayAs(value = "Employee Name", index = 1)
  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  @DisplayAs(value = "Department", index = 0)
  public String getDept() {
      return dept;
  }

  public void setDept(String dept) {
      this.dept = dept;
  }

  @DisplayAs(value = "Work Phone", index = 2)
  public String getPhone() {
      return phone;
  }

  public void setPhone(String phone) {
      this.phone = phone;
  }

  @DisplayAs(value = "Cell Phone", index = 3)
  public String getCellPhone() {
      return cellPhone;
  }

  public void setCellPhone(String cellPhone) {
      this.cellPhone = cellPhone;
  }
}

Main class

public class ExampleMain {
  public static void main(String[] args) {
      JFrame frame = createFrame();
      ObjectTableModel<Employee> tableModel = new DisplayableObjectTableModel<>(Employee.class);
      tableModel.setObjectRows(getEmployees());
      JTable table = new JTable(tableModel);
      JScrollPane pane = new JScrollPane(table);
      frame.add(pane);
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
  }

  private static JFrame createFrame() {
      JFrame frame = new JFrame("JTable example");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(new Dimension(600, 300));
      return frame;
  }

  public static List<Employee> getEmployees() {
      final List<Employee> list = new ArrayList<>();
      for (int i = 1; i <= 30; i++) {
          Employee e = new Employee();
          e.setName("name" + i);
          e.setPhone("phone" + i);
          e.setDept("dept" + i);
          e.setCellPhone("cell" + i);
          list.add(e);
      }
      return list;
  }
}

Output

Example Project

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.3.9

Creating JTable model with annotation Select All Download
  • table-model-generator-displayable
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • DisplayableObjectTableModel.java

    See Also