xref: /aosp_15_r20/external/guice/examples/src/example/xml/XmlBeanModule.java (revision dc5640d1ceac12a29404866b9a53df952a7a6c47)
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