I'm new to Java and trying to parse an XML file with objects having ENUM parameters as well. One of the ENUMs have a value as well. And at parsing I get error message (java.lang.IllegalArgumentException: No enum constant com.codecool.enums.AreaToUse.4).
the ENUM is
public enum AreaToUse {
TRAFICH(6),
TRAFICM(5),
HOMEH(4),
HOMEL(3);
private final int qualified;
AreaToUse(int qualified) {
this.qualified = qualified;
}
public int getQualified() {
return qualified;
}
}
the xml
<Lumber name="pineLong" producer="Nati" load="M" value="17.3"
qualified="4" length="3200" width="350" thickness="22"
species="pine"/>
and the parsing
for (int i = 0; i < lumbers.getLength(); i++) {
current = (Element) lumbers.item(i);
result.add(new Lumber(current.getAttribute("name"),
current.getAttribute("producer"),
Load.valueOf(current.getAttribute("load")),
Double.parseDouble(current.getAttribute("value")),
AreaToUse.valueOf(current.getAttribute("qualified")),
Integer.parseInt(current.getAttribute("length")),
Integer.parseInt(current.getAttribute("width")),
Integer.parseInt(current.getAttribute("thickness")),
current.getAttribute("species")));
}
my expected outcome is
pineLong, Nati, M, 17.3, 4, 3200, 350, 22, pine
actual return is
java.lang.IllegalArgumentException: No enum constant com.codecool.enums.AreaToUse.4
You have to write a method which returns the right Enum by the qualified value because valueOf searches an Enum by the given String. That is why you get the message No enum constant com.codecool.enums.AreaToUse.4. You have no AreaToUse called 4.
Try this:
public enum AreaToUse {
...
public static AreaToUse byQualified(int qualified) {
AreaToUse returnValue = null;
for(AreaToUse areaToUse : values()) {
if(areaToUse.getQualified() == qualified) {
returnValue = areaToUse;
}
}
return returnValue;
}
}
Instead I would recommend to create a map which maps qualified value to the area enum:
public enum AreaToUse {
private static final Map<Integer, AreaToUse> MAPPING = new HashMap<>();
static {
for(AreaToUse areaToUse : values()) {
MAPPING.put(areaToUse.getQualified(), areaToUse);
}
}
public static AreaToUse byQualified(int qualified) {
return MAPPING.get(qualified);
}
}
You should have a utility method like valueOfInt in ENUM AreaToUse which will convert your qualified int to a corresponding ENUM value.
public static AreaToUse valueOfInt(int i) {
return Arrays.stream(AreaToUse.values())
.filter(e -> e.getQualified() == i)
.findFirst()
.orElse(null);
}
And use it like:
AreaToUse.valueOfInt(current.getAttribute("qualified"))
If your current.getAttribute("qualified") returns a String then parse it to integer.
AreaToUse.valueOfInt(Integer.parseInt(current.getAttribute("qualified")))
Related
I have an enum like below. Until recently, all variables were single-valued. However, now TYPE4 can have one of three acceptable values. I was hoping to simply modify this enum to accommodate for TYPE4, but thinking perhaps having only one type that is multi-valued means I need to use an object for mapping rather than an enum. I would be grateful for any insights. Thank you.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI(TYPE_A or TYPE_B or TYPE_C);
private final String value;
public static final Map<Record, String> enumMap = new EnumMap<Record, String>(
Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValue());
}
Record(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Operationally, I use this enum in a factory class to determine which of 4 types of subclasses I should instantiate. I do this by have each of the subclasses know its own type like this:
#Override
public String getType() {
return Record.TYPE1.getValue();
}
,and then the factory class pre-builds a set of the subclasses like this:
#Component
public class RecordProcessorFactory {
#Autowired
public RecordProcessorFactory(List<RecordProcessor> processors) {
for (RecordProcessor recordProcessor : processors) {
processorCache.put(recordProcessor.getType(), recordProcessor);
}
}
private static final Map<String, RecordProcessor> processorCache = new HashMap<String, RecordProcessor>();
public RecordProcessor getSyncProcessor(String type) {
RecordProcessor service = processorCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
You could use a String array to store multiple values, note that your logic may change with enumMap that way.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
private final String[] values;
public static final Map<Record, String[]> enumMap = new EnumMap<Record, String[]>(Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValues());
}
Record(String... values) {
this.values = values;
}
public String[] getValues() {
return values;
}
}
In case you need to get the Enum from a String value, you could add this static method:
public static Optional<Record> optionalValueOf(final String value) {
for (Record record : values()) {
for (String recordValue : record.values) {
if (null == value && null == recordValue || value.equals(recordValue)) {
return Optional.of(record);
}
}
}
return Optional.empty();
}
I think it's better to encapsulate values in the enum. It should be immutable (array is not immutable data storage).
#lombok.Getter
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
// immutable list
private final List<String> values;
Record(String... values) {
this.values = Arrays.stream(values)
.collect(Collectors.toList());
}
}
P.S. Map<Record, String> enumMap I think is useless, because you have a Record already and all you need just call record.getValues() instead of Record.enumMaps.get(record). Also, this is breakes OOP encapsulation.
I have Enumutils like below:
public interface EnumUtil {
String getValue();
static <T extends Enum<T> & EnumUtil> T fromValue(String enumValue, Class<T> type) {
EnumSet<T> all=EnumSet.allOf(type);
for (final T t: all) {
System.out.println("Value: " + t.getValue());
System.out.println("Name: " + t.name());
T val = T.valueOf(t.getDeclaringClass(), t.name()).;
System.out.println("ValValVal: " + val);
if (t.getValue().equalsIgnoreCase(enumValue)) {
return t;
}
}
return null;
}
}
Then I have created an enum which appears as follows:
#Getter
#AllArgsConstructor
public enum SupportedOptions implements EnumUtil {
PART("PART"),
MSRV("MSRV");
private final String value; //If we add this line then we need not to override getValue()
public static SupportedOptions fromValue(final String text) {
return EnumUtil.fromValue(text, SupportedOptions.class);
}
}
This works fine without any compilation issues. (getValue also returns the enum parameter successfully in fromvalue in EnumUtils interface for this enum)
But in the following scenario it results in a compile time exception (need to implement abstract methods).
#Getter
#AllArgsConstructor
public enum RejectedResponseCode implements EnumUtil {
UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
IMSG("302", "105", "113", "114"),
PARS("107", "100", "102", "103", "115","720"),
SECU("302", "668", "669", "670", "671");
//approach 1
private final String value; //Here it want some value as i am using constructor like below
RejectedResponseCode(final String... codes) {
this.codes = Arrays.asList(codes);
//this.value = getValue(); <--- Is this approach fine. (For appraoch 1)
}
private final List<String> codes;
public static RejectedResponseCode getValueOfData(final String value) {
final Optional<RejectedResponseCode> result = Arrays.stream(values()).filter(rejectedResponseCode -> rejectedResponseCode.codes.contains(value)).findFirst();
if (result.isPresent()) {
return result.get();
}
return null;
}
public static RejectedResponseCode fromValue(final String text) {
return EnumUtil.fromValue(text, RejectedResponseCode.class);
}
//Approach 2
//#Override
//public String getValue(){
// return null; //Is this approach correct?
//}
}
How to ignore either not to override getValue()?
Or how to override getValue() in such a way that it returns parameter
or any other way to manage this in EnumUtil?
You need to search each enum value supporting multiple search keys, so implementing getValue is no use. On way around this is to reverse the operation such that the enum supplies a set of search criteria to be used, and implement a simple utility method on EnumUtil:
public class EnumUtil {
#SafeVarargs
public static <T,V> T match(T[] values, V fieldValue, BiPredicate<T,V> ... checks) {
for (var pred : checks)
for (T item : values)
if(pred.test(item, fieldValue))
return item;
return null; // OR throw new IllegalArgumentException("Not found: "+fieldValue);
}
}
Then your enum classes can setup any number of search parameters. SupportedOptions just matches on name:
enum SupportedOptions {
PART,
MSRV;
public static SupportedOptions fromValue(final String text) {
return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s));
}
}
RejectedResponseCode matches on name() and codes.indexOf:
enum RejectedResponseCode {
UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
IMSG("302", "105", "113", "114"),
PARS("107", "100", "102", "103", "115","720"),
SECU("302", "668", "669", "670", "671");
RejectedResponseCode(final String... codes) {
this.codes = Arrays.asList(codes);
}
private final List<String> codes;
public static RejectedResponseCode fromValue(final String text) {
return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s), (e, s) -> e.codes.indexOf(s) >= 0);
}
}
So, anything can be located by name:
public static void main(String[] args)
{
for (String s : new String[] { "PART", "msrv", "other"})
System.out.println("SupportedOptions.fromValue("+s+") => "+ SupportedOptions.fromValue(s));
for (String s : new String[] { "UNPR", "imsg", "102", "671", "999999"})
System.out.println("RejectedResponseCode.fromValue("+s+") => "+ RejectedResponseCode.fromValue(s));
}
SupportedOptions.fromValue(PART) => PART
SupportedOptions.fromValue(msrv) => MSRV
SupportedOptions.fromValue(other) => null
RejectedResponseCode.fromValue(UNPR) => UNPR
RejectedResponseCode.fromValue(imsg) => IMSG
RejectedResponseCode.fromValue(102) => PARS
RejectedResponseCode.fromValue(671) => SECU
RejectedResponseCode.fromValue(999999) => null
As directed by Joachim Sauer in comment:
EnumUtil.fromValue() simply assumes that there's always exactly one identifiying string for each enum value. That's not true for your second sample. So either don't use EnumUtil.fromValue() or extend it to also support multiple values (probably by having a second interface that can return an array or collection of potentital identifiers) –
Accordingly, EnumUtils Removed from enum and created methods inside enums only.
I would like to use an enum element as a value of an annotation attribute (which requires a string value). Hence, I have created an interface holding the String constants:
public interface MyStringConstants {
public static final String COMPANY_LOGIN = "Company Login";
public static final String COMPANY_LOGOUT = "Company Logout";
...
}
Furthermore I created the enum:
public enum MyEnumType implements MyStringConstants {
COMPANY_CONFIGURATION_READ(MyStringConstants.COMPANY_CONFIGURATION_READ),
COMPANY_CONFIGURATION_WRITE(MyStringConstants.COMPANY_CONFIGURATION_WRITE),
...;
private final String value;
private MyEnumType(final String myStringConstant) {
this.value = myStringConstant;
}
public String getValue() {
return this.value.toString();
}
public static MyEnumType getByValue(final String value){
for(final MyEnumType type : values()){
if( type.getValue().equals(value)){
return type;
}
}
return null;
}
}
There exists an annotation:
#DeviceValidatorOperation(operationType=MyStringConstants.COMPANY_CONFIGURATION_READ)
I would like to define the enum as mentioned above to put as a value for the annotation's operationType attribute. Using my enum from above results in this way:
#DeviceValidatorOperation(operationType=MyEnumType.COMPANY_CONFIGURATION_READ.getValue())
results in Eclipse complaining:
The value for annotation attribute DeviceValidatorOperation.operationType must be a constant expression
How can I achieve to use an enum element as a value for an annotation's attribute?
I've got widely used method like:
public Map<String, Double> parseData(String[] data) {
.................
Where data is something like new String[] { "column1 -> 2.00", "column2 -> New York", ... }
Problem: It appears that data can contains both: String -> Double & String -> String values. So I need smth like:
public Map<String, String or Double> parseData(String[] data) {
................
Question: Any ideas besides return Map<String, Object>?
Create a Wrapper StringOrDouble which will look a bit like this:
public class StringOrDouble {
private String internalString;
private Double internalDouble;
public StringOrDouble(String input) {
internalString = input;
}
public StringOrDouble(Double input) {
internalDouble = input;
}
public boolean hasString() {
return internalString != null;
}
public boolean hasDouble() {
return internalDouble != null;
}
public String getString() {
return internalString;
}
public Double getDouble() {
return internalDouble;
}
}
Then have a map of type Map<String, StringOrDouble> and use that. When you use the values, you can check which one it is by testing with hasString() and/or hasDouble(). Alternatively you could have an enum which determines which type it is.
public Map<String, Container> parseData(String[] data)
You can introduce a wrapper class for this
public class Container {
private String s;
private Double d;
public Container(String s) {
this.s=s;
}
public Container(Double d) {
this.d=d;
}
public hasString() {
return s!=null;
}
public hasDouble() {
return d!=null;
}
//getters/setters
}
As far as I understand, you want something like Map<String, ? extends String || Double as the return type, but no such thing is supported in Java:
4.9 Intersection Types An intersection type takes the form T1 & ... & Tn, n>0, where Ti, 1in, are type expressions. Intersection types arise
in the processes of capture conversion (§5.1.10) and type inference
(§15.12.2.7). It is not possible to write an intersection type
directly as part of a program; no syntax supports this. The values of
an intersection type are those objects that are values of all of the
types Ti, for 1in.
So you'd better parse the input array and hold different arrays for each different type or you can use a wrapper class to represent the values in the map returned, as some other answerers explained.
Use superclass:
public Map<String, Object> parseData(String[] data)
Just an alternative to #blalasaadri. don't pretend to be better:
public static class StringDoubleValue {
private final Optional<String> sValue;
private final Optional<Double> dValue;
public MetricValue(String sValue) {
this.sValue = Optional.of(sValue);
this.dValue = Optional.absent();
}
public MetricValue(Double dValue) {
this.sValue = Optional.absent();
this.dValue = Optional.of(dValue);
}
public Object get() {
return (sValue.isPresent()) ? sValue.get() : dValue.get();
}
#Override
public String toString() {
if (sValue.isPresent()) ? sValue.get() : dValue.get().toString();
}
}
i have class something like this
class MClass
{
private int mem1,mem2,mem3.......;
public int getmem1()
{
return mem1;
}
public int getmem2()
{
return mem2;
}
......
Now I want something like this :
public int getAttr(String attr)
{
if (attr=="mem1")
return mem1;
elseif (attr=="mem2")
return mem2;
.....
How do I implement getAttr for 1000s of attr ?
Please don't ask me to make mem as array.. that is not possible due to other parts of program.
Use reflection. Reflection
This will allow you to call any public method at runtime using the name of the method as a String.
Class c = Class.forName("MyClass");
Method m = c.getMethod("get"+arg);
return (Integer) m.invoke(this);
I suggest you create a Map<String, Integer> attrMap and do
public int getAttr(String attr) {
return attrMap.get(attr);
}
You create a Map<String,Object>. As key you use the attr, as value the values.
class MyCall {
private final Map<String,Object> map = new HashMap<String,Object>();
public Object getAttr(String attr) {
return map.get(attr);
}
}
If the values will be always integers, then you can replace the generic parameter Object with Integer.
public int getAttr(String attr) {
if(map.contains(attr)) {
return map.get(attr).intValue();
} else {
reutrn ERROR_CODE; //As error or throw exception
}
}