I want to parse objects where I have an input value. The object has subclasses.
I don't want to create an instance when I want to parse the string.
But I want to override the method 'parse'.
class A {
public A(object param) {
//code
}
public A parse(String input) {
//code
}
}
class B extends A {
public B parse(String input) {
//code
}
}
And when I parse the objects I want do it with reflection:
A newObject = Class.forName(className).getMethod("parse", myParseText);
Is there a nice way to do that.
For maximum flexibility I'd move the parsing code out into a separate parser class. The following code is for example a simplified implementation of a parser class I am using in a project myself:
public final class ParseUtil {
public interface IStringParser<E> {
public E parse(String s) throws ParseException;
}
private ParseUtil() {}
private static Map<Class<?>, IStringParser<?>> STRING_PARSERS = new ConcurrentHashMap<>();
public static <E> void registerStringParser(Class<E> c, IStringParser<E> p){
STRING_PARSERS.put(c, p);
}
#SuppressWarnings("unchecked")
public static <E> IStringParser<E> getStringParser(Class<E> c){
return (IStringParser<E>) STRING_PARSERS.get(c);
}
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser == null) {
throw new ParseException(clazz);
}
return stringParser.parse(s);
}
}
One can then register IStringParser implementations for arbitrary classes to the ParserUtil like so:
ParseUtil.registerStringParser(File.class, new IStringParser<File>() {
#Override
public File parse(String s)throws ParseException {
return new File(s);
}
});
Or using a lambda: ParseUtil.registerStringParser(File.class, s -> new File(s));
It is the responsibility of the caller to decide how to parse a class and when the IStringParser is registered/unregistered.
By moving the parsing code away from the class itself, it is easy to change the implementation later simply by registering a different IStringParser or by changing the parse method of the ParseUtil class. For example in my own project I use the following parse method that provides some reasonable defaults for well known Java classes and uses Gson to parse Objects for which no other IStringParser is registered:
public static <E> E parse(String s, Class<E> clazz) throws ParseException{
if (s == null || s.length() == 0 || clazz == null) {
throw new IllegalArgumentException();
}
IStringParser<E> stringParser = getStringParser(clazz);
if (stringParser != null) {
return stringParser.parse(s);
}
if (Number.class.isAssignableFrom(clazz)) {
// simple number
try {
if (clazz == Integer.class) {
return clazz.cast(Integer.parseInt(s));
}
if (clazz == Long.class) {
return clazz.cast(Long.parseLong(s));
}
if (clazz == Double.class) {
return clazz.cast(Double.parseDouble(s));
}
if (clazz == Float.class) {
return clazz.cast(Float.parseFloat(s));
}
if (clazz == Short.class) {
return clazz.cast(Short.parseShort(s));
}
if (clazz == Byte.class) {
return clazz.cast(Byte.parseByte(s));
}
} catch (NumberFormatException e) {
throw new ParseException(clazz, e);
}
}
if (clazz == String.class) {
return clazz.cast(s);
}
if (clazz == Character.class) {
if (s.length() == 1) {
return clazz.cast(s.charAt(0));
} else{
throw new ParseException("Unable to parse Character \"" + s + "\"");
}
}
if (clazz == Boolean.class) {
switch (s) {
case "true":
return clazz.cast(Boolean.TRUE);
case "false":
return clazz.cast(Boolean.FALSE);
case "1":
return clazz.cast(Boolean.TRUE);
case "0":
return clazz.cast(Boolean.FALSE);
default:
throw new ParseException("Unable to parse boolean \"" + s + "\"");
}
}
if (clazz == Class.class) {
try {
return clazz.cast(Class.forName(s));
} catch (ClassNotFoundException e) {
throw new ParseException(clazz, e);
}
}
if (Enum.class.isAssignableFrom(clazz)) {
#SuppressWarnings({ "rawtypes" })
Class c = (Class)clazz;
#SuppressWarnings("unchecked")
Object o = Enum.valueOf(c, s);
return clazz.cast(o);
}
E result = null;
try {
// if all else fails use Gson to parse the class
result = getGSON().fromJson(s, clazz);
} catch (JsonSyntaxException e) {
throw new ParseException(clazz. e);
}
if (result == null) {
throw new ParseException(clazz);
}
return result;
}
Related
i have source code call as PhpArray Class from JavaBridge
public final class PhpArray extends AbstractMap
{
private TreeMap t;
private HashMap m;
public PhpArray() {
this.t = new TreeMap(Request.PHP_ARRAY_KEY_COMPARATOR);
this.m = null;
}
#Override
public Object put(final Object key, final Object value) {
if (this.m != null) {
return this.m.put(key, value);
}
try {
return this.t.put(key, value);
}
catch (ClassCastException e) {
this.m = new HashMap(this.t);
this.t = null;
return this.m.put(key, value);
}
}
#Override
public Set entrySet() {
if (this.t != null) {
return this.t.entrySet();
}
return this.m.entrySet();
}
public int arraySize() {
if (this.t == null) {
throw new IllegalArgumentException("The passed PHP \"array\" is not a sequence but a dictionary");
}
if (this.t.size() == 0) {
return 0;
}
return 1 + this.t.lastKey();
}
}
i got an error in this code
public int arraySize() {
if (this.t == null) {
throw new IllegalArgumentException("The passed PHP \"array\" is not a sequence but a dictionary");
}
if (this.t.size() == 0) {
return 0;
}
return 1 + this.t.lastKey();
}
error which i got are
operator + undefined for the arguments type(s) int, object
from my lastKey() we call this methods
public K lastKey() {
return key(getLastEntry());
}
and here for getLastEntry
final Entry<K,V> getLastEntry() {
Entry<K,V> p = root;
if (p != null)
while (p.right != null)
p = p.right;
return p;
}
i want to know how to fix it ? i have try add after TreeMap like this TreeMap<String,String> i got still error. so i want to know how to fix it
from this call i see this is for return int
protected static final IntegerComparator PHP_ARRAY_KEY_COMPARATOR;
after i change my code as like this return 1 + (Integer)this.t.lastKey(); this code not get error again... i want to know suggestion from this forum is it ok for this code ?
Object ab = someMethod1()
if(ab != null) {
someMethod2(ab);
} else {
someMethod3()
}
converted to java optionals
Optional.ofNullable(someMethod1()).map( e -> someMethod2(ab))
.orElseGet(() -> someMethod3() );
but for the can you please help me out out to convert
// Multiple nested if else conditions are there , finding difficulty to convert to java 8 optionals
public void a() {
Object ab = someMethod1();
if(ab != null) {
Object cd = someMethod2();
if(cd != null) {
Object ef = someMethod3();
if( ef!= null) {
someMethod4();
} else {
elseMethod3()
}
} else {
elseMethod2()
}
} else {
elseMethod1();
}
}
public class a {
static String method1(String agr) {
return "method1";
}
static String method2(String agr) {
return "method2";
}
static String method3(String agr) {
return "method3";
}
public static Optional<String> cal(Optional<String> optionalS, Function<String, String> function, String agr) {
if (optionalS.isPresent())
return Optional.of(function.apply(agr));
return Optional.empty();
}
public static void main(String agr[]) {
cal(Optional.empty(), a::method1, "agr func1").orElseGet(() ->
cal(Optional.empty(), a::method2, "agr func2").orElseGet(() ->
cal(Optional.ofNullable("c"), a::method3, "agr func3").orElse("final result")
)
);
}
}
How to convert an instance of java.lang.reflect.Type into a jvm generic type signature?
Type type = method.getGenericReturnType();
String signature = toTypeSig(type);
For instance this Type:
Map<String, Map<?, ? super Integer>>
Must become this String:
"Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;"
How do people using orb.objectweb.asm.ClassWriter solve this?
I've done it iterating over the Type information.
But I'd prefer something more robust.
(I've posted my own solution as an answer because it's a bit long)
I guess you want to get the jvm signature from a class file (or did you mean from a source file?)
From a class following could be a solution
public class GetSignature {
// the method for which you want to retrieve the signature
Map<String, Map<?, ? super Integer>> someMethod() {
return null;
}
public static void main(String[] args) throws Exception {
// make the private method in class Method accessible
Method methodGenericSignature = Method.class.getDeclaredMethod(
"getGenericSignature",
(Class<?>[]) null
);
methodGenericSignature.setAccessible(true);
// get the signature from the method
Method method = GetSignature.class.getDeclaredMethod("someMethod",
(Class<?>[]) null
);
String returnValue = (String) methodGenericSignature.invoke(method,
(Object[]) null
);
System.out.println("signature: " + returnValue);
}
}
output
signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;
edit A small snipped to demonstrate how to get the signature using asm.
public class GetSignatureDemo {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("/tmp/GetSignature.class");
ClassReader classReader = new ClassReader(is);
classReader.accept(getClassVisitor(), 0);
}
private static ClassVisitor getClassVisitor() {
return new ClassVisitor(Opcodes.ASM4) {
#Override
public MethodVisitor visitMethod(int access, String name,
String descriptor, String signature, String[] exceptions) {
System.out.printf(
"method: %s descriptor: %s signature: %s%n",
name,
descriptor,
signature
);
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
};
}
}
sample output
method: someMethod descriptor: ()Ljava/util/Map; signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;
My final solution. I'm posting for reference. Since it might be useful when method.setAccessible(true) is not available.
This is more or less what I ended up using.
Because I needed to transform the generic return type into a class signature. And I had no use for the type variables.
It works well for methods without type parameters.
For methods with generic type parameters it tries to replace the parameters.
Processing the generic return type of this method: <P> List<P> someMethod()
results in "Ljava/util/List<Ljava/lang/Object;>;"
static String toGenericSignature(final Type type)
{
StringBuilder sb = new StringBuilder();
toGenericSignature(sb, type);
return sb.toString();
}
static void toGenericSignature(StringBuilder sb, final Type type)
{
if (type instanceof GenericArrayType)
{
sb.append("[");
toGenericSignature(sb, ((GenericArrayType) type).getGenericComponentType());
}
else if (type instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType) type;
sb.append('L');
sb.append(((Class) pt.getRawType()).getName().replace('.', '/'));
sb.append('<');
for (Type p : pt.getActualTypeArguments())
{
toGenericSignature(sb, p);
}
sb.append(">;");
}
else if (type instanceof Class)
{
Class clazz = (Class) type;
if (!clazz.isPrimitive() && !clazz.isArray())
{
sb.append('L');
sb.append(clazz.getName().replace('.', '/'));
sb.append(';');
}
else
{
sb.append(clazz.getName().replace('.', '/'));
}
}
else if (type instanceof WildcardType)
{
WildcardType wc = (WildcardType) type;
Type[] lowerBounds = wc.getLowerBounds();
Type[] upperBounds = wc.getUpperBounds();
boolean hasLower = lowerBounds != null && lowerBounds.length > 0;
boolean hasUpper = upperBounds != null && upperBounds.length > 0;
if (hasUpper && hasLower && Object.class.equals(lowerBounds[0]) && Object.class.equals(upperBounds[0]))
{
sb.append('*');
}
else if (hasLower)
{
sb.append("-");
for (Type b : lowerBounds)
{
toGenericSignature(sb, b);
}
}
else if (hasUpper)
{
if (upperBounds.length == 1 && Object.class.equals(upperBounds[0]))
{
sb.append("*");
}
else
{
sb.append("+");
for (Type b : upperBounds)
{
toGenericSignature(sb, b);
}
}
}
else
{
sb.append('*');
}
}
else if (type instanceof TypeVariable)
{
//sb.append("T");
//sb.append(((TypeVariable) type).getName());
//sb.append(";");
// work around: replaces the type variable with it's first bound.
toGenericSignature(sb, ((TypeVariable) type).getBounds()[0]);
}
else
{
throw new IllegalArgumentException("Invalid type: " + type);
}
}
I have a number of classes which can include one or more properties of the type TranslatableText. Also, some classes may have properties which themselves include such properties such as List<TranslatableText> or Map<String, TranslatableText>.
How would you go about scanning these classes in an efficient way, also picking up instances of TranslatableText in generic collections?
class Project{
String id;
TranslatableText name;
List<Action> actions;
}
class Action {
String id;
TranslatableText name;
TranslatableText description;
}
// getter & setters omitted
You can use a loop like this
// for super classes, use recursion.
for(Field f : obj.getClass().getDeclaredFields()) {
Class type = f.getType();
if (type == String.class || type == TranslatableText.class) {
Object value = f.get(object);
if (value != null)
map.put(f.getName(), value.toString());
}
Thanks to Peter Lawrey for the initial idea. This is what worked (so far) for me. I had to avoid circular recursion which is why I added the visited list. Arrays, Collections and Maps are scanned which is good enough for me at the moment.
/**
* Examine class members for TranslatableTexts. Traverse down into
* properties including Collections and Maps but ignoring java.lang classes
*
* #param obj
* #return
*/
public static Collection<? extends TranslatableText> getTranslatables(
Object obj, List<Object> visited)
{
List<TranslatableText> result = new ArrayList<TranslatableText>();
if (obj instanceof TranslatableText)
{
result.add((TranslatableText) obj);
return result;
}
if(visited.contains(obj))
return result;
visited.add(obj);
for (Field f : obj.getClass().getDeclaredFields())
{
f.setAccessible(true);
Class type = f.getType();
if (type.isPrimitive() == false
&& (type.getPackage() == null || type.getPackage()
.getName().startsWith("java.lang") == false))
{
try
{
Object value = f.get(obj);
if (value != null)
{
if (type.isArray())
{
Class arraytype = type.getComponentType();
if (arraytype == TranslatableText.class)
{
TranslatableText[] tt = (TranslatableText[]) value;
if (tt != null)
{
for (TranslatableText t : tt)
{
result.add(t);
}
}
}
}
else if (type == TranslatableText.class)
{
TranslatableText tt = (TranslatableText) value;
if (tt != null)
result.add(tt);
}
else if (value instanceof Collection)
{
for (Object o : (Collection<?>) value)
{
result.addAll(getTranslatables(o, visited));
}
}
else if (value instanceof Map)
{
for (Object o : ((Map) value).values())
{
result.addAll(getTranslatables(o, visited));
}
}
else
{
result.addAll(getTranslatables(value, visited));
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
return result;
}
I am using the following the following class org.apache.poi.hssf.usermodel.HSSFCell, with a list of the following methods:
void setCellValue(boolean value)
void setCellValue(java.util.Calendar value)
void setCellValue(java.util.Date value)
void setCellValue(double value)
void setCellValue(HSSFRichTextString value)
void setCellValue(java.util.Calendar value)
void setCellValue(HSSFRichTextString value)
Take note that, there are no methods with Object as method parameter.
Now, I have no way to determine my value class type during compiled time. I can only determine my value class type during runtime. Hence, how I can determine the correct method to be called, if I do not know the method signature during compiled time?
My code is below:
final int rowCount = tableModel.getRowCount();
for (int i = 0; i < rowCount; i++) {
final HSSFRow row = sheet.createRow(i + 1);
for (int j = 0; j < columnCount; j++) {
final Object object = tableModel.getValueAt(i, j);
final Class myClass = tableModel.getColumnClass(j);
// How to perform casting during compiled time, and invoke
// the setCellValue with correct signature?
if (object != null) {
row.createCell(j).setCellValue(??); // Does not accept Object!
}
}
}
Perhaps ugly if...else with instanceof would resolve my problem. However, if I do not want the ugly if...else with instanceof, is there any better way to do so?
One way of handling this is to load that list of methods at runtime into a Map and then for each call, use the Map. That is, something like this (where this code is simplified and omits error checking):
Map<? extends Object, Method> map;
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
map.put(method.getParameterTypes()[0], method);
}
}
then when you want to call this, look up the Method in the map by argument type and use that instance.
To show this off, again with simplified but this time full code. Note that to be fully general, the code gets a little more complicated, as is shown below. If you don't have to worry about primitives (which depends on your usage) or if you don't have to worry about interfaces or superclasses, then you can simplify the example below.
Also, if you can guarantee that there will be no overlap in interfaces or superclasses in the arguments that you have to worry about, you can move all of the complicated logic into initialization (which doesn't matter if it takes 1 ms longer). In this case, all of the logic in findMethodToInvoke() would be moved into the constructor, where you would loop over all interfaces and superclasses of each method you find and add them to your parameterTypeMap. If you do this optimization, then findMethodToInvoke() becomes a single line:
return parameterTypeMap.get(test.getClass());
but without this optimization and with full generality, here's my example of how to do this:
import java.lang.reflect.*;
import java.util.*;
public class Test {
private final Map<Object, Method> parameterTypeMap = new HashMap<Object, Method>();
private final Object[] tests = {Double.valueOf(3.1415),
Boolean.TRUE,
new Date(),
new GregorianCalendar(),
new HashMap<Object, Object>()};
public Test() {
Method[] methods = Setters.class.getMethods();
for (Method method : methods) {
if (method.getName().equals("setCellValue")) {
Class<?>[] clazzes = method.getParameterTypes();
if (clazzes.length != 1) {
continue;
}
if (clazzes[0].isPrimitive()) {
handlePrimitive(method, clazzes[0]);
}
parameterTypeMap.put(clazzes[0], method);
}
}
}
// See http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isPrimitive()
private void handlePrimitive(Method method, Class<?> clazz) {
if (clazz == Boolean.TYPE) {
parameterTypeMap.put(Boolean.class, method);
} else if (clazz == Double.TYPE) {
parameterTypeMap.put(Double.class, method);
} // ... and so on for the other six primitive types (void doesn't matter)
}
public void doTests(Setters setter) {
for (Object test : tests) {
Method method = findMethodToInvoke(test);
if (method == null) {
System.out.println("Nothing found for " + test.getClass());
continue;
}
try {
method.invoke(setter, test);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Method findMethodToInvoke(Object test) {
Method method = parameterTypeMap.get(test.getClass());
if (method != null) {
return method;
}
// Look for superclasses
Class<?> x = test.getClass().getSuperclass();
while (x != null && x != Object.class) {
method = parameterTypeMap.get(x);
if (method != null) {
return method;
}
x = x.getSuperclass();
}
// Look for interfaces
for (Class<?> i : test.getClass().getInterfaces()) {
method = parameterTypeMap.get(i);
if (method != null) {
return method;
}
}
return null;
}
public static void main(String[] args) {
Test test = new Test();
test.doTests(new Setters());
}
}
class Setters {
public void setCellValue(boolean value) {
System.out.println("boolean " + value);
}
public void setCellValue(double value) {
System.out.println("double " + value);
}
public void setCellValue(Calendar value) {
System.out.println("Calendar " + value);
}
public void setCellValue(Date value) {
System.out.println("Date " + value);
}
public void setCellValue(Map<?, ?> value) {
System.out.println("Map " + value);
}
}
If you don't have subclasses (you can still do it if you do, but it will be harder, let me know if you do) you could use reflection:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
final Object o;
if(argv.length == 0)
{
o = "Hello";
}
else
{
o = Integer.valueOf(42);
}
callFoo(o);
}
private static void callFoo(final Object o)
throws NoSuchMethodException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
Method method;
method = Main.class.getDeclaredMethod("foo", o.getClass());
method.invoke(null, o);
}
private static void foo(final String val)
{
System.out.println("foo(String) -> " + val);
}
private static void foo(final Integer val)
{
System.out.println("foo(Integer) -> " + val);
}
}
The downside is that you don't have the compiler tell you if you try to call a method that does not exist.
The exception handling in the code above is utter crap, but I wanted to focus on the reflection part.
Using instance of is better from the point of view that it has compile time type safety. Reflection will not have to be updated if new methods are added.
I think the instanceof is the way to go. If you think it makes your code ugly extract that instanceof expressions into an auxiliary method:
public void setCellValue(HSSFCell cell, Object value) {
if (null == cell)
throw new IllegalArgumentException("cell");
if (null == value)
throw new IllegalArgumentException("value");
if (value instanceof Double)
cell.setCellValue((Double)value); // auto-boxing will handle this
else if (value instanceof Boolean) {
cell.setCellValue((Boolean)value); // auto-boxing will handle this
} else if (value instanceof Calendar) {
cell.setCellValue((Calendar)value);
} else if ...
.....
} else {
throw new UnsupportedTypeException("Object of class " + Value.class.getName() + " not supported.");
}
}
Alternately you can use reflection. Even with reflection I think you still have to do some customization for the primitive types because the auto-boxing doesn't work for getMethod() ...
public void invokeSetCellValue(HSSFCell cell, Object obj) {
try {
Class<?> clazz = obj.getClass();
if (obj instanceof Double) {
clazz = double.class;
} else if (obj instanceof Boolean) {
clazz = boolean.class;
}
Method m = HSSFCell.class.getMethod("setCellValue", clazz);
m.invoke(cell, obj);
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}