1*dc5640d1SHerbert Xue package example.xml; 2*dc5640d1SHerbert Xue 3*dc5640d1SHerbert Xue import com.google.inject.Binder; 4*dc5640d1SHerbert Xue import com.google.inject.Key; 5*dc5640d1SHerbert Xue import com.google.inject.Module; 6*dc5640d1SHerbert Xue import com.google.inject.Provider; 7*dc5640d1SHerbert Xue import java.io.InputStreamReader; 8*dc5640d1SHerbert Xue import java.io.Reader; 9*dc5640d1SHerbert Xue import java.lang.reflect.InvocationTargetException; 10*dc5640d1SHerbert Xue import java.lang.reflect.Method; 11*dc5640d1SHerbert Xue import java.lang.reflect.Type; 12*dc5640d1SHerbert Xue import java.net.URL; 13*dc5640d1SHerbert Xue import java.util.ArrayList; 14*dc5640d1SHerbert Xue import java.util.List; 15*dc5640d1SHerbert Xue import org.xml.sax.Attributes; 16*dc5640d1SHerbert Xue import org.xml.sax.Locator; 17*dc5640d1SHerbert Xue import safesax.Element; 18*dc5640d1SHerbert Xue import safesax.ElementListener; 19*dc5640d1SHerbert Xue import safesax.Parsers; 20*dc5640d1SHerbert Xue import safesax.RootElement; 21*dc5640d1SHerbert Xue import safesax.StartElementListener; 22*dc5640d1SHerbert Xue 23*dc5640d1SHerbert Xue public class XmlBeanModule implements Module { 24*dc5640d1SHerbert Xue 25*dc5640d1SHerbert Xue final URL xmlUrl; 26*dc5640d1SHerbert Xue 27*dc5640d1SHerbert Xue Locator locator; 28*dc5640d1SHerbert Xue Binder originalBinder; 29*dc5640d1SHerbert Xue BeanBuilder beanBuilder; 30*dc5640d1SHerbert Xue XmlBeanModule(URL xmlUrl)31*dc5640d1SHerbert Xue public XmlBeanModule(URL xmlUrl) { 32*dc5640d1SHerbert Xue this.xmlUrl = xmlUrl; 33*dc5640d1SHerbert Xue } 34*dc5640d1SHerbert Xue configure(Binder binder)35*dc5640d1SHerbert Xue public void configure(Binder binder) { 36*dc5640d1SHerbert Xue this.originalBinder = binder; 37*dc5640d1SHerbert Xue 38*dc5640d1SHerbert Xue try { 39*dc5640d1SHerbert Xue RootElement beans = new RootElement("beans"); 40*dc5640d1SHerbert Xue locator = beans.getLocator(); 41*dc5640d1SHerbert Xue 42*dc5640d1SHerbert Xue Element bean = beans.getChild("bean"); 43*dc5640d1SHerbert Xue bean.setElementListener(new BeanListener()); 44*dc5640d1SHerbert Xue 45*dc5640d1SHerbert Xue Element property = bean.getChild("property"); 46*dc5640d1SHerbert Xue property.setStartElementListener(new PropertyListener()); 47*dc5640d1SHerbert Xue 48*dc5640d1SHerbert Xue Reader in = new InputStreamReader(xmlUrl.openStream()); 49*dc5640d1SHerbert Xue Parsers.parse(in, beans.getContentHandler()); 50*dc5640d1SHerbert Xue } catch (Exception e) { 51*dc5640d1SHerbert Xue originalBinder.addError(e); 52*dc5640d1SHerbert Xue } 53*dc5640d1SHerbert Xue } 54*dc5640d1SHerbert Xue 55*dc5640d1SHerbert Xue /** Handles "binding" elements. */ 56*dc5640d1SHerbert Xue class BeanListener implements ElementListener { 57*dc5640d1SHerbert Xue start(final Attributes attributes)58*dc5640d1SHerbert Xue public void start(final Attributes attributes) { 59*dc5640d1SHerbert Xue Binder sourced = originalBinder.withSource(xmlSource()); 60*dc5640d1SHerbert Xue 61*dc5640d1SHerbert Xue String typeString = attributes.getValue("type"); 62*dc5640d1SHerbert Xue 63*dc5640d1SHerbert Xue // Make sure 'type' is present. 64*dc5640d1SHerbert Xue if (typeString == null) { 65*dc5640d1SHerbert Xue sourced.addError("Missing 'type' attribute."); 66*dc5640d1SHerbert Xue return; 67*dc5640d1SHerbert Xue } 68*dc5640d1SHerbert Xue 69*dc5640d1SHerbert Xue // Resolve 'type'. 70*dc5640d1SHerbert Xue Class<?> type; 71*dc5640d1SHerbert Xue try { 72*dc5640d1SHerbert Xue type = Class.forName(typeString); 73*dc5640d1SHerbert Xue } catch (ClassNotFoundException e) { 74*dc5640d1SHerbert Xue sourced.addError(e); 75*dc5640d1SHerbert Xue return; 76*dc5640d1SHerbert Xue } 77*dc5640d1SHerbert Xue 78*dc5640d1SHerbert Xue // Look for a no-arg constructor. 79*dc5640d1SHerbert Xue try { 80*dc5640d1SHerbert Xue type.getConstructor(); 81*dc5640d1SHerbert Xue } catch (NoSuchMethodException e) { 82*dc5640d1SHerbert Xue sourced.addError("%s doesn't have a no-arg constructor."); 83*dc5640d1SHerbert Xue return; 84*dc5640d1SHerbert Xue } 85*dc5640d1SHerbert Xue 86*dc5640d1SHerbert Xue // Create a bean builder for the given type. 87*dc5640d1SHerbert Xue beanBuilder = new BeanBuilder(type); 88*dc5640d1SHerbert Xue } 89*dc5640d1SHerbert Xue end()90*dc5640d1SHerbert Xue public void end() { 91*dc5640d1SHerbert Xue if (beanBuilder != null) { 92*dc5640d1SHerbert Xue beanBuilder.build(); 93*dc5640d1SHerbert Xue beanBuilder = null; 94*dc5640d1SHerbert Xue } 95*dc5640d1SHerbert Xue } 96*dc5640d1SHerbert Xue } 97*dc5640d1SHerbert Xue 98*dc5640d1SHerbert Xue /** Handles "property" elements. */ 99*dc5640d1SHerbert Xue class PropertyListener implements StartElementListener { 100*dc5640d1SHerbert Xue start(final Attributes attributes)101*dc5640d1SHerbert Xue public void start(final Attributes attributes) { 102*dc5640d1SHerbert Xue Binder sourced = originalBinder.withSource(xmlSource()); 103*dc5640d1SHerbert Xue 104*dc5640d1SHerbert Xue if (beanBuilder == null) { 105*dc5640d1SHerbert Xue // We must have already run into an error. 106*dc5640d1SHerbert Xue return; 107*dc5640d1SHerbert Xue } 108*dc5640d1SHerbert Xue 109*dc5640d1SHerbert Xue // Check for 'name'. 110*dc5640d1SHerbert Xue String name = attributes.getValue("name"); 111*dc5640d1SHerbert Xue if (name == null) { 112*dc5640d1SHerbert Xue sourced.addError("Missing attribute name."); 113*dc5640d1SHerbert Xue return; 114*dc5640d1SHerbert Xue } 115*dc5640d1SHerbert Xue 116*dc5640d1SHerbert Xue Class<?> type = beanBuilder.type; 117*dc5640d1SHerbert Xue 118*dc5640d1SHerbert Xue // Find setter method for the given property name. 119*dc5640d1SHerbert Xue String setterName = "set" + capitalize(name); 120*dc5640d1SHerbert Xue Method setter = null; 121*dc5640d1SHerbert Xue for (Method method : type.getMethods()) { 122*dc5640d1SHerbert Xue if (method.getName().equals(setterName)) { 123*dc5640d1SHerbert Xue setter = method; 124*dc5640d1SHerbert Xue break; 125*dc5640d1SHerbert Xue } 126*dc5640d1SHerbert Xue } 127*dc5640d1SHerbert Xue if (setter == null) { 128*dc5640d1SHerbert Xue sourced.addError("%s.%s() not found.", type.getName(), setterName); 129*dc5640d1SHerbert Xue return; 130*dc5640d1SHerbert Xue } 131*dc5640d1SHerbert Xue 132*dc5640d1SHerbert Xue // Validate number of parameters. 133*dc5640d1SHerbert Xue Type[] parameterTypes = setter.getGenericParameterTypes(); 134*dc5640d1SHerbert Xue if (parameterTypes.length != 1) { 135*dc5640d1SHerbert Xue sourced.addError("%s.%s() must take one argument.", setterName, type.getName()); 136*dc5640d1SHerbert Xue return; 137*dc5640d1SHerbert Xue } 138*dc5640d1SHerbert Xue 139*dc5640d1SHerbert Xue // Add property descriptor to builder. 140*dc5640d1SHerbert Xue Provider<?> provider = sourced.getProvider(Key.get(parameterTypes[0])); 141*dc5640d1SHerbert Xue beanBuilder.properties.add(new Property(setter, provider)); 142*dc5640d1SHerbert Xue } 143*dc5640d1SHerbert Xue } 144*dc5640d1SHerbert Xue capitalize(String s)145*dc5640d1SHerbert Xue static String capitalize(String s) { 146*dc5640d1SHerbert Xue return Character.toUpperCase(s.charAt(0)) + s.substring(1); 147*dc5640d1SHerbert Xue } 148*dc5640d1SHerbert Xue 149*dc5640d1SHerbert Xue static class Property { 150*dc5640d1SHerbert Xue 151*dc5640d1SHerbert Xue final Method setter; 152*dc5640d1SHerbert Xue final Provider<?> provider; 153*dc5640d1SHerbert Xue Property(Method setter, Provider<?> provider)154*dc5640d1SHerbert Xue Property(Method setter, Provider<?> provider) { 155*dc5640d1SHerbert Xue this.setter = setter; 156*dc5640d1SHerbert Xue this.provider = provider; 157*dc5640d1SHerbert Xue } 158*dc5640d1SHerbert Xue setOn(Object o)159*dc5640d1SHerbert Xue void setOn(Object o) { 160*dc5640d1SHerbert Xue try { 161*dc5640d1SHerbert Xue setter.invoke(o, provider.get()); 162*dc5640d1SHerbert Xue } catch (IllegalAccessException e) { 163*dc5640d1SHerbert Xue throw new RuntimeException(e); 164*dc5640d1SHerbert Xue } catch (InvocationTargetException e) { 165*dc5640d1SHerbert Xue throw new RuntimeException(e); 166*dc5640d1SHerbert Xue } 167*dc5640d1SHerbert Xue } 168*dc5640d1SHerbert Xue } 169*dc5640d1SHerbert Xue 170*dc5640d1SHerbert Xue class BeanBuilder { 171*dc5640d1SHerbert Xue 172*dc5640d1SHerbert Xue final List<Property> properties = new ArrayList<>(); 173*dc5640d1SHerbert Xue final Class<?> type; 174*dc5640d1SHerbert Xue BeanBuilder(Class<?> type)175*dc5640d1SHerbert Xue BeanBuilder(Class<?> type) { 176*dc5640d1SHerbert Xue this.type = type; 177*dc5640d1SHerbert Xue } 178*dc5640d1SHerbert Xue build()179*dc5640d1SHerbert Xue void build() { 180*dc5640d1SHerbert Xue addBinding(type); 181*dc5640d1SHerbert Xue } 182*dc5640d1SHerbert Xue addBinding(Class<T> type)183*dc5640d1SHerbert Xue <T> void addBinding(Class<T> type) { 184*dc5640d1SHerbert Xue originalBinder 185*dc5640d1SHerbert Xue .withSource(xmlSource()) 186*dc5640d1SHerbert Xue .bind(type) 187*dc5640d1SHerbert Xue .toProvider(new BeanProvider<T>(type, properties)); 188*dc5640d1SHerbert Xue } 189*dc5640d1SHerbert Xue } 190*dc5640d1SHerbert Xue 191*dc5640d1SHerbert Xue static class BeanProvider<T> implements Provider<T> { 192*dc5640d1SHerbert Xue 193*dc5640d1SHerbert Xue final Class<T> type; 194*dc5640d1SHerbert Xue final List<Property> properties; 195*dc5640d1SHerbert Xue BeanProvider(Class<T> type, List<Property> properties)196*dc5640d1SHerbert Xue BeanProvider(Class<T> type, List<Property> properties) { 197*dc5640d1SHerbert Xue this.type = type; 198*dc5640d1SHerbert Xue this.properties = properties; 199*dc5640d1SHerbert Xue } 200*dc5640d1SHerbert Xue get()201*dc5640d1SHerbert Xue public T get() { 202*dc5640d1SHerbert Xue try { 203*dc5640d1SHerbert Xue T t = type.newInstance(); 204*dc5640d1SHerbert Xue for (Property property : properties) { 205*dc5640d1SHerbert Xue property.setOn(t); 206*dc5640d1SHerbert Xue } 207*dc5640d1SHerbert Xue return t; 208*dc5640d1SHerbert Xue } catch (InstantiationException e) { 209*dc5640d1SHerbert Xue throw new RuntimeException(e); 210*dc5640d1SHerbert Xue } catch (IllegalAccessException e) { 211*dc5640d1SHerbert Xue throw new RuntimeException(e); 212*dc5640d1SHerbert Xue } 213*dc5640d1SHerbert Xue } 214*dc5640d1SHerbert Xue } 215*dc5640d1SHerbert Xue xmlSource()216*dc5640d1SHerbert Xue Object xmlSource() { 217*dc5640d1SHerbert Xue return xmlUrl + ":" + locator.getLineNumber(); 218*dc5640d1SHerbert Xue } 219*dc5640d1SHerbert Xue } 220