I want to create a Swingtable with a dynamic Layout, regarding the class data that is set as sources.
To be more specific:
I have a class with multiple attributes. When creating the table, I want to do it, so that the table looks: "which public getFunction with returntyp String are available" and use the attributes behind this functions as Columnnames and later also as source for the rows.
That is working at the moment.
My problem now is:
How can I ensure a specific order of my Columns with this approach?
for example i have a column "ID","callsign","categorie".
I want to display them in this order.
No metter how i order the methodes in sourceCode, the columns are allways in the same order ("ID","categorie","callsign").
java.lang.reflect.Method methodes[] = null;
methodes = classObjectOfT.getMethods();
List<String> tempList=new ArrayList<String>();
for (java.lang.reflect.Method m: methodes)
{
if (m.getReturnType().equals(String.class)&&m.getName().startsWith("get"))
{
tempList.add(m.getName().substring(3));
}
}
columnNames=(String[]) tempList.toArray(new String[tempList.size()]);
above is the code i use for retriving the columnames.
A workaround would be to name the attributes/getMethodes "ID_00","Callsign_01","Categorie_02" and do the ordering by using the last 2 chars of the String, but that would be rather ugly and i'm searching for a cleaner solution.
I would suggest creating an annotation which you would use to define the order and even more meta data for your table columns, for example a label:
#Retention(RetentionPolicy.RUNTIME)
#interface TableColumn {
String label();
int order();
}
Then retrieve them like this:
public Set<Method> findTableColumsGetters(Class<TestTableData> clazz) {
Set<Method> methods = new TreeSet<>(new Comparator<Method>() {
#Override
public int compare(Method o1, Method o2) {
return Integer.valueOf(o1.getAnnotation(TableColumn.class).order())
.compareTo(o2.getAnnotation(TableColumn.class).order());
}
});
for(Method method : clazz.getMethods()) {
if(method.isAnnotationPresent(TableColumn.class)) {
methods.add(method);
}
}
return methods;
}
Here is some testings:
Test table data
static class TestTableData {
private String id, callsign, categorie;
#TableColumn(label = "Caterogy", order = 3)
public String getCategorie() {
return categorie;
}
public void setCategorie(String categorie) {
this.categorie = categorie;
}
#TableColumn(label = "Call sign", order = 2)
public String getCallsign() {
return callsign;
}
public void setCallsign(String callsign) {
this.callsign = callsign;
}
#TableColumn(label = "ID", order = 1)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Test:
#Test
public void findTableColumsGetters() {
Set<Method> getters = findTableColumsGetters(TestTableData.class);
for(Method getter : getters) {
TableColumn annotation = getter.getAnnotation(TableColumn.class);
System.out.printf("%d %s (%s)%n", annotation.order(), annotation.label(), getter.getName());
}
}
Output:
1 ID (getId)
2 Call sign (getCallsign)
3 Caterogy (getCategorie)
I would suggest though that you don't retrieve the annotation reach time you need info from it, instead create a Metadata class for your methods where you put everyting including the method itself while you are performing the search.
Related
I have a predicate that I use to filter a list of the same Entity Object:
Predicate<DWHDeal> companyFilter = i -> i.getCompany().equals(company);
I also have to apply the same filter, with the exact same condition on the exact same field, on a list of DTOs where the DTOS is built based on the entity from before:
Predicate<DWHDealDTO> companyFilterDTO = i -> i.getCompany().equals(company);
Is it possible to achieve this without instancing two different predicates? If possible, I would like to achieve this by making only one Predicate.
Assuming getCompany() returns a String you could create Predicate<String>:
Predicate<String> predicate = s -> s.equals(company);
And then using it like:
list.stream()
.filter(dto -> predicate.test(dto.getCompany()))
...
But there is not much benefit since it requires almost the same code.
If equality is only check then you can use static Predicate isEqual(Object targetRef). see java doc https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#isEqual-java.lang.Object-
class StudentView{
String name;
public StudentView(String name) {
this.name = name;
}
}
class StudentDTO{
String name;
public StudentDTO(String name) {
this.name = name;
}
}
public void testPredicate(){
StudentView studentView= new StudentView("John");
StudentDTO studentDTO = new StudentDTO("Sam");
Predicate p = Predicate.isEqual("John");
System.out.println("Test for Student View "+ p.test(studentView.name));
System.out.println("Test for Student DTO "+ p.test(studentDTO.name));
}
I think you will need a Function<T,R> before using Predicate :
There are two concepts to Function. First is a java.util.function.Function which accepts one argument and produces a result. The second is stream intermediate operation map which converts each element in a stream into another object via the supplied function.
In your case the Function should look like :
Function<DWHDeal, DWHDealDTO> myFunction = new Function<DWHDeal, DWHDealDTO>() {
public DWHDealDTO apply(DWHDeal t) {
return ... ;
}
};
I tried the basic Program as below with success:
static class DWHDeal{
String name;
public DWHDeal(String name) {
this.name = name;
}
}
static class DWHDealDTO{
String name;
public DWHDealDTO(String name) {
this.name = name;
}
}
static Predicate<DWHDealDTO> companyFilter = i -> i.name.equalsIgnoreCase("com");
public static void main(String[] args) {
Function<DWHDeal, DWHDealDTO> myFunction = new Function<DWHDeal, DWHDealDTO>() {
public DWHDealDTO apply(DWHDeal t) {
return new DWHDealDTO("com");
}
};
DWHDeal newDWHDealDTOObj = new DWHDeal("com");
System.out.println(companyFilter.test(myFunction.apply(newDWHDealDTOObj))); //Works
}
As suggested in the comments, the common interface would be the preferred solution.
I guess you could do something like this, but to be fair, it is ugly.
private String getCompany(Object o) {
if(o instanceof DWHDeal)
return ((DWHDeal) o).getCompany();
else
return ((DWHDealDTO) o).getCompany();
}
Predicate<Object> companyFilter = i -> getCompany(i).equals(company);
I'm implementing server side processing for jQuery datatables. For those of you who are unfamiliar with it, the plugin allows you to sort a column asc/desc, as well as search all columns with a single textbox. Since my list of objects is too large to send to the client, i need to replicate it's sorting functionality thru Java.
This is the object that i'm working with. Each field is a column in the client side table. All fields are either Strings or primitives/wrappers.
public class MyObject{
String id;
String productType;
String productGroup;
double totalSales;
double salesVariance;
int vendorId;
String vendorName;
}
I need to be able to sort by any of the fields, ascending/descending, WITHOUT hard coding a comparator function for each field.
Given a string that represents a field name, how would I implement a generic sorting function?
My current solution is to process the list with Nashorn... :)
Java method that calls Nashorn:
/**
* #param sortBy - field name
* #param sortDirection - asc/desc
*/
public void applyFilteringChanges(List<MyObject> myObjects, String sortBy, String sortDirection) throws Exception{
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("sortObjects", myObjects, sortBy, sortDirection);
}
Nashorn code:
function sortObjects(myObjects, prop, direction) {
var dir = (direction === 'asc') ? 1 : -1;
myObjects.sort(function(a,b){
return compare(a,b,prop) * dir;
})
};
function compare(a,b,prop){
if(a[prop] < b[prop])
return -1;
else if(a[prop] > b[prop])
return 1;
return 0;
}
I also dabbled in Reflection, but it's incomplete at the moment.
public void applyFilteringChanges(List<MyObject> myObjects, String sortBy, String sortDirection) throws Exception{
myObjects.sort((s1,s2)->{
Field field;
try {
field = s1.getClass().getDeclaredField(sortBy);
Class<?> type = field.getType();
if(type.isPrimitive()){
//deal with primitive
}else{
Comparable o1FieldValue = (Comparable) field.get(s1);
Comparable o2FieldValue = (Comparable) field.get(s2);
return o1FieldValue.compareTo(o2FieldValue);
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
});
}
Both of my approaches feel like hacks, is there a standard way to accomplish this?
You can create a generic Comparator like this :
class MyComparator<T extends MyObject> implements Comparator<T> {
private String field;
public MyComparator(String field) {
this.field = field;
}
#Override
public int compare(T o1, T o2) {
switch (field) {
case "id" :
return o1.id.compareTo(o2.id);
case "productType":
return o1.productType.compareTo(o2.productType);
case "productGroup":
return o1.productGroup.compareTo(o2.productGroup);
//...
}
return 0;
}
}
Use it like this :
public static void main(String[] args)
{
List<MyObject> objects = new ArrayList<>();
Collections.sort(objects, new MyComparator<>("id"));
}
I would like to define a method and by passing the enum, returns the mapped type based on the enum. So far I only work out this way:
public class Person {
HashMap<String, Object> mData;
void int getDetail(DetailInt detail){
Object data = mData.get(detail.name());
if(data instanceof Integer)
return (int)data;
return 0;
}
void String getDetail(DetailStr detail){
Object data = mData.get(detail.name());
if(data instanceof String)
return (String)data;
return "";
}
}
public enum DetailInt {
Age("age"), Weight("weight"), Height("height");
String columnName;
DetailInt(String columnName){
this.columnName= columnName;
}
}
public enum DetailStr {
FirstName("first_name"), LastName("last_name");
String columnName;
DetailStr (String columnName){
this.columnName= columnName;
}
}
So I can use the same method, but passing different enums to get the data with the type.
int age = person.getDetail(DetailInt.Age);
String firstName = person.getDetail(DetailStr.FirstName);
Now, what I would like to achieve is to merge both enums together, so I can call as below:
int age = person.getDetail(Detail.Age);
String firstName = person.getDetail(Detail.FirstName);
It is neater. However, I have tried generic type and interface, still cannot find the way to do it. Use below way is similar to what I want but this is not enum type.
abstract class Detail {
}
class DetailStr extend Detail {
}
interface Details {
DetailStr firstName = new DetailStr("first_name");
DetailStr lastName = new DetailStr("las_name");
DetailInt age = new DetailInt("age");
DetailInt weight = new DetailInt("weight");
DetailInt height = new DetailInt("height");
}
public class Person {
void int getDetail(DetailInt detail){
....
}
void String getDetail(DetailStr detail){
....
}
}
You can't do this in Java.
This is because a particular value of an enumerator has the same type as any other value of that enumerator. It's therefore not possible to construct an overloaded function since there's no type difference to act as a descriminator. (You cannot overload a function by return type difference alone.)
The obvious solution is to have two methods getDetailAsInt and getDetailAsString.
I'll share this approach that does not use enums, but it might be of some use to you:
public class Key<T> {
private String key;
...
}
public class Keys {
public static final Key FIRST_NAME = new Key<String>("first_name");
public static final Key AGE = new Key<Integer>("age");
}
public class Person {
public <T> T getDetail(Key<T> key) {
Object detail = mData.get(key.getKey());
return (T) detail;
}
}
I'm afraid it might not be possible to convert it to use enums, so you'd have to ensure no unwanted keys are created in some other way (package-private constructor etc.)
i have a domain class(DB):
public class PersonDoamin {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
i also have model class:
public class PersonBean extends PersonDoamin {
}
so when i go to DAOImpl class and query for List and transfer this list to List and return to users as i have interface method for List getAllPerson(). so my questions is here when i transfer all data from List. Here i have some utility method that copies from one bean to another like this:
List<PersonDoamin> list = PersonDAO.getAllPersons();
List<PersonBean> pbList = new ArrayList<PersonBean>();
/* this below logic is pretty much in the all DAO impl*/
for(PersonDoamin p : list){
PersonBean pb = new PersonBean();
CopyHelper.copyBean(p, pb);
pbList.add(pb);
}
return pbList;
can we replace the looping and copying and adding to another list and returning part with somekind of generic method which will take any object two list and loop thorugh one and add it to another passed List parameter and return it. something like below which is not perfect right now:
public static <T> List<T> listToArray(List<T> list,List<T> list2) {
for(T element : list){
list2.add(element);
}
return list2;
}
public static void main(String[] args) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName("aj");
p.setAge("25");
personList.add(p);
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
Test.listToArray(personList , personBeansToReturn );
}
A bit off topic, your design seems a bit weird that you have "Domain" class and "Bean" class and have "Bean" extends "Domain"...
Anyway, come back to your question, what you are trying to do is:
You have a List<Domain>
You want to transform each Domain in the List into a Bean (by use of some util method)
Put the resulting Beans into a list and return
Let's go through it step by step.
(by the way, the listToArray method you wrote does not align with your original loop as it does not do the transformation (point 2). I guess it is typo?)
(all psuedo code as I don't have environment on hand to make it compile. Concept should be correct I guess)
Step 1: Util method for Person
One biggest problem of your original util method is that, it is illegal to put a Parent object instance to a List of Child (it should be easy to figure why by yourself).
The util method should look like this:
List<PersonBean> toBeans(List<PersonDomain> domains) {
List<PersonBean> beans = new ArrayList<>(domains.size());
for (PersonDomain domain: domains) {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
beans.add(bean);
}
return beans;
}
Step 2: Make it generic
The problem above is that it only works for Person. If you want to make it generic, you will also need to provide the function to transform Domain to Bean:
(Assume you are using Java8, should be trivial to make your own interface if you are using older version)
<D,B> List<B> toBeans(List<D> domains, Function<B,D> mapper) {
List<PersonBean> beans = new ArrayList<>(domains.size());
for (PersonDomain domain: domains) {
beans.add(mapper.apply(domain));
}
return beans;
}
so that you can use it by:
return toBeans(personDomains, (domain) -> {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
return bean;
});
(You may consider wrap the function if in most case you are going to use the CopyHelper way)
<D,B> List<B> toBeansByBeanCopy(List<D> domains, Class<B> beanClass) {
return toBeans(domains, (domain)-> {
B bean = beanClass.newInstance();
CopyHelper.copyBean(domain, bean);
return bean;
});
}
so that you can use it as
return toBeansByBeanCopy(personDomains, PersonBean.class);
Step 3: Java has done it for you
Actually what you are trying to do above, it is already provided by Java in Java 8. You can simply do:
return personDomains.stream()
.map(d -> {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
return bean;
})
.collect(Collectors.toList());
You may write a little method to use in the lambda expression if it is the standard way.
return personDomains.stream()
.map(BeanMapper.mapper(PersonBean.class))
.collect(Collectors.toList());
(Leave the implementation as your exercise)
If you're looking for a way to call new on a generic type, you can, sort of. You have to use reflection and call newInstance on the Class object. I don't know if this is going to be feasible for you.
Also, I don't see anyway of realistically implementing your bean copy method without using some heavy reflection as well. In the example below I faked by just casting to the required classes.
public class GenericCopyTest
{
public static void main( String[] args ) throws Exception
{
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName( "aj" );
p.setAge( "25" );
personList.add( p );
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
copyAndDowncast( personList, personBeansToReturn, PersonBean.class );
System.out.println( personBeansToReturn );
}
public static <T,U extends T> List<U> copyAndDowncast( List<T> from,
List<U> to, Class<U> type )
throws InstantiationException, IllegalAccessException
{
for( T element : from ) {
U nu = type.newInstance();
copyBean( element, nu );
to.add( nu );
}
return to;
}
private static <X,Y extends X> void copyBean( X from, Y nu ) {
((PersonBean)nu).setName( ((PersonDoamin)from).getName() );
((PersonBean)nu).setAge( ((PersonDoamin)from).getAge() );
}
}
class PersonDoamin {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
#Override
public String toString()
{
return "PersonDoamin{" + "name=" + name + ", age=" + age + '}';
}
}
class PersonBean extends PersonDoamin {
#Override
public String toString()
{
return "PersonBean{" + getName() + ',' + getAge()+ '}';
}
}
Output:
run:
[PersonBean{aj,25}]
BUILD SUCCESSFUL (total time: 0 seconds)
Why not just use addAll() for this? It does what you're trying to do, and it's already part of the system library.
Remember you can add a PersonBean to a PersonDomain list, but not the other way around.
public class GenericCopyTest
{
public static void main( String[] args ) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
personList.addAll( personBeansToReturn );
personBeansToReturn.addAll( personList ); // <-- FAILS
// No suitable method found
}
}
class PersonDoamin {}
class PersonBean extends PersonDoamin {}
If you want to put more than one bean class in the same list,
how about creating the list with parent class PersonDoamin , and then, you can store both PersonDoamin and PersonBean classes.
public static void main(String[] args) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName("aj");
p.setAge("25");
personList.add(p);
// Changed here. PersonBean => PersonDoamin
List<PersonDoamin> personBeansToReturn = new ArrayList<PersonDoamin>();
Test.listToArray(personList, personBeansToReturn);
// also you can insert PersonBean into the list
personBeansToReturn.add(new PersonBean());
}
I have created a Vector object to store data in Table object as Vector<Table>. Vector<Table> contains components as below.
[Vector<Record> records, String tableName, String keyColumnName, int recordCount, int columnCount]
I need to sort tableName in above Vector to my own order and return Vector<Table> with sorted tableNames for other processes.
I have wrote method as below.
private Vector<Table> orderTables(Vector<Table> loadTables) {
List<String> tableNames = new ArrayList<String>();
for (Table table : loadTables) {
String tblName = table.getTableName();
tableNames.add(tblName);
}
Collections.sort(tableNames, new MyComparable());
return null;
}
But I have no idea about how to write Comparator to this. My own sort order is stored in .properties file. I can read it and get value. But I have no idea about how to compare it.
How could I do it?
Before clarification
You need to write a Comparator for Table objects that delegates to the tableName's comparator:
new Comparator<Table>() {
#Override public int compare(Table one, Table two) {
return one.getTableName().compareTo(two.getTableName());
}
}
Note that this will consider Tables that have the same name to be equal. This can mess things up if you put these tables in a HashMap or HashSet. To avoid this, you can detect this case and return one.hashCode() - two.hashCode() if the table names are the same.
Guava's ComparisonChain is a convenient way to write such multi-stage comparisons:
new Comparator<Table>() {
#Override public int compare(Table one, Table two) {
return ComparisonChain.start()
.compare(one.getTableName(), two.getTableName())
.compare(one.hashCode(), two.hashCode())
.result();
}
}
After clarification
Okay, the question is to impose a predefined sorting order rather than sorting the Tables by name. In that case, you need to make a Comparator that is aware of the ordering defined in the .properties file.
One way to achieve this is to initialize a mapping of table names to sorting order indices, and refer that mapping during the comparison. Given the property value:
SORT_ORDER = SALES,SALE_PRODUCTS,EXPENSES,EXPENSES_ITEMS
The mapping should look like:
{
SALES: 0,
SALE_PRODUCTS: 1,
EXPENSES: 2,
EXPENSES_ITEMS: 3
}
Here's what the comparator would look like:
private static class PredefinedOrderComparator implements Comparator<Table> {
public PredefinedOrderComparator() {
// Initialize orderIndex here
}
private final Map<String, Integer> orderIndex;
#Override public int compare(Table one, Table two) {
return orderIndex.get(one.getTableName()) - orderIndex.get(two.getTableName());
}
}
To populate orderIndex from the property value, you need to:
Get the comma-separated list using getProperty() as you mentioned
Split that value on comma (I recommend using Guava's Splitter, but String.split or others will work too)
Initialize a new HashMap<String, Integer> and an int index = 0
Iterate through the split tokens, map the current token to index and increment index
Note the implicit assumption that none of the table names have a comma in it.
public class MyComparable implements Comparator<Table>{
#Override
public int compare(Table table1, Table table2) {
return (table1.getTableName().compareTo(table2.getTableName());
}
}
make sure that you have overridden the hashcode and equals in Table class to achieve this.
I wrote you a very simple example on how to work with a Comparator. If you create a class called Main, copy paste below contents in it, compile and run it, you can see what's going on.
A comparator just needs to implement an interface. For this it needs to implement one method (public int compare(T arg0, T arg1). There you specify how a collection will get sorted; in this case according to the alfabet.
I hope this helps you.
import java.util.*;
public class Main {
public static void main(String[] args) {
System.out.println("Start\n");
List<Item> items = new ArrayList<Item>();
for(String s : new String[]{"mzeaez", "xcxv", "hjkhk", "azasq", "iopiop"}) {
items.add(createItem(s));
}
System.out.println("Items before sort:");
System.out.println(Item.toString(items));
Collections.sort(items, new ItemComparator());
System.out.println("Items after sort:");
System.out.println(Item.toString(items));
System.out.println("End");
}
private static Item createItem(String s) {
Item item = new Item();
item.setS(s);
return item;
}
}
class Item {
private String s;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
#Override
public String toString() {
return "Item: " + s;
}
public static String toString(Collection<Item> items) {
String s = "";
for(Item item : items) {
s += item + "\n";
}
return s;
}
}
class ItemComparator implements Comparator<Item> {
#Override
public int compare(Item item1, Item item2) {
return item1.getS().compareTo(item2.getS());
}
}