package com.googlecode.gwt.test.internal.resources; import com.google.gwt.core.client.GWT; import com.google.gwt.resources.client.*; import com.googlecode.gwt.test.exceptions.GwtTestResourcesException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.List; /** * Builder in charge of the creation of {@link ResourcePrototype} proxies. <strong>For internal use * only.</strong> * * @author Gael Lazzari */ public class ResourcePrototypeProxyBuilder { private static class ResourcePrototypeInvocationHandler implements InvocationHandler { private ResourcePrototypeCallback callback; private String name; private Class<?> ownerClass; private Class<?> proxiedClass; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("getName".equals(method.getName())) { return name; } else if ("toString".equals(method.getName())) { return callback.getClass().getName() + " generated for '" + ownerClass.getName() + "." + name + "()'"; } else if ("hashCode".equals(method.getName())) { return callback.hashCode(); } else if ("equals".equals(method.getName())) { return areEqual(callback, args[0]); } else { Object result = callback.call(method, args); if (result != null) { return result; } else { throw new GwtTestResourcesException("Not managed method '" + method.getName() + "' for generated '" + proxiedClass.getName() + "' proxy"); } } } private boolean areEqual(ResourcePrototypeCallback myCallback, Object object) { if (!Proxy.isProxyClass(object.getClass())) { return false; } InvocationHandler ih = Proxy.getInvocationHandler(object); if (ResourcePrototypeInvocationHandler.class != ih.getClass()) { return false; } ResourcePrototypeCallback itsCallback = ((ResourcePrototypeInvocationHandler) ih).callback; return myCallback.equals(itsCallback); } } public static ResourcePrototypeProxyBuilder createBuilder(Class<?> proxiedClass, Class<?> ownerClass) { return new ResourcePrototypeProxyBuilder(proxiedClass, ownerClass); } private String name; private final Class<?> ownerClass; private final Class<?> proxiedClass; private String text; private List<URL> urls; private ResourcePrototypeProxyBuilder(Class<?> proxiedClass, Class<?> ownerClass) { if (!proxiedClass.isInterface()) { throw new GwtTestResourcesException("Cannot create a resource proxy instance for '" + proxiedClass.getName() + "' because it is not an interface"); } this.proxiedClass = proxiedClass; this.ownerClass = ownerClass; } public Object build() { ResourcePrototypeCallback callback; if (TextResource.class.isAssignableFrom(proxiedClass)) { callback = urls != null ? new TextResourceCallback(urls) : new TextResourceCallback(text); } else if (CssResource.class.isAssignableFrom(proxiedClass)) { callback = urls != null ? new CssResourceCallback(urls) : new CssResourceCallback(text); } else if (DataResource.class.isAssignableFrom(proxiedClass)) { callback = new DataResourceCallback(computeUrl(urls, ownerClass)); } else if (ImageResource.class.isAssignableFrom(proxiedClass)) { callback = new ImageResourceCallback(computeImageUrl(urls, ownerClass)); } else { throw new GwtTestResourcesException("Not managed return type for ClientBundle : " + proxiedClass.getName()); } InvocationHandler resourceInvocationHandler = generateInvocationHandler(callback); return Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[]{proxiedClass}, resourceInvocationHandler); } public ResourcePrototypeProxyBuilder name(String name) { this.name = name; return this; } public ResourcePrototypeProxyBuilder resourceURL(URL url) { urls = new ArrayList<URL>(1); urls.add(url); return this; } public ResourcePrototypeProxyBuilder resourceURLs(List<URL> url) { this.urls = url; return this; } public ResourcePrototypeProxyBuilder text(String text) { this.text = text; return this; } private String computeImageUrl(List<URL> resourceURLs, Class<?> resourceClass) { if (resourceURLs.size() > 1) { throw new GwtTestResourcesException("Too many ImageResource files found for method '" + ownerClass.getName() + "." + name + "()'"); } return computeUrl(resourceURLs, resourceClass); } private String computeUrl(List<URL> resourceURLs, Class<?> resourceClass) { StringBuilder sb = new StringBuilder(); for (URL url : resourceURLs) { sb.append(extractFileName(url, resourceClass)).append("_"); } return GWT.getModuleBaseURL() + sb.substring(0, sb.length() - 1); } private String extractFileName(URL resourceURL, Class<?> resourceClass) { return resourceURL.getPath().substring(resourceURL.getPath().lastIndexOf('/') + 1); } private InvocationHandler generateInvocationHandler(final ResourcePrototypeCallback callback) { ResourcePrototypeInvocationHandler ih = new ResourcePrototypeInvocationHandler(); ih.callback = callback; ih.name = name; ih.ownerClass = ownerClass; ih.proxiedClass = proxiedClass; return ih; } }