I have a list of bean coming from the db as a result of querying.
The bean is as follows:
public class Employee implements Comparator<Employee> {
protected String empId; //alphanumeric e.g.abc123
protected String empFullName;
protected String empAddress;
protected String dept;
protected String project;
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getEmpFullName() {
return empFullName;
}
public void setEmpFullName(String empFullName) {
this.empFullName = empFullName;
}
public String getEmpAddress() {
return empAddress;
}
public void setEmpAddress(String empAddress) {
this.empAddress = empAddress;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
#Override
public int compare(Employee e1, Employee e2) {
if (e1 == null && e2 == null) return 0;
if (e1 != null && e2 == null) return -1;
if (e1 == null && e2 != null) return 1;
return e1.empId.compareTo (e2.empId);
}
}
I have comparator which sorts by empId which is alphanumeric.
I want to know what is the best way to sort it by empId,dept,project.
In the code if I do as follows, it sorts by empId.
List<Employee> empList = someDao.getEmpList();
Collections.sort(empList, new Employee());
Any suggestions.
This is an odd declaration in itself:
public class Employee implements Comparator<Employee>
It would be much more common to have:
public class Employee implements Comparable<Employee>
and then maybe:
public class EmployeeByIdComparator implements Comparator<Employee>
and
public class EmployeeByNameComparator implements Comparator<Employee>
etc.
An instance of a class implementing Comparable knows how to compare itself with another instance; typically this is reserved for a "natural" ordering. Compare that with an instance of Comparator, which knows how to compare two instances usually of a different type (the one specified as the type argument for Comparator).
So if you want multiple types of comparison, create multiple comparators. You may then want to chain them together - Guava provides an easy way of doing this. (See ComparisonChain.) For convenience, you may want to implement the comparators as private static classes within Employee, then expose single instances of them via public static final fields:
public class Employee
{
public static final Comparator<Employee> BY_ID_COMPARATOR
= new ByIdComparator();
private static final class ByIdComparator : Comparator<Employee>
{
...
}
}
public class Employee implements Comparator<Employee>
This is a wrong (probably not intended) use of the Comparator interface. I think you want Comparable here.
For sorting by empId, dept and project create a custom comparator that does just that and pass it to the sort() method. This comparator would then just check the properties you want to compare by in the order you need and return the result when it is not 0 for the first time - or at the end if all properties are equal.
Example:
new Comparator<Employee>() {
public int compare(Employee e1, Employee e2) {
//check if both are not null
int result = e1.empId.compareTo( e2.empId );
if( result == 0) {
result = e1.dept .compareTo( e2.dept );
}
...
return result;
}
}
I have a list of java beans, now I want to sort them with specified property and sort order
(the property and sort order are input parameters), like this:
class Person{
private String userName;
private Integer age;
private String address;
public void sort(List<Person> ps, String property, String sortOrder)
{
// How to use the property and sortOrder??
Collections.sort(ps);
}
}
What is the best way of writing the sortList() method?
actually I have one way to do this. I can write two static properties for Person. then I set these two properties before sorting:
class Person implements Comparable<Person>{
private String userName;
private Integer age;
private String address;
public static String sortProperty;
public static String sortOrder;
public void sort(List<Person> ps, String property, String sortOrder)
{
Person.sortProperty=property;
Person.sortOrder=sortOrder;
Collections.sort(ps);
}
#Override
public int compareTo(Person o)
{
// find the property with Person.sortProperty using reflection
// then sort the property
}
}
This is not a good solution. Could anyone give me some suggestion? thanks in advance
Hope this makes sense for you. Use final keyword to avoid creating static variable for passing sort order. Also changed it to boolean for easy access. Below code is just for illustration.
public static void sort(List<Person> ps, String property, final boolean asc) {
if (property.equals("userName")) {
Collections.sort(ps, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
// pls use appropriate compareTo I always get confused
// which one to call when it is asc or desc.
if (asc)
return o1.getUserName().compareTo(o2.getUserName());
else
return o2.getUserName().compareTo(o1.getUserName());
}
});
}
if (property.equals("age")) {
Collections.sort(ps, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
if (asc)
return o1.getAge().compareTo(o2.getAge());
else
return o2.getAge().compareTo(o1.getAge());
}
});
}
}
You can implement a Comparator and use it like this.
Collections.sort(ps, myComparator)
Inside the comparator you can just retrieve property and sortOrder from person and compare with these variable
Collections.sort(ps, new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
int result = 0;
if(sortProperty.equals("userName")) {
result = p1.userName.compareTo(p2.userName);
} else if(//...
//determine how based on sortOrder here
//e.g.
if(sortOrder.equals("ascending") {
return result;
} else {
return (result * (-1));
}
}
});
Note: property and sortOrder need to be declared final.
You should be able to use the Bean Comparator for this.
Write three Comparators. They could be static fields. E.g.
public static final Comparator COMPARE_BY_NAME = (your code here)
And similar for COMPARE_BY_AGE etc.
Pass them to the standard Java sort() methods.
To make the sorting as generic extended method of a list you can use below code:
public static List<T> SortList<T>(this List<T> list, string sortDirection, string sortExpression)
{
if (sortDirection.ToLower() == "sorting_asc")
{
return (from n in list
orderby GetDynamicSortProperty(n, sortExpression) ascending
select n
).ToList();
}
else if (sortDirection.ToLower() == "sorting_desc")
{
return (from n in list
orderby GetDynamicSortProperty(n, sortExpression) descending
select n
).ToList();
}
else
{
return list;
}
}
public static object GetDynamicSortProperty(object item, string propName)
{
//Use reflection to get order type
return item.GetType().GetProperty(propName).GetValue(item, null);
}
Calling:
List<Employee> employees = new List<Employee>();
var sortedEmployees = employees.SortList(sortedorder, sortedcolumn);
i have a java class like this
public class A {
private String field1;
private String field2;
// getters, setters but no equals and hashcode
}
and a list of objects of this class, i want to remove from this list all the duplicates elements that has the same field1 or the same field2, so i have 2 Comparators
public class Comparator1 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField1().compareToIgnoreCase( o2.getField1() );
}
}
public class Comparator2 implements Comparator<A> {
public int compare(A o1, A o2) {
return o1.getField2().compareToIgnoreCase(o2.getField2());
}
}
so to do the task i use treeset like
TreeSet<A> ts1 = new TreeSet<A>(new Comparator1())
ts1.addAll(list)
TreeSet<A> ts2 = new TreeSet<A>(new Comparator2())
ts2.addAll(ts1)
list.clear()
list.addAll(ts2)
but how can i do the same using just one comparator and one treeset ?
Thanks for the help
Update:
Thanks all for the answers, but after reading them i don't know if this is the right approach to the real problem.
In my real case field1 is like a phone number and field2 is like a name.
So i don't want to call the same phone number more than one time (this is the first treeset to removes duplicates) and i don't want to call more than one time the same name (the second treeset to removes duplicates)
You can modify the class but i'd like to know if this approach is ok to resolve the real problem.
If this approach is correct, from your question, i see that without modifying the class is not possible to use just one comparator
Thanks
You can't use one comparator to sort by two criteria at the same time, so there is no real way to go better than two TreeSets in your case. Of course, you can wrap them in one data structure.
(Alternatively you could use two HashMaps, each having one of the strings as key - this will be faster on average, but is more complicated to program.)
You can't, and it's not clear to me that what you're trying to do is well-defined.
Are you aware that your current approach depends both on the order in which elements are added and on whether you check field1 or field2 first for duplicates? Imagine you had these objects of class A:
A ab = new A("a", "b");
A cb = new A("c", "b");
A cd = new A("c", "d");
Checking field1 first gives the result [ab] or [ab, cd], depending on the order added.
Checking field2 first gives the result [cb] or [ab, cd], depending on the order added.
This is pretty strange behavior. Is this what you intended? I don't think it is possible to reproduce this with a single TreeSet and Comparator in the general case.
public static <A extends Comparable<?>> TreeSet<A> getTreeSet(Collection<A> list){
TreeSet<A> result = new TreeSet<A>();
HashSet<A> unique = new HashSet<A>();
unique.addAll(list);
result.addAll(unique);
return result;
}
Generic function that adds items to hashset to make them unique, and then drop them to TreeSet to sort. You can use it with: TreeSet<A> ts1 = getTreeSet(list);.
This approach works well for a fixed list.
#BalusC No, this assumes
public class A implements Comparable<A> {
private String field1;
private String field2;
#Override
public int compareTo(A o) {
// No null checks, because it's illegal anyways.
int tmp = 0;
if ((tmp = field1.compareToIgnoreCase(o.field1)) != 0)
return tmp;
if ((tmp = field2.compareToIgnoreCase(o.field2)) != 0)
return tmp;
return tmp;
}
// getters, setters but no equals and hashcode
}
If your intention is to do two levels of sorting(first: PhoneNumber and second:Name), then you can use the following code, where the duplicate check will be done against both the fields(field1 and field2). As we are already using compareTo for both the fields, it is not required to use equals and hashcode. But it is always good practice to use hashcode and equals.
public class A implements Comparable<A> {
private String field1;
private String field2;
public A(String number, String name) {
this.field1 = number;
this.field2 = name;
}
// First level sorting will be done by field1.
// If field1 is equal then second level sorting will be done on field2
#Override
public int compareTo(A o) {
int compareTo = field1.compareTo(o.getNumber());
if(compareTo==0){
return field2.compareTo(o.getName());
}
return compareTo;
}
public String getNumber() {
return field1;
}
public String getName() {
return field2;
}
}
public class RemoveDuplicate {
public static void main(String[] args) {
final ArrayList<Student> students = new ArrayList<Student>();
Set<Student> set = new TreeSet<Student>();
Student[] starr = new Student[6];
starr[0] = new Student("Student1", "1005");
starr[1] = new Student("Student2", "1004");
starr[2] = new Student("Student3", "1003");
starr[3] = new Student("Student6", "1002");
starr[4] = new Student("Student5", "1001");
starr[5] = new Student("Student6", "1000");
Arrays.sort(starr, Student.StudentIdComparator);
for (Student s : starr) {
students.add(s);
}
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.Comparator;
import java.util.List;
public class RemoveDuplicate {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
List<Student> students = Arrays.asList(new Student("Student1", "1005"), new Student("Student2", "1004"),
new Student("Student3", "1003"), new Student("Student6", "1002"), new Student("Student5", "1001"),
new Student("Student6", "1000"));
// Sorting Using Lambda
students.sort(new Comparator<Student>() {
#Override
public int compare(Student s1, Student s2) {
return s1.getId().compareTo(s2.getId());
}
});
System.out.println(students);
set.addAll(students);
System.out.println("\n***** After removing duplicates *******\n");
final ArrayList<Student> newList = new ArrayList<Student>(set);
/** Printing original list **/
System.out.println(newList);
}
}
class Student implements Comparable<Student> {
private String name;
private String id;
public Student(String name, String id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "\n" + "Name=" + name + " Id=" + id;
}
#Override
public int compareTo(Student o1) {
if (o1.getName().equalsIgnoreCase(this.name)) {
return 0;
}
return 1;
}
// public static Comparator<Student> StudentIdComparator = (Student
// s1,Student s2) -> s1.getId().compareTo(s2.getId());
}
When working with variables/parameters that can only take a finite number of values, I try to always use Java's enum, as in
public enum BonusType {
MONTHLY, YEARLY, ONE_OFF
}
As long as I stay inside my code, that works fine. However, I often need to interface with other code that uses plain int (or String) values for the same purpose, or I need to read/write from/to a database where the data is stored as a number or string.
In that case, I'd like to have a convenient way to associate each enum value with a an integer, such that I can convert both ways (in other words, I need a "reversible enum").
Going from enum to int is easy:
public enum BonusType {
public final int id;
BonusType(int id) {
this.id = id;
}
MONTHLY(1), YEARLY(2), ONE_OFF(3);
}
Then I can access the int value as BonusType x = MONTHLY; int id = x.id;.
However, I can see no nice way for the reverse, i.e. going from int to enum. Ideally, something like
BonusType bt = BonusType.getById(2);
The only solutions I could come up with are:
Put a lookup method into the enum, which uses BonusType.values() to fill a map "int -> enum", then caches that and uses it for lookups. Would work, but I'd have to copy this method identically into each enum I use :-(.
Put the lookup method into a static utility class. Then I'd only need one "lookup" method, but I'd have to fiddle with reflection to get it to work for an arbitrary enum.
Both methods seem terribly awkward for such a simple (?) problem.
Any other ideas/insights?
enum → int
yourEnum.ordinal()
int → enum
EnumType.values()[someInt]
String → enum
EnumType.valueOf(yourString)
enum → String
yourEnum.name()
A side-note:As you correctly point out, the ordinal() may be "unstable" from version to version. This is the exact reason why I always store constants as strings in my databases. (Actually, when using MySql, I store them as MySql enums!)
http://www.javaspecialists.co.za/archive/Issue113.html
The solution starts out similar to yours with an int value as part of the enum definition. He then goes on to create a generics-based lookup utility:
public class ReverseEnumMap<V extends Enum<V> & EnumConverter> {
private Map<Byte, V> map = new HashMap<Byte, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(byte num) {
return map.get(num);
}
}
This solution is nice and doesn't require 'fiddling with reflection' because it's based on the fact that all enum types implicitly inherit the Enum interface.
I found this on the web, it was very helpful and simple to implement.
This solution was NOT made by me
http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private static final Map<Integer,Status> lookup
= new HashMap<Integer,Status>();
static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
return lookup.get(code);
}
}
Seems the answer(s) to this question are outdated with the release of Java 8.
Don't use ordinal as ordinal is unstable if persisted outside the
JVM such as a database.
It is relatively easy to create a static map
with the key values.
public enum AccessLevel {
PRIVATE("private", 0),
PUBLIC("public", 1),
DEFAULT("default", 2);
AccessLevel(final String name, final int value) {
this.name = name;
this.value = value;
}
private final String name;
private final int value;
public String getName() {
return name;
}
public int getValue() {
return value;
}
static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getName, Function.identity()));
static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values())
.collect(Collectors.toMap(AccessLevel::getValue, Function.identity()));
public static AccessLevel fromName(final String name) {
return names.get(name);
}
public static AccessLevel fromValue(final int value) {
return values.get(value);
}
}
org.apache.commons.lang.enums.ValuedEnum;
To save me writing loads of boilerplate code or duplicating code for each Enum, I used Apache Commons Lang's ValuedEnum instead.
Definition:
public class NRPEPacketType extends ValuedEnum {
public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1);
public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2);
protected NRPEPacketType(String name, int value) {
super(name, value);
}
}
Usage:
int -> ValuedEnum:
NRPEPacketType packetType =
(NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1);
You could perhaps use something like
interface EnumWithId {
public int getId();
}
enum Foo implements EnumWithId {
...
}
That would reduce the need for reflection in your utility class.
In this code, for permanent and intense search , have memory or process for use, and I select memory, with converter array as index.
I hope it's helpful
public enum Test{
VALUE_ONE(101, "Im value one"),
VALUE_TWO(215, "Im value two");
private final int number;
private final byte[] desc;
private final static int[] converter = new int[216];
static{
Test[] st = values();
for(int i=0;i<st.length;i++){
cv[st[i].number]=i;
}
}
Test(int value, byte[] description) {
this.number = value;
this.desc = description;
}
public int value() {
return this.number;
}
public byte[] description(){
return this.desc;
}
public static String description(int value) {
return values()[converter[rps]].desc;
}
public static Test fromValue(int value){
return values()[converter[rps]];
}
}
Use an interface to show it who's boss.
public interface SleskeEnum {
int id();
SleskeEnum[] getValues();
}
public enum BonusType implements SleskeEnum {
MONTHLY(1), YEARLY(2), ONE_OFF(3);
public final int id;
BonusType(int id) {
this.id = id;
}
public SleskeEnum[] getValues() {
return values();
}
public int id() { return id; }
}
public class Utils {
public static SleskeEnum getById(SleskeEnum type, int id) {
for(SleskeEnum t : type.getValues())
if(t.id() == id) return t;
throw new IllegalArgumentException("BonusType does not accept id " + id);
}
public static void main(String[] args) {
BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1);
System.out.println(shouldBeMonthly == BonusType.MONTHLY);
BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1);
System.out.println(shouldBeMonthly2 == BonusType.YEARLY);
BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2);
System.out.println(shouldBeYearly == BonusType.YEARLY);
BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3);
System.out.println(shouldBeOneOff == BonusType.ONE_OFF);
BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4);
}
}
And the result:
C:\Documents and Settings\user\My Documents>java Utils
true
false
true
true
Exception in thread "main" java.lang.IllegalArgumentException: BonusType does not accept id 4
at Utils.getById(Utils.java:6)
at Utils.main(Utils.java:23)
C:\Documents and Settings\user\My Documents>
Both the .ordinal() and values()[i] are unstable since they are dependent to the order of enums. Thus if you change the order of enums or add/delete some your program would break.
Here is a simple yet effective method to map between enum and int.
public enum Action {
ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5);
public final int id;
Action(int id) {
this.id = id;
}
public static Action get(int id){
for (Action a: Action.values()) {
if (a.id == id)
return a;
}
throw new IllegalArgumentException("Invalid id");
}
}
Applying it for strings shouldn't be difficult.
A very clean usage example of reverse Enum
Step 1
Define an interface EnumConverter
public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> {
public String convert();
E convert(String pKey);
}
Step 2
Create a class name ReverseEnumMap
import java.util.HashMap;
import java.util.Map;
public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> {
private Map<String, V> map = new HashMap<String, V>();
public ReverseEnumMap(Class<V> valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(String pKey) {
return map.get(pKey);
}
}
Step 3
Go to you Enum class and implement it with EnumConverter<ContentType> and of course override interface methods. You also need to initialize a static ReverseEnumMap.
public enum ContentType implements EnumConverter<ContentType> {
VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image");
private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class);
private final String mName;
ContentType(String pName) {
this.mName = pName;
}
String value() {
return this.mName;
}
#Override
public String convert() {
return this.mName;
}
#Override
public ContentType convert(String pKey) {
return map.get(pKey);
}
}
Step 4
Now create a Communication class file and call it's new method to convert an Enum to String and String to Enum. I have just put main method for explanation purpose.
public class Communication<E extends Enum<E> & EnumConverter<E>> {
private final E enumSample;
public Communication(E enumSample) {
this.enumSample = enumSample;
}
public String resolveEnumToStringValue(E e) {
return e.convert();
}
public E resolveStringEnumConstant(String pName) {
return enumSample.convert(pName);
}
//Should not put main method here... just for explanation purpose.
public static void main(String... are) {
Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME);
comm.resolveEnumToStringValue(ContentType.GAME); //return Game
comm.resolveStringEnumConstant("Game"); //return GAME (Enum)
}
}
Click for for complete explanation
I'm not sure if it's the same in Java, but enum types in C are automatically mapped to integers as well so you can use either the type or integer to access it. Have you tried simply accessing it with integer yet?
Really great question :-) I used solution similar to Mr.Ferguson`s sometime ago. Our decompiled enum looks like this:
final class BonusType extends Enum
{
private BonusType(String s, int i, int id)
{
super(s, i);
this.id = id;
}
public static BonusType[] values()
{
BonusType abonustype[];
int i;
BonusType abonustype1[];
System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i);
return abonustype1;
}
public static BonusType valueOf(String s)
{
return (BonusType)Enum.valueOf(BonusType, s);
}
public static final BonusType MONTHLY;
public static final BonusType YEARLY;
public static final BonusType ONE_OFF;
public final int id;
private static final BonusType ENUM$VALUES[];
static
{
MONTHLY = new BonusType("MONTHLY", 0, 1);
YEARLY = new BonusType("YEARLY", 1, 2);
ONE_OFF = new BonusType("ONE_OFF", 2, 3);
ENUM$VALUES = (new BonusType[] {
MONTHLY, YEARLY, ONE_OFF
});
}
}
Seeing this is apparent why ordinal() is unstable. It is the i in super(s, i);. I'm also pessimistic that you can think of a more elegant solution than these you already enumerated. After all enums are classes as any final classes.
For the sake of completeness, here is a generic approach to retrieve enum values by index from any enum type. My intention was to make the method look and feel like Enum.valueOf(Class, String). Fyi, i copied this method from here.
Index related issues (already discussed in depth here) still apply.
/**
* Returns the {#link Enum} instance for a given ordinal.
* This method is the index based alternative
* to {#link Enum#valueOf(Class, String)}, which
* requires the name of an instance.
*
* #param <E> the enum type
* #param type the enum class object
* #param ordinal the index of the enum instance
* #throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length
* #return the enum instance with the given ordinal
*/
public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) {
Preconditions.checkNotNull(type, "Type");
final E[] enums = type.getEnumConstants();
Preconditions.checkElementIndex(ordinal, enums.length, "ordinal");
return enums[ordinal];
}
Int -->String :
public enum Country {
US("US",0),
UK("UK",2),
DE("DE",1);
private static Map<Integer, String> domainToCountryMapping;
private String country;
private int domain;
private Country(String country,int domain){
this.country=country.toUpperCase();
this.domain=domain;
}
public String getCountry(){
return country;
}
public static String getCountry(String domain) {
if (domainToCountryMapping == null) {
initMapping();
}
if(domainToCountryMapping.get(domain)!=null){
return domainToCountryMapping.get(domain);
}else{
return "US";
}
}
private static void initMapping() {
domainToCountryMapping = new HashMap<Integer, String>();
for (Country s : values()) {
domainToCountryMapping.put(s.domain, s.country);
}
}
I needed something different because I wanted to use a generic approach. I'm reading the enum to and from byte arrays. This is where I come up with:
public interface EnumConverter {
public Number convert();
}
public class ByteArrayConverter {
#SuppressWarnings("unchecked")
public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException {
if (values == null || values.length == 0) {
final String message = "The values parameter must contain the value";
throw new IllegalArgumentException(message);
}
if (!dtoFieldType.isEnum()) {
final String message = "dtoFieldType must be an Enum.";
throw new IllegalArgumentException(message);
}
if (!EnumConverter.class.isAssignableFrom(fieldType)) {
final String message = "fieldType must implement the EnumConverter interface.";
throw new IllegalArgumentException(message);
}
Enum<?> result = null;
Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field.
for (Object enumConstant : fieldType.getEnumConstants()) {
Number ev = ((EnumConverter) enumConstant).convert();
if (enumValue.equals(ev)) {
result = (Enum<?>) enumConstant;
break;
}
}
if (result == null) {
throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString());
}
return result;
}
public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
if (!(value instanceof EnumConverter)) {
final String message = "dtoFieldType must implement the EnumConverter interface.";
throw new IllegalArgumentException(message);
}
Number enumValue = ((EnumConverter) value).convert();
byte[] result = convertToBytes(enumValue, requiredLength, numberSystem);
return result;
}
public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException {
// some logic to convert the byte array supplied by the values param to an Object.
}
public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException {
// some logic to convert the Object supplied by the'value' param to a byte array.
}
}
Example of enum's:
public enum EnumIntegerMock implements EnumConverter {
VALUE0(0), VALUE1(1), VALUE2(2);
private final int value;
private EnumIntegerMock(int value) {
this.value = value;
}
public Integer convert() {
return value;
}
}
public enum EnumByteMock implements EnumConverter {
VALUE0(0), VALUE1(1), VALUE2(2);
private final byte value;
private EnumByteMock(int value) {
this.value = (byte) value;
}
public Byte convert() {
return value;
}
}
Just because the accepted answer is not self contained:
Support code:
public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> {
public Integer getCode();
E fromCode(Integer code);
}
public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> {
private final HashMap<Integer, V> _map = new HashMap<Integer, V>();
public EnumWithCodeMap(Class<V> valueType) {
for( V v : valueType.getEnumConstants() )
_map.put(v.getCode(), v);
}
public V get(Integer num) {
return _map.get(num);
}
}
Example of use:
public enum State implements EnumWithCode<State> {
NOT_STARTED(0), STARTED(1), ENDED(2);
private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>(
State.class);
private final int code;
private State(int code) {
this.code = code;
}
#Override
public Integer getCode() {
return code;
}
#Override
public State fromCode(Integer code) {
return map.get(code);
}
}
given:
public enum BonusType {
MONTHLY(0), YEARLY(1), ONE_OFF(2)
}
BonusType bonus = YEARLY;
System.out.println(bonus.Ordinal() + ":" + bonus)
Output:
1:YEARLY
If you have a class Car
public class Car {
private Color externalColor;
}
And the property Color is a class
#Data
public class Color {
private Integer id;
private String name;
}
And you want to convert Color to an Enum
public class CarDTO {
private ColorEnum externalColor;
}
Simply add a method in Color class to convert Color in ColorEnum
#Data
public class Color {
private Integer id;
private String name;
public ColorEnum getEnum(){
ColorEnum.getById(id);
}
}
and inside ColorEnum implements the method getById()
public enum ColorEnum {
...
public static ColorEnum getById(int id) {
for(ColorEnum e : values()) {
if(e.id==id)
return e;
}
}
}
Now you can use a classMap
private MapperFactory factory = new DefaultMapperFactory.Builder().build();
...
factory.classMap(Car.class, CarDTO.class)
.fieldAToB("externalColor.enum","externalColor")
.byDefault()
.register();
...
CarDTO dto = mapper.map(car, CarDTO.class);