1919import java .io .FileInputStream ;
2020import java .io .IOException ;
2121import java .io .InputStream ;
22+ import java .lang .reflect .InvocationTargetException ;
23+ import java .lang .reflect .Method ;
24+ import java .math .BigDecimal ;
25+ import java .math .BigInteger ;
2226import java .net .URI ;
2327import java .nio .file .FileVisitOption ;
2428import java .nio .file .Files ;
2933import java .util .Collection ;
3034import java .util .Comparator ;
3135import java .util .List ;
36+ import java .util .Map ;
3237import java .util .Properties ;
3338import java .util .UUID ;
39+ import java .util .function .Function ;
3440import java .util .regex .Matcher ;
3541import java .util .regex .Pattern ;
42+ import java .util .stream .Collectors ;
3643
44+ import com .google .common .collect .ImmutableMap ;
3745import com .google .protobuf .ByteString ;
3846import com .google .protobuf .Timestamp ;
3947import org .apache .commons .compress .archivers .ArchiveEntry ;
@@ -60,6 +68,26 @@ public final class Utils {
6068 private static final Config config = Config .getConfig ();
6169 private static final int MAX_LOG_STRING_LENGTH = config .maxLogStringLength ();
6270
71+ private static final Map <Class <?>, Function <String , ?>> STRING_CONVERSIONS_BY_TYPE =
72+ new ImmutableMap .Builder <Class <?>, Function <String , ?>>()
73+ .put (Byte .class , Byte ::valueOf )
74+ .put (byte .class , Byte ::valueOf )
75+ .put (Short .class , Short ::valueOf )
76+ .put (short .class , Short ::valueOf )
77+ .put (Integer .class , Integer ::valueOf )
78+ .put (int .class , Integer ::valueOf )
79+ .put (Long .class , Long ::valueOf )
80+ .put (long .class , Long ::valueOf )
81+ .put (Float .class , Float ::valueOf )
82+ .put (float .class , Float ::valueOf )
83+ .put (Double .class , Double ::valueOf )
84+ .put (double .class , Double ::valueOf )
85+ .put (Boolean .class , Boolean ::valueOf )
86+ .put (boolean .class , Boolean ::valueOf )
87+ .put (BigInteger .class , BigInteger ::new )
88+ .put (BigDecimal .class , BigDecimal ::new )
89+ .build ();
90+
6391 /**
6492 * Generate parameter hash for the given chaincode path,func and args
6593 *
@@ -408,6 +436,76 @@ public static String toHexString(String bytes) {
408436
409437 }
410438
439+ /**
440+ * Lookup method by name and params
441+ * If there are no strict matches - trying to convert args to appropriate type
442+ * Useful for building network configs from yaml files
443+ * */
444+ public static Object invokeMethod (final Object target , final String methodName , final Class <?>[] parameterTypes , final Object [] args ) throws InvocationTargetException ,
445+ IllegalAccessException , NoSuchMethodException {
446+ if (parameterTypes .length != args .length ) {
447+ throw new IllegalArgumentException ("Parameters types " + java .util .Arrays .toString (parameterTypes ) +
448+ " do not match arguments " + java .util .Arrays .toString (args ));
449+ }
450+
451+ Method method = lookupMethod (target .getClass (), methodName , parameterTypes );
452+
453+ //convert args to founded method param's types
454+ Object [] coercedArgs = new Object [args .length ];
455+ Class <?>[] methodParameterTypes = method .getParameterTypes ();
456+ for (int i = 0 ; i < args .length ; i ++) {
457+ coercedArgs [i ] = convertArgumentToType (args [i ], methodParameterTypes [i ]);
458+ }
459+
460+ return method .invoke (target , coercedArgs );
461+ }
462+
463+ private static Method lookupMethod (final Class <?> cls , final String name , final Class <?>[] parameterTypes ) throws NoSuchMethodException {
464+ try {
465+ return cls .getMethod (name , parameterTypes );
466+ } catch (NoSuchMethodException originalException ) {
467+ //trying to find method with same name and parameters count
468+ List <Method > candidates = java .util .Arrays .stream (cls .getMethods ())
469+ .filter (it -> it .getName ().equals (name ))
470+ .filter (it -> it .getParameterCount () == parameterTypes .length )
471+ .collect (Collectors .toList ());
472+
473+ if (candidates .isEmpty ()) {
474+ throw originalException ;
475+ }
476+
477+ //if there is only one candidate - return it
478+ if (candidates .size () == 1 ) {
479+ return candidates .get (0 );
480+ }
481+
482+ //else, it could be same method declared in hierarchy (override). They must be with equal parameter types
483+ for (int i = 0 ; i < candidates .size () - 1 ; i ++) {
484+ Class <?>[] types1 = candidates .get (i ).getParameterTypes ();
485+ Class <?>[] types2 = candidates .get (i + 1 ).getParameterTypes ();
486+ //otherwise - we found methods with different params and can't choose one
487+ if (!java .util .Arrays .equals (types1 , types2 )) {
488+ throw originalException ;
489+ }
490+ }
491+
492+ return candidates .get (0 );
493+ }
494+ }
495+
496+ private static Object convertArgumentToType (final Object arg , final Class <?> type ) {
497+ if (arg .getClass ().equals (type ) || !(arg instanceof String )) {
498+ return arg ;
499+ }
500+
501+ Function <String , ?> convert = STRING_CONVERSIONS_BY_TYPE .get (type );
502+ if (null == convert ) {
503+ throw new IllegalArgumentException ("Unable to convert \" " + arg + "\" to " + type .getTypeName ());
504+ }
505+
506+ return convert .apply ((String ) arg );
507+ }
508+
411509 /**
412510 * Private constructor to prevent instantiation.
413511 */
0 commit comments