how to create table for any java type

Explanation
Imagine that your have a lot of tables. Millions.. well... tens... Still its boring to create table after table, page after page. Why not thing about something generic, dynamic?
We all know 'dataTable' component. Its about show some table with some data. Most programmers found the 'h:dataTable' component hard to customize, difficult to understand and to maintain. In this case almost impossible to create 'generic' table.
Lets see how to overcame this problem, using simple loop in loop and reflection!

New to Java, JSF, Icefaces? Here is some links to start with:
Why and when use ICEFACES?
First steps with jsf and java

We use simplest HTML tags, together with "ui:repeat" tag, which loops on 'tr' element. Then loop on 'column' inside 'tr' element.
Customization is simple too. Style of 'td' element can be dynamic.
Use you favorite HTML editor, or JSF editor together with CSS tools to customize every aspect of this table, its easy!
fragment from TableForAnyJavaType.jspx
	<table> 
		<caption>table for any java type</caption> 
		<tr> 
			<ui:repeat value="#{tableForAnyJavaTypeBean.columnsTitles}" 
				var="title"> 
				<td><h:outputText value="#{title}"></h:outputText></td> 
			</ui:repeat> 
 
		</tr> 
		<!-- using double loop, first on rows, then on cells --> 
		<ui:repeat value="#{tableForAnyJavaTypeBean.dynamicData}" var="dynamicRow"> 
			<tr> 
				<ui:repeat value="#{dynamicRow}" var="cell"> 
					<td class="#{cell.style}"><h:outputText value="#{cell.value}"></h:outputText></td> 
				</ui:repeat> 
			</tr> 
		</ui:repeat> 
	</table> 

This is main entry point. Its referenced by 'tableForAnyJavaTypeBean' from faces-config.xml In this case we use simple reflection to collect data from getters methods. The java type of 'row' is a parameters and can set in constructor or via setter.
com.gpost.jsfexamples.tableForAnyJavaType.TableForAnyJavaTypeBean
package com.gpost.jsfexamples.tableForAnyJavaType;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.gpost.jsfexamples.tableWithLoop.ManWithPhone;

/*
* Java class for example 'table for any java type'
* referenced as 'tableForAnyJavaTypeBean' from faces-config.xml, 
* used in 'TableForAnyJavaType.jspx'
*/
public class TableForAnyJavaTypeBean{
	// javaType of table row
	Class javaType;
	
	List<Object> rows = null;
	
	List<String> columnsTitles = null;

	// dynamic data
	List<List<Cell>> dynamicData = null;
	
	public TableForAnyJavaTypeBean() {
		
		// for example only use this as default
		this(ManWithPhone.class); 
	}
	
	public TableForAnyJavaTypeBean(Class javaType) {
		super();
		this.javaType = javaType;
		
		// find out names of the getters and use them as titles
		columnsTitles = new ArrayList<String>();
		Method []methods = javaType.getDeclaredMethods();
		for (Method method : methods) {
			if (method.getName().startsWith("get")) {
				// cut 'get'
				String title = method.getName().substring(3);
				
				// the order of title is defined from java type.
				// use annotation to define real order if you need,
				// or sort title by any other criteria
				columnsTitles.add(title);
			}
		}
	}

	
	public List<Object> getRows() {
		if (rows == null) {
			rows = new ArrayList();
			// create same sample data...
			rows.add(new ManWithPhone("Gary", "1234-6767"));
			rows.add(new ManWithPhone("Bob", "5678-4553"));
		}
		return rows;
	}
	
	public List<String> getColumnsTitles() {
		return columnsTitles;
	}

	/*
	 * return double list of cells. exception handling can be improved.
	 */
	public List<List<Cell>> getDynamicData()  {
		if (dynamicData == null) {
			dynamicData = new ArrayList<List<Cell>>();
			
			for (Object someObject : getRows()) {
				List<Cell> cells = new ArrayList<Cell>();
				dynamicData.add(cells);
				
				// get value in the same order as defined in 'columnsTitles' variable
				for (String title : columnsTitles) {
					Method getter;
					try {
						// here we use reflection
						getter = someObject.getClass().getMethod("get"+title, null);
						Object value = getter.invoke(someObject, null);
						
						Cell cell = new Cell();
						cell.setValue(""+value);
						cell.setStyle(""+title);
						
						cells.add(cell);
					} catch (SecurityException e) {
						e.printStackTrace();
					} catch (NoSuchMethodException e) {
						e.printStackTrace();
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return dynamicData;
	}

	public Class getJavaType() {
		return javaType;
	}

	public void setJavaType(Class javaType) {
		this.javaType = javaType;
	}

} // end of class

This class defines data in row.
com.gpost.jsfexamples.tableForAnyJavaType.ManWithPhone
package com.gpost.jsfexamples.tableForAnyJavaType;

public class ManWithPhone {
	public ManWithPhone(String name, String phone) {
		super();
		this.name = name;
		this.phone = phone;
	}

	String name;
	
	String phone;

	public String getName() {
		return name;
	}

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

	public String getPhone() {
		return phone;
	}

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

This class defines data in row.
com.gpost.jsfexamples.tableForAnyJavaType.Cell
package com.gpost.jsfexamples.tableForAnyJavaType;

/*
 * contains value of cell and style
 */
public class Cell {
	String value;
	String style;
	
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
	public String getStyle() {
		return style;
	}
	public void setStyle(String style) {
		this.style = style;
	}
}


part of faces-config.xml
<managed-bean>
 <managed-bean-name>tableForAnyJavaTypeBean</managed-bean-name>
 <managed-bean-class>com.gpost.jsfexamples.tableForAnyJavaType.TableForAnyJavaTypeBean</managed-bean-class>
 <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

conclusion
As we saw in this example, you can create perfect table for any java type using simple html/css, then add some binding to your java beans and you get very flexible, easily customizable data table without complexity of 'h:dataTable' component. Every aspect of table-tr-td combination can be dynamic, controlled by your business logic or in every other way.

No comments:

Post a Comment