Question

I have a String property that relates to calling a particular method.

I have this DTO, with an icon property

public class MyDto
{
    String icon; // "first", "second", "third" etc

    public String getIcon()
    { 
         return icon;
    }
}

In my interface I have these methods: (GWT ClientBundle)

public interface MyClientBundle extends ClientBundle
{
    @Source("icon_first.jpg")
    ImageResource icon_first();

    @Source("logo_second.jpg")
    ImageResource icon_second();

    @Source("icon_third.jpg")
    ImageResource icon_third();
}

I'm currently using an inefficient lookup with selection statements, but want to select the right method by building a string instead:

public ImageResource getValue(MyDto object)
{
    return getIconFromCode(object.getIcon());
}

private ImageResource getIconFromCode(String code)
{
    if(code.equalsIgnoreCase("first"))
    {
        return resources.icon_first();
    }
    else if(code.equalsIgnoreCase("second"))
    {
        return resources.icon_second();
    }
    else if(code.equalsIgnoreCase("third"))
    {
        return resources.icon_third();
    }
    else
    {
        return resources.icon_default();
    }
}

I want to build a string to select the right method instead, something like "icon_" + object.getIcon()+ "()"

Having done some research I understand I need to use reflection? How would this be accomplished?

Was it helpful?

Solution

The interface MyClientBundle should extends ClientBundleWithLookup instead of ClientBundle.

The ClientBundleWithLookup has a getResource(String name) method that lets you retrieve the resource from the resource name (method name).

OTHER TIPS

Cache the getter methods by the icon name from the annotation and use that cache to call the getters.

The code below only gets the annotations from interfaces one level up. If you need to go higher for some reason, just recursively call the cacheGettersFor() method.

There is no check here to make sure the icon getter methods have the right signature (take no arguments, return ImageResource), you'll want to add that if you use any of this code.

public class IconGetterCache {
    class IconGetterCacheForBundleType {
        private Map<String, Method> _iconGetters = new HashMap<>();
        private Class<?> _runtimeClass;

        private void cacheGettersFor(Class<?> forClazz) {
            for (Method method : forClazz.getMethods()) {
                Source iconSourceAnnotation = method.getAnnotation(Source.class);
                if (iconSourceAnnotation != null) {
                    _iconGetters.put(iconSourceAnnotation.value(), method);
                }
            }
        }

        IconGetterCacheForBundleType(final Class<?> runtimeClass) {
            _runtimeClass = runtimeClass;
            for (Class<?> iface : _runtimeClass.getInterfaces()) {
                cacheGettersFor(iface);
            }
            cacheGettersFor(_runtimeClass);
        }

        public ImageResource getIconFromBundle(ClientBundle bundle, String iconName) {

            if (!_runtimeClass.isAssignableFrom(bundle.getClass())) {
                throw new IllegalArgumentException("Mismatched bundle type");
            }

            Method getter = _iconGetters.get(iconName);
            if (getter == null) { return null; }
            try {
                return (ImageResource) getter.invoke(bundle);
            }
            catch (Throwable t) {
                throw new RuntimeException("Could not get icon", t);
            }
        }
    }

    private Map<Class<?>, IconGetterCacheForBundleType> _getterCaches = new HashMap<>();

    //main entry point, use this to get your icons
    public ImageResource getIconFromBundle(ClientBundle bundle, String iconName) {
        final Class<? extends ClientBundle> getterClass = bundle.getClass();
        IconGetterCacheForBundleType getterCache = _getterCaches.get(getterClass);
        if (getterCache == null) {
            _getterCaches.put(getterClass, getterCache = new IconGetterCacheForBundleType(getterClass));
        }
        return getterCache.getIconFromBundle(bundle, iconName);
    }
}


//Here's how I tested
IconGetterCache gc = new IconGetterCache();
MyClientBundle concreteClientBundle = new MyClientBundle() {
    @Override
    public ImageResource icon_first() {
       //return correct icon
    }

    @Override
    public ImageResource icon_second() {
        //return correct icon
    }

    @Override
    public ImageResource icon_third() {
        //return correct icon
    }
};
gc.getIconFromBundle(concreteClientBundle, "icon_first.jpg");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top