write parcelable on enum is crashing the app - java

This line is dest.writeParcelable(department, flags); causing the app to crash as I parcel the object via the intent.
When I comment the line, the app goes through but of course I don't have the department value.
public class User implements Parcelable {
public String id;
public String name;
public String email;
public Department department;
// other implementations of Parcelable
protected UserVO(Parcel in) {
id = in.readString();
name = in.readString();
email = in.readString();
department = in.readParcelable(Department.class.getClassLoader());
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeString(email);
dest.writeParcelable(department, flags); // crashes
}
}
Department Enum
public enum Department implements Parcelable {
SALES("Sales", 1),
HR("Human Resources", 2),
SHIPPING("Shipping", 3),
public final String name;
public final int ordinal;
Department(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
Department(Parcel in) {
name = in.readString();
ordinal = in.readInt();
}
public static final Creator<Department> CREATOR = new Creator<Department>() {
#Override
public Department createFromParcel(Parcel in) {
return Department.valueOf(in.readString()); // crash
//return new DeptEnum(in); // android studio generated code but has error: enum types may not be instantiated
}
#Override
public Department[] newArray(int size) {
return new Department[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(ordinal);
}
}
Error
No enum constant Department.Sales
I took the help from this answer for the line return Department.valueOf(in.readString());
Observation
I guess it has to do something with having both name and ordinal values, my enum type is not simple as in the linked answer, somehow I need to create an enum from parcel.
public Department createFromParcel(Parcel in) {
Log.d("abc", "createFromParcel: " + in.readString()); // returns null
Log.d("abc", "createFromParcel: " + in.readInt()); // returns 127 or 128
return SHIPPING; // hard coded value works but of course not a solution
}

You don't need to parcel the ordinal. Doing valueOf alone on the name would be fine
However, the parameter of valueOf must match exactly the enum defined name.
For example, this would work for sales and shipping, but not HR
Department.valueOf(in.readString().toUpperCase());
The solution you need is to loop over Department.values() and compare each name field of the values with the in.readString() result
How can I lookup a Java enum from its String value?
Or you could use the ordinal instead like so, although you're duplicating ordinal information and you'd have to guarantee that the enums have sequential numbers
return Department.values()[in.readInt() - 1]

Related

Firebase Realtime No properties to serialize found on class java.lang.CharSequence

I am currently using firebase recycler adapter and running into this error
"com.google.firebase.database.DatabaseException: No properties to
serialize found on class java.lang.CharSequence".
I am currently using a char sequence to retreive text from a button.
//Class for storing creating litter sightings
public class Item implements Serializable
{
//class variables
public CharSequence user;
public String brief;
//constructor
public Item(CharSequence user, String brief)
{
this.user=user;
this.brief=brief;
}
public Item()
{
}
//getters
public String getBrief() {
return brief;
}
public void setBrief(String myBrief) {
this.brief = myBrief;
}
public String getUser() {
return user;
}
}
You have to must use Percable because
Serializable
Serializable is a standard Java interface. You can just implement Serializable interface and add override methods. The problem with this approach is that reflection is used and it is a slow process. This method creates a lot of temporary objects and causes quite a bit of garbage collection. However, Serializable interface is easier to implement.
Parcelable
Parcelable process is much faster than Serializable. One of the reasons for this is that we are being explicit about the serialization process instead of using reflection to infer it. It also stands to reason that the code has been heavily optimized for this purpose.
Look at the example below (Parcelable):
// MyObjects Parcelable class
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
public class MyObjects implements Parcelable {
private int age;
private String name;
private ArrayList<String> address;
public MyObjects(String name, int age, ArrayList<String> address) {
this.name = name;
this.age = age;
this.address = address;
}
public MyObjects(Parcel source) {
age = source.readInt();
name = source.readString();
address = source.createStringArrayList();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
dest.writeStringList(address);
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public ArrayList<String> getAddress() {
if (!(address == null))
return address;
else
return new ArrayList<String>();
}
public static final Creator<MyObjects> CREATOR = new Creator<MyObjects>() {
#Override
public MyObjects[] newArray(int size) {
return new MyObjects[size];
}
#Override
public MyObjects createFromParcel(Parcel source) {
return new MyObjects(source);
}
};
}
// MyObjects instance
MyObjects mObjects = new MyObjects("name", "age", "Address array here");
// Passing MyOjects instance
Intent mIntent = new Intent(FromActivity.this, ToActivity.class);
mIntent.putExtra("UniqueKey", mObjects);
startActivity(mIntent);
Conclusion
Parcelable is faster than Serializable interface
Parcelable interface takes more time to implement compared to Serializable interface Serializable interface is easier to implement Serializable interface creates a lot of temporary objects and causes quite a bit of garbage collection
Parcelable array can be passed via Intent in android.

Having trouble with some validating methods

I am having a bit of trouble on this homework on a couple things
First, they want me to create a method that checks whether the property type is one of the three types : mountain, fixie, cross-country. How do I go about doing this?
Second, they want me to create a method that fixes the string model if contains special characters. I created the method private boolean isValidModel(String model) to check that but am not sure if my fixing method is correct.
Lastly, how do I go about creating several instances of the bike class as mentioned below?
For reference the question is below.
Any help will be greatly appreciated
Write a class called Bike, which has three private properties: model, type, year - provide getters and setters for the properties. Don’t allow invalid data to be stored in the properties – year should be positive number, model should contain only letters, digits and spaces and type should be one of the following: mountain, fixie, cross-country. Provide private methods to validate the model and use it upon changing the model, also provide method to remove any invalid characters from the model so that you could save the new String into the model property.
private boolean isValidModel(String model)
private String fixModel(String model) // will return the model, with removed invalid characters
Also provide the public method display, which displays information about the bike:
public void display()
Write a main program to create several instances of the Bike class, try to set invalid data to the properties and invoke display after each change.
public class Bike
{
// Instance field
private String model;
private String type;
private int year;
public Bike(String model, String type, int year )
{
model = unknown;
type = unknown;
year = unknown;
}
//getters
public String getModel()
{
return model;
}
public String getType()
{
return type;
}
public int getYear()
{
return year;
}
//setters
public void setModel( String model )
{
model = N/A;
}
public void setType( String type )
{
type = N/A;
}
public void setYear( int year )
{
if(year < 1970)
year = 1970;
}
private boolean isValidModel(String model){
int len = model.length();
for (int i = 0; i < len; i++) {
if ((Character.isLetterOrDigit(model.charAt(i)) == false) && model.charAt(i)!=' ') {
return false;
}
}
return true;
}
private String fixModel(String model){
model= model.replaceAll("[^A-Za-z0-9]","");
}
public void display(){
System.out.println("Year: "+year+" Model: "+model+"Type: "+type);
}
You can check if the model is valid using different methods, I have did choose to use RegEx. If you still want to use your solution you have to change the '&&' (AND) symbol to an '||' (OR) symbol.
Bike class:
public class Bike {
private String model;
private String type;
private int year;
private boolean isValidModel(String model) {
// Making a pattern that checks for the requirements
Pattern pattern = Pattern.compile("[ A-Za-z0-9]*");
return pattern.matcher(model).matches();
}
private String fixModel(String model) {
// Replacing all the bad characters
return model.replaceAll("[^ A-Za-z0-9]", "");
}
public String getModel() {
return model;
}
public void setModel(String model) {
// Check if the Model is valid
if (this.isValidModel(model))
this.model = model; // If so store the model
else
this.model = this.fixModel(model); // Store the fixed model
}
public String getType() {
return type;
}
public void setType(String type) {
// Check if the type contains one of the hard coded types
if (type.equals("mountain") || type.equals("fixie") || type.equals("cross-country"))
this.type = type;
}
public int getYear() {
return year;
}
public void setYear(int year) {
if (year > 0) // Check if the year is positive
this.year = year;
}
public void display() {
// Displaying the stored information
System.out.println("Year:" + year + " Model:" + model + " Type:" + type);
}
}
The main method:
public static void main(String[] args) {
Bike bike1 = new Bike(); // Creating the first Bike instance
bike1.setModel("This Is Valid"); // Valid model
bike1.setYear(10); // Valid year
bike1.setType("cross-country"); // Valid type
Bike bike2 = new Bike(); // Creating the second Bike instance
bike2.setModel("This-Is-Invalid"); // Invalid model
bike2.setYear(-1); // Invalid year
bike2.setType("Invalid Type"); // Invalid type
bike1.display(); // Expected Year:10 Model:This Is Valid Type:cross-country
bike2.display(); // Expected Year:0 Model:ThisIsInvalid Type:null
}
I hope you find this answer helpful.

What Pattern design to get rid of the type casting in my case?

public abstract class Employee {
String name;
String position
public Employee(String name, String position) {
this.name = name;
this.position = position
}
}
public class Pilot extends Employee {
public Pilot(String name,String position) {
super();
}
public void flight() {//flight the plane}
//getter and setter for the fields
}
public class Attendance extends Employee {
public Attendance(String name,String position) {
super();
}
public Food servingFood(String foodName) {}
}
// there will be many other positions
public class Company {
HashMap<String, ArrayList<Employee>> employeeTable; //values is a list of workers, key is the position
public Company() {this.employeeTable = new HashMap<>();}
public initializeEmployeeTable(file) {} //read file, and create keys in map (file contains information of the position)
public Worker hireEmployee(String position, String name){
if (position.equals("pilot")) {
Pilot p = Pilot(name);
employeeTable.get("pilot").add(p);
return p
}
else if (position.equals("flightAttendance")) {// the else if statement continuous to check the other position; }
}
public Worker callEmployee(String position, String name) {
for ( Employee e : employeeTable.get(position) ) {
if e.getName().equals(name) {
return e;
}
}
return null;
}
public static void main(String[] args) {
Company company = new Company();
company.initializeEmployeeTable(filePath);
File eventFile = new File(filePath); // event file describes what's happening in real world; read the lines, and call the program so that program simulates the real world events
sc = new Scanner(eventFile);
do {
String currentEvent = sc.nextLine();
String[] currentEventParts = currentEvent.split(", ");
if (currentEvent[0].equals("New Airplane")) { // currentEvent looks like {"New Airplane", "Attendance"// this part can be other position name, "Linda"}
Worker w = company.hireEmployee(currentEventParts[1], currentEventParts[2]); }
else if ((currentEvent[0].equals("flying"))) {
Worker w = company.callEmployee(currentEvent[0], currentEvent[1])
if (w.getPosition().equals("Pilot")) {(Worker) w.flight()}
if (w.getPosition().equals("Attendance")) {(Worker) w.serveFood()}
}
}
The reason there is HashMap for employee because there will be many positions; and reading the event file (when the first index is "New Airplane"); I don't want to go check the following index (would be name and position) with so many if statements to create corresponding employee. But when comes to calling specific methods, I need type casting now; since each method can be different (different type parameter, return type); so it's not ideal to have this methods be abstract method in super class employee and have the subclass implements the body.
Any advices: employee data structure; reading file strategy, pattern design would be appreciated. thanks

Send List of Custom model between Intents

I've found lots of answers on SO but none working for me.
I have a List<MyModel> myList that I want to pass and retrieve through Intents.MyModel implements Parcelable and its implementation.I am using intent.putExtra("my_key",(Parcelable) myList);At runtime it throws the exception
java.util.ArrayList cannot be cast to android.os.Parcelable
And if I use putParcelableArrayListExtra then it says wrong second argument type.Any explaination or other way would be helpfulEDIT: My Model class is :
public class Filter {
private String categoryId;
public List<PrimaryFilterData> getPrimaryFilterDataList() {
return primaryFilterDataList;
}
public void setPrimaryFilterDataList(List<PrimaryFilterData> primaryFilterDataList) {
this.primaryFilterDataList = primaryFilterDataList;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public String getCategoryId() {
return categoryId;
}
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
private String categoryName;
private List<PrimaryFilterData> primaryFilterDataList;
public static class PrimaryFilterData implements Parcelable
{
private String filterId;
protected PrimaryFilterData(Parcel in) {
filterId = in.readString();
filterName = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(filterId);
dest.writeString(filterName);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<PrimaryFilterData> CREATOR = new Creator<PrimaryFilterData>() {
#Override
public PrimaryFilterData createFromParcel(Parcel in) {
return new PrimaryFilterData(in);
}
#Override
public PrimaryFilterData[] newArray(int size) {
return new PrimaryFilterData[size];
}
};
public String getFilterName() {
return filterName;
}
public void setFilterName(String filterName) {
this.filterName = filterName;
}
public String getFilterId() {
return filterId;
}
public void setFilterId(String filterId) {
this.filterId = filterId;
}
public List<SecondaryFilterData> getSecondaryFilterDataList() {
return secondaryFilterDataList;
}
public void setSecondaryFilterDataList(List<SecondaryFilterData> secondaryFilterDataList) {
this.secondaryFilterDataList = secondaryFilterDataList;
}
private String filterName;
private List<SecondaryFilterData> secondaryFilterDataList;
}
public static class SecondaryFilterData implements Parcelable {
private String secFilterName;
private String secFilterId;
private boolean isChecked=false;
public SecondaryFilterData(){}
protected SecondaryFilterData(Parcel in) {
secFilterName = in.readString();
secFilterId = in.readString();
isChecked = in.readByte() != 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(secFilterName);
dest.writeString(secFilterId);
dest.writeByte((byte) (isChecked ? 1 : 0));
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<SecondaryFilterData> CREATOR = new Creator<SecondaryFilterData>() {
#Override
public SecondaryFilterData createFromParcel(Parcel in) {
return new SecondaryFilterData(in);
}
#Override
public SecondaryFilterData[] newArray(int size) {
return new SecondaryFilterData[size];
}
};
public boolean isChecked() {
return isChecked;
}
public void setIsChecked(boolean isChecked) {
this.isChecked = isChecked;
}
public String getSecFilterName() {
return secFilterName;
}
public void setSecFilterName(String secFilterName) {
this.secFilterName = secFilterName;
}
public String getSecFilterId() {
return secFilterId;
}
public void setSecFilterId(String secFilterId) {
this.secFilterId = secFilterId;
}
}
}
Now I wish to send list of Primary data from one activity to other
You can achieve this with a Bundle.
// Important - use array list not regular list.
// MyModel must implement Parcelable
ArrayList<MyModel> myList;
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("my_key", myList);
intent.putExtras(bundle);
If your list must be of type List you can convert it at the last minute.
bundle.putParcelableArrayList("my_key", new ArrayList<MyModel>(myList));
Then in the receiving activity.
List<MyModel> myList = getIntent().getParcelableArrayListExtra("my_key");
if your MyModel class contains data memeber of only primary or String type then
Implement java.io.Serializable interface in your MyModel class.
Use Intent.putExtra(String name, Serializable value) method to send the list of model with Intent.
Intent.getSerializableExtra(String name) to retrieve list back;
You can pass your java object as a Parcelable object in the bundle. But before that you need your model java class to implement Parcelable interface and override the necessary methods.
Once you have implemented the Parcelable interface you can easily pass your model class(java class) object as Parcelable to the bundle. Small snippet below:
yourBundle.putParcelable(YOUR_OBJ_KEY,yourModelObject);
Here, yourModelObject is the object of your model class that contains all the getters and setters method that you can use.
The point i am trying to make is that you pass your entire java object to the next activity rather than the list. By doing this you will get all the information that you require in your next activity inside this java object.
I think the problem is that your List does indeed not implement Parcelable. Its content does. Try to pass an array, not a List, of Parcelables:
intent.putExtra("my_key", myList.toArray());
Edit: I just saw that it is also possible to send lists but they have to be of type ArrayList. If you don't want to convert your List to an array, try to declare your List as ArrayList<MyModel> myList where MyModel must implement Parcelable.
Phew!
After long hours of work; I come to following conculsion (I'll update answer if I find some better reason).
Parceble/Serializablwe is passing value of list as reference; that's why when I was trying to access that list from other activity; it was returning null.
Currently ; I have maintained a global list(singleton using application class) and updating / retreiving it.

Conveniently map between enum and int / String

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);

Categories