I have an enum defined as below, which contains a static function fromString(String s) which is like valueOf(String s) but case insensitive.
enum Platform {
TWITTER("TWITTER"), INSTAGRAM("INSTAGRAM"), UNKNOWN;
private String value;
Platform(String value) {
this.value = value;
}
private static final Map<String, Platform> stringToEnumMap = new HashMap<>();
static {
for (Platform platform : values()) {
stringToEnumMap.put(platform.toString().toLowerCase(), platform);
}
}
public static Platform fromString(String symbol) {
Platform platform = stringToEnumMap.get(symbol.toLowerCase());
if (platform != null) {
return platform;
} else {
return UNKNOWN;
}
}
public String getValue() {
return value;
}
#Override
public String toString() {
return getValue();
}
}
but when I execute the following code
Platform platform = Platform.fromString("twitter");
I get Platform.UNKNOWN returned on few devices on production.
Any Idea?
Update
symbol.toString() gives twitter
stringToEnumMap.toString() gives this
Platform StringToEnumMap: {
twıtter=TWITTER, ınstagram=INSTAGRAM,
unknown=UNKNOWN}
If you look closely, the letter i is different in the keys of HashMap and thats why string comparison fails.
the hexvalue of letter ı in stringToEnumMap is 0131, whereas the it should be 0069
Why is this happening on only few devices? How to avoid it?
It seems to be a dirty ide issue, try to clean up your project, build and run again.
UPDATE
Try replacing your if statement by getOrDefault method:
public static Letter fromString(String symbol) {
return stringToEnumMap.getOrDefault(symbol.toLowerCase(), UNKNOWN);
}
In my Opinion, there is no need for a map.... nor a new method for doing something that valueOf() is able to do...
look this implemetation..
enum ECase {
A, B, UNK;
public static ECase resolveEnumFromString(final String string) {
ECase r = null;
try {
r = ECase.valueOf(string.toUpperCase());
} catch (final IllegalArgumentException e) {
r = ECase.UNK;
}
return r;
}
}
you can verify the results doing:
ECase d = null;
d = ECase.resolveEnumFromString("a");
System.out.println(d);
d = ECase.resolveEnumFromString("A");
System.out.println(d);
d = ECase.resolveEnumFromString("0");
System.out.println(d);
I am able to find what the issue was. After some careful logging I found the issue was this http://mattryall.net/blog/2009/02/the-infamous-turkish-locale-bug.
Apparently toLowerCase() and toUpperCase() functions are locale dependent due to which you can't use them safely for case insensitive string comparison.
So all you need to do is pass the english locale in the parameter of these functions like this -
toLowerCase(Locale.ENGLISH).
Related
I am writing test method like setTask(Task task). And Task object has several fields, e.g.
public String vehicle;
Method setTask should be used in different test-cases, so I'd like to have an options for this field to accept values:
null - the method should not do anything in this particulare case;
some string value - e.g. "", "Hello, World!", "Iso Isetta", ...
random - a value that indicates (as well as null indicates "no changes") that a random value should be selected for a drop-down list corresponding to this field.
So what can I do to make String to be SpecialString which could accept values null, random & some string value? (BTW: I don't want to set it to string value "RANDOM", and chech whether the value is equal to "RANDOM"-string)
UPDATE: I don't mean random like random value from a set of values, I mean random as well as null and this is for setTask() to handle random (select random from drop-down), and not to pass a random string from a set of values.
Pseudocode:
Task task = new Task();
task.vehicle = random; // as well as null
setTask(task)
in setTask(Task task):
if (task.vehicle == null) {
//skip
} else if (task.vehicle == random) {
// get possible values from drop-down list
// select one of them
} else {
// select value from drop-down list which is equal to task.vehicle
}
Don't assign a fixed String but use a Supplier<String> which can generate a String dynamically:
public Supplier<String> vehicleSupplier;
This, you can assign a generator function as you request:
static Supplier<String> nullSupplier () { return () -> null; }
static Supplier<String> fixedValueSupplier (String value) { return () -> value; }
static Supplier<String> randomSupplier (String... values) {
int index = ThreadLocalRandom.current().nextInt(values.length) -1;
return index > 0 && index < values.length ? values[index] : null;
}
In use, this looks like:
task.setVehicleSupplier(nullSupplier()); // or
task.setVehicleSupplier(fixedValueSupplier("value")); // or
task.setVehicleSupplier(randomSupplier("", "Hello, World!", "Iso Isetta"));
and you can get the String by
String value = task.vehicleSupplier().get();
or hide the implementation in a getter function
class Task {
// ...
private Supplier<String> vehicleSupplier;
public void setVehicleSupplier(Supplier<String> s) {
vehicleSupplier = s;
}
public String getVehicle() {
return vehicleSupplier != null ? vehicleSupplier.get() : null;
}
// ...
}
What you may want to do is to create an object that wraps a string as well as some information about whether or not it's a special value. Something along the lines of...
public class Special<T> {
public enum Type {
NOTHING, RANDOM, SPECIFIC
}
private final Type type;
private final T specificValue;
public Special(Type type, T specificValue) {
this.type = type;
this.specificValue = specificValue;
}
public Type getType() {
return type;
}
public T getSpecificValue() {
if (type != SPECIFIC) {
throw new IllegalStateException("Value is not specific");
}
return specificValue;
}
}
The class above could be used like so:
Special<String> a = new Special<>(Special.Type.NOTHING, null);
Special<String> b = new Special<>(Special.Type.SPECIFIC, "Hello");
if (b.getType() == Special.Type.RANDOM) {
// do something
}else if (b.getType() == Special.Type.SPECIFIC) {
String val = b.getSpecificValue();
// do something else
}
A slightly more polished variant of the thing above is probably the best way, but there is a way, a much uglier way, to do it using nothing but a String field.
What you could do is to have a "magical" string instance that behaves differently from all other string instances, despite having the same value. This would be done by having something like
static final String SPECIAL_VALUE_RANDOM = new String("random");
Note the use of the String constructor, which ensures that the string becomes a unique, non-interned instance. You can then say if (vehicle == SPECIAL_VALUE_RANDOM) { ... } (note the use of == instead of .equals()) to check if that specific instance (rather than any other string that says "random") was used.
Again, this is not a particularly good way of doing this, especially if you intend to do this more than once ever. I would strongly suggest something closer to the first way.
I have the following code :
class MyClass {
private Value value;
public enum Value {
INSERT_ONLY("INSERT_ONLY"), UPDATE_ONLY("UPDATE_ONLY"), UPSERT("UPSERT") ;
private final String val ;
private Value(final String v) {val = v ;}
public String toString() {return val ;}
public String getVal() {
return val;
}
} ;
public Value getValue() {
return value;
}
public void setValue(Value value) {
this.value = value;
}
}
public class one {
public static void main(String[] args) {
MyClass obj = new MyClass() ;
obj.setValue(MyClass.Value.INSERT_ONLY) ;
String s = obj.getValue().toString() ;
String s1 = MyClass.Value.INSERT_ONLY.toString() ;
switch(s) {
case "INSERT_ONLY" : System.out.println("INSERT_ONLY") ;
break ;
case "s2" : System.out.println("s2") ;
break ;
}
}
}
This code works. But what I want is that in switch-case I use the strings as defined in the enum Value. If I use s1 in case, it generates an error. What is the way out?
Enums have a method .name() that returns the enum as a String. All your "values" are equivalent to this - just switch on that.
You can delete the value field, the constructor and getter and call name() where you are currently calling getValue().
Further, the default implementation of toString() returns name() so you can delete your toString() method without having any effect.
Finally, you can switch on the enum itself.
you string in the enum is actually the same as the enumertor constants... so it is a little redundant and completely unnescessary...
try this:
enum Value {
INSERT_ONLY, UPDATE_ONLY, UPSERT;
}
public static void main(String[] args) {
Week obj = new Week() ;
Value s = obj.getValue( ) ;
switch(s) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
case UPDATE_ONLY : System.out.println("UPDATE_ONLY") ;
break ;
}
}
}
You could try it this way:
MyClass.Value value = obj.getValue() ;
switch(value) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
case UPSERT : System.out.println("Upsert") ;
break ;
case UPDATE_ONLY : System.out.println("Update only") ;
break ;
}
Basically, the switch-statement uses the enum-names. So there is no need to apply the toString()-method on the Value returned by obj.getValue().
If you would like to know why switch will not work in combination with strings, please have a look here.
One more suggestion: add also the default-branch to the switch-statement.
If I understand correctly, you're trying to look up an enum constant by an unknown string s. The reason your case expression can't be s1 is because it must be a compile-time constant, which s1 is not. Since your example seems to be mostly theoretical, I'll suggest a few options and you can pick the most appropriate for your actual case:
Assuming the enum names are the same as their values (in which case you can scrap the field entirely) and you're just trying to look up an enum by its name, just do this:
MyClass.Value v = MyClass.Value.valueOf(s);
This will throw an IllegalArgumentException if no mapping is found for s1.
Still assuming the names are the same, but you do need an actual switch with some additional cases and custom logic:
try {
MyClass.Value v = MyClass.Value.valueOf(s);
switch (v) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
}
} catch (IllegalArgumentException e) {
switch (s)
case "s2" : System.out.println("s2") ;
break ;
}
}
If the names are not actually the same, you can add a static map of constants inside the enum class, to simulate valueOf():
public enum Value {
ONLY_INSERT("ONLY_INSE"), ONLY_UPDATE("UPDATE_ONLY"), UPSERT("UPSERT") ;
private static final Map<String, Value> byName = new HashMap<>();
static {
for (Value v : values()) {
byName.put(v.getVal(), v);
}
}
public static Value byName(String name) {
Value result = byName.get(name);
if (result == null) {
throw new IllegalArgumentException("Invalid name" + name);
}
return result;
}
private final String val ;
private Value(final String v) {val = v ;}
public String toString() {return val ;}
public String getVal() {
return val;
}
} ;
Now you can do the same as the previous solutions, using MyClass.Value.byName().
I have a list which contains Strings ABC:123,abc:123 ;when I am converting it to Set its giving me 2 different elements.Is there a one liner way to convert this List to Set ignoring the case so that my Set contains ABC:123 only.` But if the input List contains ABC:123a4,abc:1234A4 it should give me 2 different elements in the Set : ABC:123a4,ABC:1234A4
I know this can be done spliting the list elements on ":" first and converting the abc to all uppercase and adding them to new list and then the rest.But just wanted to know if there a better way (small lines of code) to do that.Thanks for any brain storming ideas in advance.
List<String> memlist = new ArrayList<String>(Arrays.asList(memberList.split(",")));
Set<String> memberSet = new HashSet<String>(memlist );
memlist = new ArrayList<String>(memberSet);
You can use a TreeSet with the String.CASE_INSENSITIVE_ORDER flag set.
String startingString = "ABC:123,abc:123";
List<String> caseSensitiveList = Arrays.asList(startingString.split(","));
Set<String> caseInsensitiveSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
caseInsensitiveSet.addAll(caseSensitiveList);
for(String caseInsensitiveString : caseInsensitiveSet){
System.out.println(caseInsensitiveString);
}
This code, when run, gives me the output:
ABC:123
replace
memberList.split(",")
with
memberList.toUpperCase().split(",")
The cleanest solution is the one suggested by #SQLHacks. But then you said ABC:123a4 must be different from abc:1234A4. I guess the only solution now is to create a wrapper for the String objects and override the equals() and hashCode() method to do what you want, as #PaulBoddington suggested in his comment.
This is what I came up with (edited and improved based on #nafas answer):
public class StringWrapper {
private String value;
private String beforeColon;
private String afterColon;
private int hash;
public StringWrapper(String value) {
this.value = value;
String[] splitted = value.split(":");
beforeColon = splitted[0];
afterColon = splitted[1];
hash = Objects.hash(beforeColon.toUpperCase(), afterColon);
}
public String getValue() {
return value;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof StringWrapper) {
StringWrapper other = (StringWrapper) obj;
return beforeColon.equalsIgnoreCase(other.beforeColon) && afterColon.equals(other.afterColon);
}
return false;
}
#Override
public int hashCode() {
return hash;
}
#Override
public String toString() {
return value;
}
}
And then:
// this method is just to help you building a List<StringWrapper> from your String (memberList variable)
public static List<StringWrapper> split(String string, String regex) {
List<StringWrapper> list = new ArrayList<>();
for (String element : string.split(regex)) {
list.add(new StringWrapper(element));
}
return list;
}
public static void main(String[] args) {
String memberList = "ABC:123,abc:123,ABC:123a4,ABC:123A4";
List<StringWrapper> memlist = new ArrayList<>(split(memberList, ","));
Set<StringWrapper> memberSet = new HashSet<>(memlist);
memlist = new ArrayList<StringWrapper>(memberSet);
for (StringWrapper element : memlist) {
System.out.println(element);
}
}
If you run this, you get as output the following:
ABC:123a4
ABC:123A4
ABC:123
abc:123 is out but ABC:123a4 and ABC:123A4 are both present.
You can make things even easier changing the static split method to create the Set for you. The reason I didn't do that was to make things look familiar to you.
what is wrong with actually creating a little Model class to take care of all possible cases?
final class Model{
final String firstPart;
final String secondPart;
final int hashCode;
Model(String s){
String[] splitted=s.split(":");
firstPart=splitted[0];
secondPart=splitted[1];
hashCode=Objects.hash(firstPart.toLowerCase(),secondPart);
}
#Override
public boolean equals(Object o){
String[] splitted=o.toString().split(":");
return firstPart.equalsIgnoreCase(splitted[0]) && secondPard.equals(splitted[1]);
}
#Override
public int hashCode(){
return hashCode;
}
#Override
public String toString(){
return firstPart+":"+secondPart;
}
}
now create a set and etc:
Set<Model> set =new HashSet<Model>();
You can try this Java 8 one liner
Set<String> memberSet = memlist.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());
It will go through all strings in memlist convert them to uppercase and put them to a set.
That means of course that if your list contains "abc:123" but not "ABC:123", you will still get "ABC:123" in the set.
I have an instance of this Java class accessible in my Javascript program
public class ContentProvider {
public Object c(int n) {
switch (n) {
case 1: return 1.1;
case 2: return 2.2;
case 3: return 3.3;
case 4: return "4";
case 5: return new java.util.Date();
}
return null;
}
}
This is the code inside main():
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
engine.put("ctx", new ContentProvider());
res = engine.eval("ctx.c(1)");
System.out.printf("rhino:> %s (%s)%n"
, res
, res != null ? res.getClass().getName() : null
);
The simple expression ctx.c(1) prints:
rhino:> 1.1 (java.lang.Double)
Now here is what happens with ctx.c(1) + ctx.c(2):
rhino:> 1.12.2 (java.lang.String)
And finally (ctx.c(1) + ctx.c(2)) * ctx.c(3):
rhino:> nan (java.lang.Double)
Rhino is performing string concatenation instead of number arithmetics! The following program works as expected instead:
engine.put("a", 1.1);
engine.put("b", 2.2);
engine.put("c", 3.3);
res = engine.eval("(a + b) * c");
Outputs:
rhino:> 10,89 (java.lang.Double)
This is a strange feature of Rhino: a Java Number set with engine.put("one", new Double(1)) works as expected, while the result of a Java method depends on the return type declared by the method itself, which is read with the reflection API:
if it's a primitive, like double, it's converted to a Javascript number
otherwise it's handled like other host objects and the + means concatenation, either Object like in your sample as well as Double
You can configure this behavior with wrapFactory.setJavaPrimitiveWrap(false) on the WrapFactory in the current Context. This way the Rhino code can be kept in the bootstrap lines of your program and doesn't clutter ContentProvider (which I guess is some sort of configuration proxy)
From the live Javadoc of WrapFactory.isJavaPrimitiveWrap()
By default the method returns true to indicate that instances of
String, Number, Boolean and Character should be wrapped as any other
Java object and scripts can access any Java method available in these
objects
So you can set this flag to false to indicate that Java Number's should be converted to Javascript numbers. It takes just two lines of code
Context ctx = Context.enter();
ctx.getWrapFactory().setJavaPrimitiveWrap(false);
Here is the Gist with the full code I used to test
I created a value wrapper:
public static class JSValue extends sun.org.mozilla.javascript.internal.ScriptableObject
{
Object value;
public JSValue(Object value) {
this.value = value;
}
public String getClassName() {
return value != null? value.getClass().getName(): null;
}
#Override
public Object getDefaultValue(Class typeHint) {
if (typeHint == null || Number.class.isAssignableFrom(typeHint)) {
if (value instanceof Number)
return ((Number) value).doubleValue();
}
return toString();
}
#Override
public String toString() {
return value != null? value.toString(): null;
}
}
and an edit function:
public static class ContentProvider {
public Object c(int n) {
... return new JSValue(1.1);
Now the expression works as expected. Thanks all.
I am learning about Security and looking at storing secrets in the clear.
When I retrieve the contents of a private field, it returns an Object. My mal code correctly assumes and casts the Object as an int, however if I change/parse the field type from int secretInt = 42; to String secretInt = (new Integer(42).intValue()).tostring the Mal code fails miserably.
EDIT: The unusual wrapping (new Integer(42).intValue()).tostring is created by a automated parser, it is not written by a programmer.
how can I add robustness to Mal code so the assumption of the returned type is removed. Is this possible? I need to use this value as int param.
EDIT: 'String' is one example but the parser may choose a data-structure as suitably-inappropriate as byte[], char[].
This is my non-compliant code.
public final class SecretInClear implements Check4SecretsInClear {
//Non-Compliant: Secret int stored in Clear.
private final int secretInt = 42;
#Override
public boolean isSecretInt(int check) {
return (check == secretInt);
}
}
This is my mal code.
public class ReadClearSecret implements Tester {
//Example of running
public static void main(String[] args) {
String testResult = new ReadClearSecret().test(new SecretInClear());
System.out.println(testResult);
}
private Object readPrivateField(Object o, String fieldName) {
try {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(o);
} catch(Exception e) {
throw new IllegalArgumentExecption(e);
}
#Override
public String test(final Object secretChecks) {
final Check4SecretsInClear check4SecretsInClear = (Check4SecretsInClear)secretChecks;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("class:").
append(check4SecretsInClear.getClass().getSimpleName());
boolean bSecretInt = false;
String s = "";
try {
int secretInt = (Integer)readPrivateField(check4SecretsInClear,"secretInt"); //<<< HERE! It's cast as an integer!!!
bSecretInt = check4SecretsInClear.isSecretInt(secretInt); //<<< HERE! Param must be an int.
} catch (ClassCastException e) {
s = "," + e.getClass().getSimpleName();
} finally {
stringBuilder.append(" int:").append(bSecretInt).append(s);
s = "";
}
return stringBuilder.toString();
}
}
EDIT:
Instead of casting (int) from readPrivateField(). Instead I extract the string value String.valueOf(Object) or Object.toString(). I can then pass that string as a int param with new Integer(stringValue).
HOWEVER: If the parser chooses to represent secretInt as type byte[] the string value will be nuts and the mal code will be pwned. Any suggest to produce robustness against this?
The return type of Field.get() is an Object. If you need to know its class you can call Field.getType() but usually you don't need to know the type, only the information contained.
You could just do
String secret = "42";
or
#Override
public boolean isSecretInt(int check) {
return check / 6.0 == 7;
}
Don't use Integer to compare values, as this compare objects, not their values.
A shorter implementation
private Object readPrivateField(Object o, String fieldName) {
try {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(o);
} catch(Exception e) {
throw new IllegalArgumentExecption(e);
}
}
BTW: "What do you get if you multiply 6 by 9", for those who have read the Hitchhiker's Guide to the Galaxy.
System.out.println(Integer.toString(6 * 9, 13));
prints
42
;)
You can always use:
String value = String.valueOf(field.get(o));
To avoid caring what the type is and always give you a String.
CREDIT: Peter Lawrey
If it is suitably inappropriate you know the answer, it can't be don't generically in code. You need to read the byte code of isSecretInt to see how it is done, and for that a human is the simplest solution ;) –