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

[Updated: Dec 26, 2017, Created: Jul 22, 2017]

This is an improvement of the last example. In the following example as well, a generic TableModel will be created automatically by using the JavaBean properties of the provided object and Java reflection. Additionally a new annotation DisplayAs will be used to specify various column properties such as 'display name' and 'index'.

Creating the TableModel

public class DisplayableTableModelCreator {

    public static <T> TableModel createTableModel(Class<T> beanClass,
                                                  DisplayableDataProvider<T> dataProvider ) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);

            Map<Integer, ColumnInfo> 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;
                columnInfoMap.put(columnInfo.index, columnInfo);
            }


            TableModel model = new AbstractTableModel() {
                @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 int getRowCount() {
                    return dataProvider.getTotalRows();
                }

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

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

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

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

    T getRowAtIndex(int rowIndex);
}

Following is the usage of above annotation:

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;
  }
}
public class ExampleMain {
  public static void main(String[] args) {
      JFrame frame = createFrame();
      TableModel tableModel = DisplayableTableModelCreator.createTableModel(Employee.class,
              getDisplayableDataProvider());
      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 DisplayableDataProvider<Employee> getDisplayableDataProvider() {
      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 new DisplayableDataProvider<Employee>() {
          @Override
          public int getTotalRows() {
              return list.size();
          }

          @Override
          public Employee getRowAtIndex(int rowIndex) {
              return list.get(0);
          }
      };
  }
}

Output

Example Project

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.3.9

Generate JTable model with an annotation Select All Download
  • table-model-generator-displayable
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also