Sumário
1.1 Introdução
1.2 Formatadores customizados.
1.3 Métodos da interface List.
1.4 Alterando os valores e pegando objetos do modelo.
2. Avançado
2.1 FieldResolver
2.2 FieldHandler e MethodHandler
3.Para saber mais
import mark.utils.el.annotation.Resolvable;
public class Person {
@Resolvable(colName = "Name")
private String name;
@Resolvable(colName = "Age", formatter = IntFormatter.class)
private int age;
private Person parent;
public Person(String name, int age) {
this(name, age, null);
}
public Person(String name, int age, Person parent) {
this.name = name;
this.age = age;
this.parent = parent;
}
//Getters and setters ommited
}
E o código para criarmos a tabela é apenas este.
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import mark.utils.el.annotation.AnnotationResolver;
import mark.utils.swing.table.ObjectTableModel;
import test.Person;
public class ObjectTableModelDemo {
public void show() {
//Here we create the resolver for annotated classes.
AnnotationResolver resolver = new AnnotationResolver(Person.class);
//We use the resolver as parameter to the ObjectTableModel
//and the String represent the cols.
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(
resolver, "name,age");
//Here we use the list to be the data of the table.
tableModel.setData(getData());
JTable table = new JTable(tableModel);
JFrame frame = new JFrame("ObjectTableModel");
JScrollPane pane = new JScrollPane();
pane.setViewportView(table);
pane.setPreferredSize(new Dimension(400,200));
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Just for create a default List to show.
private List<PERSON> getData() {
List<PERSON> list = new ArrayList<PERSON>();
list.add(new Person("Marky", 17, new Person("Marcos", 40)));
list.add(new Person("Jhonny", 21));
list.add(new Person("Douglas", 50, new Person("Adams", 20)));
return list;
}
public static void main(String[] args) {
new ObjectTableModelDemo().show();
}
}
[sourcecode language="Java"]
AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(
resolver, "name,age,parent.name,parent.age");
[/sourcecode]
Se voce usar "parent.name" voce ve o nome do parent na tabela.
[sourcecode language="Java"] AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(
resolver, "name:Person Name,age:Person Age,parent.name:Parent Name,parent.age:Parent Age");
[/sourcecode]
1.2 Formatadores customizados
Na maioria dos casos apenas String é o suficiente para vermos a correta visualização do campo.
[sourcecode language="Java"]
java.util.GregorianCalendar[time=-367016400000,areFieldsSet=true,areAllFieldsSet=
true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Sao_Paulo",offset=
-10800000,dstSavings=3600000,useDaylight=true,transitions=129,lastRule=java.util.
SimpleTimeZone[id=America/Sao_Paulo,offset=-10800000,dstSavings=3600000,useDaylight=
true,startYear=0,startMode=3,startMonth=9,startDay=15,startDayOfWeek=1,startTime=0,
startTimeMode=0,endMode=3,endMonth=1,endDay=15,endDayOfWeek=1,endTime=0,endTimeMode=
0]],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=1,YEAR=1958,MONTH=4,WEEK_OF_YEAR=
20,WEEK_OF_MONTH=3,DAY_OF_MONTH=16,DAY_OF_YEAR=136,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH
=3,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=
-10800000,DST_OFFSET=0]
[/sourcecode]
Isto não é agradavel para ver.
[sourcecode language="Java"]
package mark.utils.bean;
/**
public interface Formatter {
/**
* Convert a object to String.
*/
public abstract String format(Object obj);
/**
* Convert the String to the Object.
*/
public abstract Object parse(String s);
/**
* Naming proposes only
*/
public abstract String getName();
}
[/sourcecode]
Podemos setar o formatador na anotação @Resolvable, e aqui está minha implementação para a classe Calendar.
[sourcecode language="Java"]
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import mark.utils.bean.Formatter;
public class CalendarFormatter implements Formatter {
private final static SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
@Override
public String format(Object obj) {
Calendar cal = (Calendar) obj;
return formatter.format(cal.getTime());
}
@Override
public String getName() {
return "calendar";
}
@Override
public Object parse(String s) {
Calendar cal = new GregorianCalendar();
try {
cal.setTime(formatter.parse(s));
} catch (ParseException e) {
e.printStackTrace();
}
return cal;
}
}
[/sourcecode]
Voltando a nossa classe Person vamos criar um campo Calendar chamado birth para nossa tabela.
[sourcecode language="Java"]
@Resolvable(formatter = CalendarFormatter.class)
private Calendar birth;
[/sourcecode]
No segundo parametro da ObjectTableModel podemos passar a seguinte String: "name,age,birth".
Eu coloquei métodos da interface List no modelo por que isso torna simples trabalhar com os objetos assim como nas listas.
[sourcecode language="Java"]
ObjectTableModel<PERSON> model = new ObjectTableModel<PERSON>(new AnnotationResolver(Person.class).resolve("name,age"));
Person person1 = new Person("Marky", 17);
Person person2 = new Person("MarkyAmeba", 18);
model.add(person1);
model.add(person2);
List<PERSON> list = new ArrayList<PERSON>();
list.add(new Person("Marcos", 40));
list.add(new Person("Rita", 40));
model.addAll(list);
int index = model.indexOf(person2);// Should return 2
model.remove(index);//Delete with the index
model.remove(person1);//Delete with the object
model.clean();//Clean the model
[/sourcecode]
1.4 Alterando e recuperando objetos do modelo
É claro, uma tabela não é exclusivamente para mostrar dados. No modelo tem um método chamado setEditDefault que recebe um valor boolean e o método isEditable(int x, int y) retorna esse valor. (Isso significa que se está setado true, toda tabela é editabel. Caso falso, toda tabela não será editavel.
[sourcecode language="Java"]
import mark.utils.el.annotation.Resolvable;
import mark.utils.el.handler.MethodHandler;
public class Person {
@Resolvable(colName = "Name")
private String name;
@Resolvable(colName = "Age", formatter = IntFormatter.class)
private int age;
private Person parent;
public Person(String name, int age, Person parent) {
this.name = name;
this.age = age;
this.parent = parent;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static class IntFormatter implements Formatter {
@Override
public String format(Object obj) {
return Integer.toString((Integer) obj);
}
@Override
public String getName() {
return "int";
}
@Override
public Object parse(String s) {
return Integer.parseInt(s);
}
}
}
[/sourcecode]
O exemplo:
[sourcecode language="Java"]
package test.el.annotation;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import mark.utils.el.annotation.AnnotationResolver;
import mark.utils.swing.table.ObjectTableModel;
import test.Person
public class AnnotationResolverTest {
public void testAnnotationResolverInit() {
AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(resolver, "name,age,parent.name:Parent,parent.age:Parent age");
tableModel.setData(getData());
tableModel.setEditableDefault(true);
JTable table = new JTable(tableModel);
JFrame frame = new JFrame("ObjectTableModel");
JScrollPane pane = new JScrollPane();
pane.setViewportView(table);
pane.setPreferredSize(new Dimension(400, 200));
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private List<PERSON> getData() {
List<PERSON> list = new ArrayList<PERSON>();
list.add(new Person("Marky", 17, new Person("Marcos", 40)));
list.add(new Person("Jhonny", 21, new Person("",0)));
list.add(new Person("Douglas", 50, new Person("Adams",20)));
return list;
}
public static void main(String[] args) {
new AnnotationResolverTest().testAnnotationResolverInit();
}
}
[/sourcecode]
Qualquer alteração nas células ira fazer o update no objeto.
Recuperando o objeto do modelo.
A pior parte trabalhando com JTables é para recuperar os valores. Quase todo tempo temos que usar o getValueAt e setar no atributo correspondente do objeto. Mas o objetivo desse projeto é fazer isso virar passado.
AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(resolver, "name,age,parent.name:Parent,parent.age:Parent age");
tableModel.setData(getData());
tableModel.setEditableDefault(true);
Person person = tableModel.getValue(2);//The row
System.out.println(person.getName());
[/sourcecode]
2. Avançado
Tudo visto até aqui já é o suficiente para utilizar o modelo. Mas ainda assim é possivel extender mais ainda as funcionalidades da tabela para servir a qualquer proposito.
2.1 FieldResolver
Todo plano de fundo desse projeto está nessa classe.E a anotação @Resolvable e a classe AnnotationResolver são apenas para criar FieldResolvers.
[sourcecode language="Java"]
FieldResolver nameResolver = new FieldResolver(Person.class, "name");
FieldResolver ageResolver = new FieldResolver(Person.class, "age");
ageResolver.setFormatter(new IntFormatter());
FieldResolver parentNameResolver = new FieldResolver(Person.class,"parent.name", "Parent");
FieldResolver parentAgeResolver = new FieldResolver(Person.class,"parent.age", "Parent age");
FieldResolver birthResolver = new FieldResolver(Person.class, "birth","Birth day");
birthResolver.setFormatter(new CalendarFormatter());
ObjectTableModel<PERSON> model = new ObjectTableModel<PERSON>(
new FieldResolver[] { nameResolver, ageResolver, parentNameResolver, parentAgeResolver, birthResolver });
[/sourcecode]
É equivalente ao seguinte.
[sourcecode language="Java"]
AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(
resolver, "name,age,parent.name:Parent,parent.age:Parent age,birth: Birth day");
tableModel.setData(getData());
[/sourcecode]
Mas no primeiro caso não precisamos das anotações @Resolvable nos campos da classe.
FieldResolverFactory
A classe FieldResolverFactory foi feita apenas para facilitar a criação de FieldResolvers. Seu construtor recebe um Class que representa a classe que criaremos os FieldResolvers.(O mesmo que passamos como argumento para o FieldResolver)
createResolver(String fieldName).
createResolver(String fieldName, String colName).
createResolver(String fieldName, Formatter formatter).
createResolver(String fieldName, String colName, Formatter formatter).
[sourcecode language="Java"]
FieldResolverFactory fac = new FieldResolverFactory(Person.class);
FieldResolver nameRslvr = fac.createResolver("name");
FieldResolver ageRslvr = fac.createResolver("age", new IntFormatter());
FieldResolver parentNameRslvr = fac.createResolver("paren.name","Parent");
FieldResolver parentAgeRslvr = fac.createResolver("parent.age","Parent age", new IntFormatter());
FieldResolver birthRslvr = fac.createResolver("birth", "Birth day", new CalendarFormatter());
ObjectTableModel<PERSON> model = new ObjectTableModel<PERSON>(
new FieldResolver[] { nameRslvr, ageRslvr, parentNameRslvr, parentAgeRslvr, birthRslvr });
[/sourcecode]
Até aqui nós estamos usando o FieldAccessHandler padrão, o FieldHandler.
Usando o MethodHandler ele procura na classe os métodos getter(ou is)/setters para utilizar para setar e pegar os valores.
[sourcecode language="Java"]
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import mark.utils.el.annotation.Resolvable;
import mark.utils.el.handler.MethodHandler;
public class Person {
@Resolvable(colName = "Name", accessMethod = MethodHandler.class)
private String name;
@Resolvable(colName = "Age", formatter = IntFormatter.class)
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return "The name is: " + name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return 150;
}
public void setAge(int age) {
this.age = age;
}
}
[/sourcecode]
E o ObjectTableModel.
[sourcecode language="Java"]
AnnotationResolver resolver = new AnnotationResolver(Person.class);
ObjectTableModel<PERSON> tableModel = new ObjectTableModel<PERSON>(resolver, "name,age");
tableModel.setData(getData());
[/sourcecode]
Rodando esse exemplo, notamos que todas as células da coluna name começa com "The name is:" por que isto está como retorno para o método getName e usamos o MethodHandler nesse campo. Mas para o getAge que sempre retorna 150 notamos os valores atuais por que ele ainda usa o FieldHandler.
3. Pontos de Interesse
Reflection é incrível. Vejam o pacote mark.utils.el e seus subpacotes para ver toda Reflection implementada para esse projeto.