/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jpa.tools.swing; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.SingularAttribute; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; /** * A data model for a tabular view of a list of persistent entities. * The data supplied by this model can be filtered to display field values of * basic type or single-valued or multi-valued relationships. * <br> * The meta-information about the attributes of the entity are supplied by * newly defined {@link Metamodel meta-model API} of JPA 2.0 specification. * * @author Pinaki Poddar * */ @SuppressWarnings("serial") public class EntityDataModel<T> extends AbstractTableModel implements TableModel, Iterable<T> { /** * Constant designates to include non-relation fields. */ public static int BASIC_ATTR = 1 << 0; /** * Constant designates to include single-valued relation fields. */ public static int ASSOCIATION_ATTR = 1 << 1; /** * Constant designates to include multi-valued relation fields. */ public static int PLURAL_ATTR = 1 << 2; /** * Constant designates to include all fields. */ public static int ALL_ATTR = BASIC_ATTR | ASSOCIATION_ATTR | PLURAL_ATTR; /** * Constant designates to show a row count field at the first column. */ public static int ROW_COUNT = 1 << 3; private List<String> columnNames; private List<Class<?>> columnClasses; private List<Method> methods; private List<T> data; private List<Attribute<? super T,?>> attributes; private static Object[] EMPTY_ARGS = null; private static Class<?>[] EMPTY_CLASSES = null; private boolean showsRowCount; private boolean showsBasicAttr; private boolean showsSingularAttr; private boolean showsPluralAttr; /** * Attributes of the entity are reordered with basic attributes, followed by singular * association followed by the many-valued attributes. */ public EntityDataModel(Class<T> cls, List<T> data, Metamodel meta, int styleBits) { super(); this.data = data; EntityType<T> entityType = meta.entity(cls); columnNames = new ArrayList<String>(); columnClasses = new ArrayList<Class<?>>(); attributes = new ArrayList<Attribute<? super T,?>>(); methods = new ArrayList<Method>(); showsRowCount = (styleBits & ROW_COUNT) != 0; showsBasicAttr = (styleBits & BASIC_ATTR) != 0; showsSingularAttr = (styleBits & ASSOCIATION_ATTR) != 0; showsPluralAttr = (styleBits & PLURAL_ATTR) != 0; Set<SingularAttribute<? super T, ?>> sAttrs = entityType.getSingularAttributes(); if (showsBasicAttr) { for (SingularAttribute<? super T, ?> attr : sAttrs) { if (!attr.isAssociation()) { attributes.add(attr); } } } if (showsSingularAttr) { for (SingularAttribute<? super T, ?> attr : sAttrs) { if (attr.isAssociation()) { attributes.add(attr); } } } if (showsPluralAttr) { Set<PluralAttribute<? super T, ?, ?>> pAttrs = entityType.getPluralAttributes(); for (PluralAttribute<? super T, ?, ?> attr : pAttrs) { attributes.add(attr); } } Collections.sort(attributes, new MetamodelHelper.AttributeComparator()); for (Attribute<? super T, ?> attr : attributes) { populate(cls, attr); } if (showsRowCount) { columnNames.add(0,"Row"); columnClasses.add(0, Long.class); attributes.add(0, null); methods.add(0, null); } } private void populate(Class<T> cls, Attribute<?,?> attr) { columnNames.add(attr.getName()); columnClasses.add(wrap(attr.getJavaType())); methods.add(getMethod(cls, attr.getName())); } /** * Gets the attribute at a given column index. * Can be null for 0-th index if row count is being shown. */ public Attribute<?,?> getAttribute(int columnIndex) { return attributes.get(columnIndex); } /** * Gets the entity represented in the given row. */ public T getRow(int row) { return data.get(row); } @Override public Class<?> getColumnClass(int columnIndex) { return columnClasses.get(columnIndex); } @Override public int getColumnCount() { return columnNames.size(); } @Override public String getColumnName(int columnIndex) { return columnNames.get(columnIndex); } @Override public int getRowCount() { return data.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { Method method = methods.get(columnIndex); if (method == null) { return columnIndex == 0 && showsRowCount ? rowIndex+1 : "?"; } Object row = data.get(rowIndex); Object val = getValueByReflection(rowIndex, row, columnIndex, method); return val; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } @Override public void setValueAt(Object value, int rowIndex, int columnIndex) { // should never be called } /** * Gets the value by reflection. */ Object getValueByReflection(int rowIndex, Object o, int columnIndex, Method m) { if (o == null) { System.err.println("Row " + rowIndex + " is null"); return null; } if (m == null) { System.err.println("Column " + columnIndex + ":" + getColumnName(columnIndex) + " method is null"); return null; } try { return m.invoke(o, EMPTY_ARGS); } catch (Exception e) { System.err.println("Can not extract value at [" + rowIndex + "," + columnIndex + "] due to " + e); e.printStackTrace(); } return null; } Class<?> wrap(Class<?> c) { if (c == null || c.isInterface() || c.isArray()) return Object.class; if (c.isPrimitive()) { if (c == boolean.class) return Boolean.class; if (c == byte.class) return Byte.class; if (c == char.class) return Character.class; if (c == double.class) return Double.class; if (c == float.class) return Float.class; if (c == int.class) return Integer.class; if (c == long.class) return Long.class; if (c == short.class) return Short.class; } return c; } private Method getMethod(Class<T> type, String p) { try { String getter = "get" + Character.toUpperCase(p.charAt(0))+p.substring(1); return type.getMethod(getter, EMPTY_CLASSES); } catch (Exception e) { e.printStackTrace(); } return null; } public void updateData(List<T> newData) { data = newData; fireTableDataChanged(); } @Override public Iterator<T> iterator() { return data.iterator(); } }