Use HashMap to create dynamic function call - java

I am trying to create a function call using hashmap.
For example,
Hashmap--
"a" -> "b"
"c" -> "d"
Now I should parse this Hashmap and create a function like this-
someFun("{a:#,c:#}",new SomeClass(b),new SomeClass(d));
As you can see, a and c are keys of hashmap(first argument) ; b and d are used to create objects (second argument and so on..)
someFun parameters depend on HashMap size..
I am confused! Because I can loop through the map to get Keys and easily create the first argument.
For the second argument, I can use the value to create Objects.
But now how do I add these together to make the function call as specified?
Any help would be very much appreciated :)
P.S: My question is not about getting values/keys from hashmap,but using them to create a function call something as specified.I am not allowed to change someFun consider it as API call.

Is it something like this, that you need:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
public class Draft {
public static class SomeClass{
final private String val;
public SomeClass(String val) {
this.val = val;
}
}
public void someFun(String str, SomeClass ... classes) {
System.out.println("someFun" + str + Arrays.toString(classes));
}
public static void main(String[] args) {
HashMap<String, String> keyToParam = new HashMap<>();
keyToParam.put("a", "b");
keyToParam.put("c", "d");
String strArg = null;
SomeClass[] classes = new SomeClass[keyToParam.size()];
int pointer = 0;
for(Entry<String, String> entry: keyToParam.entrySet()) {
strArg += entry.getKey() + ":#";
classes[pointer++] = new SomeClass(entry.getValue());
}
new Draft().someFun(strArg, classes);
}
}

First, you should define function like this:
public void someFun(String str, SomeClass[] someClasses) {
// do something
}
Or this:
public void someFun(String str, SomeClass... someClass) {
// do something
}
The former would be better because it's easy to call it by Java Reflection.
Then go through the hashmap and concentrate all keys to a string someString as the first parameter. While doing the iteration, you put all the values into an array someClasses as the second parameter.
Finally get the method and invoke it by (assume that we use SomeObject someObject to call the function):
Method method = SomeObject.getClass().getMethod("someFun");
method.invoke(someObject, new Object[] {someString, someClasses});

To get the HapsMap Key values
HashMap<Object, Object> hashMap= new HashMap<Object,Object>();
...
Set<Object> keyValueSet=hashMap.keySet();
for ( Object keyValue : keyValueSet) {
//you get the key from keyValue and the corresponding value from hashMap using this
hashMap.get(keyValue);
}
The rest is your logic, you can use the values in any place you want.

Try this way...
try {
Object obj = new Object();// Create a new instance of the class that contain your method.
Method m = obj.getClass().getMethod("methodName", param1.class, param2.class, ..);
// In your case Method m = obj.getClass().getMethod("someFun", String.class,SomeClass.class,SomeClass.class);
m.invoke(obj, "{a:#,c:#}", new SomeClass(b),new SomeClass(d));
}catch (SecurityException e) {
// ...
} catch (NoSuchMethodException e) {
// ...
}catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
Method implementation :
public static void someFun(String name, SomeClass classes ...) {
for (SomeClass cls : classes) {
//Logic here
}
}

Related

Concatenate object values regarding name of fields in alphabetical order

I'm looking for a method to concatenate object values regarding name of fields in alphabetical order.
Example:
public Class Request {
private String number;
private String amount;
private String currency;
}
Request request = new Request();
request.setNumber("tata");
request.setCurrency("toto");
With this, my method should return tototata.
Method must be generic:
public static String concatenate(Object object) { ...}
null values must not be concatenated.
I already checked out Apache Commons BeanUtils and Java 8 streams, but found nothing nice.
Thanks you Andrew Tobilko, i was doing this (working)
public static String concatenateAlphabetically(Object object) {
Map<String, String> map = null;
try {
map = BeanUtils.describe(object);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
// remove class attribute generating from BeanUtils.describe
map.remove("class");
map.values().removeAll(Collections.singleton(null));
Map<String, String> treeMap = new TreeMap<String, String>(map);
return treeMap.values().stream().collect(Collectors.joining());
}
But i don't like to use BeanUtils like this, i prefer your method.
I just add f.setAccessible(true); to access to private fields
You may write this by iterating over all declared fields in the given object.
public static String concatenate(Object object) {
return Arrays.stream(object.getClass().getDeclaredFields())
.filter(f -> f.getType() == String.class)
.sorted(Comparator.comparing(Field::getName))
.map(f -> {
try { return (String)f.get(object); }
catch (IllegalAccessException e) { return null; }
})
.filter(Objects::nonNull)
.collect(Collectors.joining());
}

Java - Make a library class comparable

Is it possible to make library classes comparable without extending them?
import org.json.JSONObject;
LinkedList<JSONObject> list = getListFromFunction();
TreeSet<JSONObject> treeSet = new TreeSet<JSONObject>(list);
Making a TreeSet here is not possible as JSONObject is not comparable. How can I "attach" a custom comparator to JSONObject? (There is a unique property, say "_some_id" to compare with)
We can use Comparator in such a case and handle the scenario. Please refer the below example.
Main Class
public class ComparatorTest{
public static void main(String[] ar) {
// System.out.println(new Sample().stringTimes("vivek", 5));
JSONObject emp1 = new JSONObject();
JSONObject emp2 = new JSONObject();
try {
emp1.put("department", "IT");
emp1.put("name", "bvivek");
emp1.put("id", 1);
emp2.put("department", "IT");
emp2.put("name", "avikash");
emp2.put("id", 2);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
List<JSONObject> employess = new ArrayList<JSONObject>();
employess.add(emp1);//add to list
employess.add(emp2);//add to list
System.out.println(employess);//unsorted, as is
Collections.sort(employess, new JSONComparator("name"));
System.out.println(employess);//sorted as per the field
//using treeSet
TreeSet<JSONObject> jsonInTree = new TreeSet<JSONObject>(new JSONComparator("id"));
jsonInTree.addAll(employess);
System.out.println(jsonInTree);//using the tree implementation
}
}
JSONComparator
class JSONComparator implements Comparator<JSONObject> {
private String fieldToCompare;
public JSONComparator(String fieldToCompare) {
this.fieldToCompare = fieldToCompare;
}
#Override
public int compare(JSONObject o1, JSONObject o2) {
String id1 = "";
String id2 = "";
try {
id1 = o1.getString(this.fieldToCompare);
id2 = o2.getString(this.fieldToCompare);
} catch (JSONException e) {
}
return id1.compareTo(id2);
}
}
The simplest way to do something like this will work for any classes that aren't comparable. The way you do this is by creating your own comparing method, you can do this in a way like:
public static int compareJSONObjects(JSONObject obj1, JSONObject obj2){
if(obj1.getField()>obj2.getField()){
return 1;
}else{
return -1;
}
}
now when you call list.sort() you can create your own Comparator like this:
list.sort( (obj1, obj2) -> compareJSONObject(obj1, obj2) );
by doing this you cut down on the amount of lines required, as this entire thing could be shortened to 1 line by using ternary and doing:
list.sort( (obj1, obj2) -> obj1.getField()>obj2.getField() ? 1 : -1 );

Invoking method on actual parameter not declared in formal parameter type

I am an experienced programmer but a Java beginner. I have a benchmarking method that accepts a parameter of type Map and performs some tests on it. It can be invoked on a HashMap, Hashtable, IdentityHashMap, TreeMap etc because these all implement Map. They also all implement Cloneable, but Eclipse tells me I am not allowed to invoke the clone() method.
private static double[] timeMapRemoves(Map<String,Integer> map,
Collection<String> data,
int reps) {
Map<String,Integer> map_clone = map.clone(); // OOPS -- "clone not accessible"
So I delve into the Oracle website and I come up with a solution of sorts
Map<String,Integer> map_clone = null;
Method clone = null;
try {
clone = map.getClass().getMethod("clone", null);
map_clone = (Map<String,Integer>)clone.invoke(map, null);
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
I feel that I may, like Drool Rockworm, have delved too deep and missed the canonical solution.
clone() is protected which means it is only accessible from a subclass or that very same package.
Reiteration from the comments:
It all depends on the context from which it is called, and if that context is the same type then you can call the protected method. Here the context is a different type so it cannot call it.
When you change the parameter to HashMap<K, V> for example you can call it because HashMap overrides the clone() method with a public modifier. So in short: you can't do that with a simple Map<K, V> declaration.
This means a situation like this will work:
class X {
public X(){
X newX = new X().clone();
}
}
but this won't:
class X {
public X(){
String newString = "hello".clone();
}
}
But then again, this will:
class X implements Map<String, String>{
public X(){
Map<String, String> map = new HashMap<>().clone();
}
}
And so will this:
private static double[] timeMapRemoves(HashMap<String,Integer> map,
Collection<String> data,
int reps) {
Map<String, String> someMap = (Map<String, String>) map.clone();
}
Notice how I changed the parameter to HashMap<String,Integer>.
The reason for why this works is very simple: HashMap defines its own clone() method.
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
result.table = new Entry[table.length];
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);
return result;
}

Java—how can I dynamically reference an object's property?

In javascript, I can do this:
function MyObject(obj) {
for (var property in obj) {
this[property] = obj[property];
}
}
Can I do anything close in Java?
class MyObject {
String myProperty;
public MyObject(HashMap<String, String> props) {
// for each key in props where the key is also the name of
// a property in MyObject, can I assign the value to this.[key]?
}
}
Not that I disagree with Joel's answer, but I do not think it is not quite that difficult, if you essentially just want a best effort. Essentially check if it is there, and if it is try to set. If it works great if not, oh well we tried. For example:
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class MyObject {
protected String lorem;
protected String ipsum;
protected int integer;
public MyObject(Map<String, Object> valueMap){
for (String key : valueMap.keySet()){
setField(key, valueMap.get(key));
}
}
private void setField(String fieldName, Object value) {
Field field;
try {
field = getClass().getDeclaredField(fieldName);
field.set(this, value);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Map<String, Object> valueMap = new HashMap<String, Object>();
valueMap.put("lorem", "lorem Value");
valueMap.put("ipsum", "ipsum Value");
valueMap.put("integer", 100);
valueMap.put("notThere", "Nope");
MyObject f = new MyObject(valueMap);
System.out.println("lorem => '"+f.lorem+"'");
System.out.println("ipsum => '"+f.ipsum+"'");
System.out.println("integer => '"+f.integer+"'");
}
}
Yes, you can do it by reflection with something along the following lines:
/**
* Returns a list of all Fields in this object, including inherited fields.
*/
private List<Field> getFields() {
List<Field> list = new ArrayList<Field>();
getFields(list, getClass());
return list;
}
/**
* Adds the fields of the provided class to the List of Fields.
* Recursively adds Fields also from super classes.
*/
private List<Field> getFields(List<Field> list, Class<?> startClass) {
for (Field field : startClass.getDeclaredFields()) {
list.add(field);
}
Class<?> superClass = startClass.getSuperclass();
if(!superClass.equals(Object.class)) {
getFields(list, superClass);
}
}
public void setParameters(Map<String, String> props) throws IllegalArgumentException, IllegalAccessException {
for(Field field : getFields()) {
if (props.containsKey(field.getName())) {
boolean prevAccessible = field.isAccessible();
if (!prevAccessible) {
/*
* You're not allowed to modify this field.
* So first, you modify it to make it modifiable.
*/
field.setAccessible(true);
}
field.set(this, props.get(field.getName()));
/* Restore the mess you made */
field.setAccessible(prevAccessible);
}
}
}
However, if you are not very familiar with Java, this approach should be avoided if at all possible, as it is somewhat dangerous and error prone. For instance, there is no guarantee that the Field you are attempting to set are actually expecting a String. If it is the case that they are not, your program will crash and burn.
First, I would use a map if at all possible:
class MyObject {
// String myProperty; // ! not this
HashMap<String,String> myProperties; // use this instead
}
but let's say you wanted to set the fields dynamically.
public MyObject(HashMap<String, String> props) {
for (Map.Entry<String,String> entry : props.entrySet()) {
Field field = this.getClass().getField(entry.getKey());
field.set(this, entry.getValue());
}
}
of course, you will want to use a try/catch in the above constructor.
Well, if you really want to go down the reflection raod, then I suggest to have a look at the Introspector class and get the list of PropertyDescriptors from the BeanInfo.

Invoking all setters within a class using reflection

I have a domain object, that for the purposes of this question I will call Person with the following private variables:
String name
int age
Each of these have getters and setters. Now I also have a Map<String, String> with the following entries:
name, phil
age, 35
I would like to populate a list of all setter methods within the class Person and then looping through this list and invoking each method using the values from the map.
Is this even possible as I cannot see any examples close to this on the net. Examples are very much appreciated.
Sure it's possible! You can get all methods that start with "set" back by doing this:
Class curClass = myclass.class;
Method[] allMethods = curClass.getMethods();
List<Method> setters = new ArrayList<Method>();
for(Method method : allMethods) {
if(method.getName().startsWith("set")) {
setters.add(method);
}
}
Now you've got the methods. Do you already know how to call them for your instance of the class?
Have you tried BeanUtils.populate()) from Apache Commons BeanUtils?
BeanUtils.populate(yourObject, propertiesMap);
This is a full solution that verifies output class beforehand and consequently calls setters for all the properties that the map contains. It uses purely java.beans and java.lang.reflect.
public Object mapToObject(Map<String, Object> input, Class<?> outputType) {
Object outputObject = null;
List<PropertyDescriptor> outputPropertyDescriptors = null;
// Test if class is instantiable with default constructor
if(isInstantiable(outputType)
&& hasDefaultConstructor(outputType)
&& (outputPropertyDescriptors = getPropertyDescriptors(outputType)) != null) {
try {
outputObject = outputType.getConstructor().newInstance();
for(PropertyDescriptor pd : outputPropertyDescriptors) {
Object value = input.get(pd.getName());
if(value != null) {
pd.getWriteMethod().invoke(outputObject, value);
}
}
} catch (InstantiationException|IllegalAccessException|InvocationTargetException|NoSuchMethodException e) {
throw new IllegalStateException("Failed to instantiate verified class " + outputType, e);
}
} else {
throw new IllegalArgumentException("Specified outputType class " + outputType + "cannot be instantiated with default constructor!");
}
return outputObject;
}
private List<PropertyDescriptor> getPropertyDescriptors(Class<?> outputType) {
List<PropertyDescriptor> propertyDescriptors = null;
try {
propertyDescriptors = Arrays.asList(Introspector.getBeanInfo(outputType, Object.class).getPropertyDescriptors());
} catch (IntrospectionException e) {
}
return propertyDescriptors;
}
private boolean isInstantiable(Class<?> clazz) {
return ! clazz.isInterface() && ! Modifier.isAbstract(clazz.getModifiers());
}
private boolean hasDefaultConstructor(Class<?> clazz) {
try {
clazz.getConstructor();
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
I think you could use a library, the Apache Commons BeanUtils. If you have a map that contains field and value pairs, the class PropertyUtils can help you:
Person person = new Person();
for(Map.Entry<String, Object> entry : map.entrySet())
PropertyUtils.setProperty(person, entry.getKey(), entry.getValue());

Categories

Resources