1 package org.codehaus.xfire.aegis.type.basic;
2
3 import java.beans.PropertyDescriptor;
4 import java.util.HashSet;
5 import java.util.Iterator;
6 import java.util.Map;
7 import java.util.Set;
8
9 import javax.xml.namespace.QName;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.codehaus.xfire.MessageContext;
14 import org.codehaus.xfire.XFireRuntimeException;
15 import org.codehaus.xfire.aegis.MessageReader;
16 import org.codehaus.xfire.aegis.MessageWriter;
17 import org.codehaus.xfire.aegis.type.Type;
18 import org.codehaus.xfire.fault.XFireFault;
19 import org.codehaus.xfire.soap.SoapConstants;
20 import org.codehaus.xfire.util.NamespaceHelper;
21 import org.codehaus.yom.Attribute;
22 import org.codehaus.yom.Element;
23
24 /***
25 * Serializes JavaBeans.
26 *
27 * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
28 */
29 public class BeanType
30 extends Type
31 {
32 private static final Log logger = LogFactory.getLog(BeanType.class);
33
34 private static Map objectProperties = null;
35
36 private BeanTypeInfo _info;
37
38 public BeanType()
39 {
40 setNillable(true);
41 }
42
43 public BeanType(BeanTypeInfo info)
44 {
45 this._info = info;
46 setNillable(true);
47
48 this.setTypeClass(info.getTypeClass());
49 }
50
51 public Object readObject(MessageReader reader, MessageContext context)
52 throws XFireFault
53 {
54 BeanTypeInfo info = getTypeInfo();
55
56 try
57 {
58 Class clazz = getTypeClass();
59 Object object = clazz.newInstance();
60
61
62 while (reader.hasMoreAttributeReaders())
63 {
64 MessageReader childReader = reader.getNextAttributeReader();
65 QName name = childReader.getName();
66
67 Type type = info.getType(name.getLocalPart());
68
69 if (type != null)
70 {
71 Object writeObj = type.readObject(childReader, context);
72
73 writeProperty(name.getLocalPart(), object, writeObj);
74 }
75 }
76
77
78 while( reader.hasMoreElementReaders() )
79 {
80 MessageReader childReader = reader.getNextElementReader();
81 QName name = childReader.getName();
82
83 Type type = info.getType(name.getLocalPart());
84
85 if (type != null)
86 {
87 Object writeObj = type.readObject(childReader, context);
88
89 writeProperty(name.getLocalPart(), object, writeObj);
90 }
91 else
92 {
93 readToEnd(childReader);
94 }
95 }
96
97 return object;
98 }
99 catch (IllegalAccessException e)
100 {
101 throw new XFireFault("Illegal access.", e, XFireFault.RECEIVER);
102 }
103 catch (InstantiationException e)
104 {
105 throw new XFireFault("Couldn't instantiate service.", e, XFireFault.SENDER);
106 }
107 }
108
109 private void readToEnd(MessageReader childReader)
110 {
111 while (childReader.hasMoreElementReaders())
112 {
113 readToEnd(childReader.getNextElementReader());
114 }
115 }
116
117 /***
118 * Write the specified property to a field.
119 */
120 protected void writeProperty(String name, Object object, Object property)
121 throws XFireFault
122 {
123 try
124 {
125 PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name);
126 desc.getWriteMethod().invoke(object, new Object[] {property});
127 }
128 catch (Exception e)
129 {
130 throw new XFireFault("Couldn't set property " + name, e, XFireFault.SENDER);
131 }
132 }
133
134
135
136 /***
137 * @see org.codehaus.xfire.aegis.type.Type#writeObject(java.lang.Object)
138 */
139 public void writeObject(Object object, MessageWriter writer, MessageContext context)
140 throws XFireFault
141 {
142 if (object == null)
143 return;
144
145 BeanTypeInfo info = getTypeInfo();
146
147 for (Iterator itr = info.getAttributes(); itr.hasNext(); )
148 {
149 String name = (String) itr.next();
150
151 Object value = readProperty(object, name);
152 if (value != null)
153 {
154 Type type = getType( info, name );
155
156 if ( type == null )
157 throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
158
159 MessageWriter cwriter;
160
161 if (type.isAbstract())
162 {
163 cwriter = writer.getAttributeWriter(name, getSchemaType().getNamespaceURI());
164 }
165 else
166 {
167 cwriter = writer.getAttributeWriter(name, type.getSchemaType().getNamespaceURI());
168 }
169
170 type.writeObject(value, cwriter, context);
171
172 cwriter.close();
173 }
174 }
175
176 for (Iterator itr = info.getElements(); itr.hasNext(); )
177 {
178 String name = (String) itr.next();
179
180 Object value = readProperty(object, name);
181
182 Type type = getType( info, name );
183 MessageWriter cwriter;
184
185
186 if (type.isAbstract())
187 {
188 cwriter = writer.getElementWriter(name, getSchemaType().getNamespaceURI());
189 }
190 else
191 {
192 cwriter = writer.getElementWriter(name, type.getSchemaType().getNamespaceURI());
193 }
194
195
196 if ( value != null)
197 {
198 if ( type == null )
199 throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
200
201 type.writeObject(value, cwriter, context);
202 }
203 else if (info.isNillable(name))
204 {
205
206 MessageWriter attWriter = cwriter.getAttributeWriter("nil", SoapConstants.XSI_NS);
207 attWriter.writeValue("true");
208 attWriter.close();
209 }
210
211 cwriter.close();
212 }
213 }
214
215 protected Object readProperty(Object object, String name)
216 {
217 try
218 {
219 PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name);
220 return desc.getReadMethod().invoke(object, new Object[0]);
221 }
222 catch (Exception e)
223 {
224 throw new XFireRuntimeException( "Couldn't get property " + name, e );
225 }
226 }
227
228 /***
229 * @see org.codehaus.xfire.aegis.type.Type#writeSchema()
230 */
231 public void writeSchema(Element root)
232 {
233 BeanTypeInfo info = getTypeInfo();
234
235 Element complex = new Element(SoapConstants.XSD_PREFIX + ":complexType",
236 SoapConstants.XSD);
237 complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
238 root.appendChild(complex);
239
240 Element seq = null;
241
242
243 for (Iterator itr = info.getElements(); itr.hasNext();)
244 {
245 if (seq == null)
246 {
247 seq = new Element(SoapConstants.XSD_PREFIX + ":sequence", SoapConstants.XSD);
248 complex.appendChild(seq);
249 }
250
251 String name = (String) itr.next();
252
253 Element element = new Element(SoapConstants.XSD_PREFIX + ":element",
254 SoapConstants.XSD);
255 seq.appendChild(element);
256
257 Type type = getType(info, name);
258
259 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
260 type.getSchemaType().getNamespaceURI() );
261
262 writeTypeReference(name, element, type, prefix);
263 }
264
265
266 for (Iterator itr = info.getAttributes(); itr.hasNext();)
267 {
268 String name = (String) itr.next();
269
270 Element element = new Element(SoapConstants.XSD_PREFIX + ":attribute",
271 SoapConstants.XSD);
272 complex.appendChild(element);
273
274 Type type = getType(info, name);
275
276 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
277 type.getSchemaType().getNamespaceURI() );
278
279 element.addAttribute(new Attribute("name", name));
280 element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
281 }
282 }
283
284 private Type getType(BeanTypeInfo info, String name)
285 {
286 Type type = info.getType(name);
287
288 if (type == null)
289 {
290 throw new NullPointerException("Couldn't find type for" + name + " in class " +
291 getTypeClass().getName());
292 }
293
294 return type;
295 }
296
297 private void writeTypeReference(String name, Element element, Type type, String prefix)
298 {
299 if (type.isAbstract())
300 {
301 element.addAttribute(new Attribute("name", name));
302 element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
303
304 if (getTypeInfo().isNillable(name))
305 {
306 element.addAttribute(new Attribute("nillable", "true"));
307 }
308 }
309 else
310 {
311 element.addAttribute(new Attribute("ref", prefix + ":" + type.getSchemaType().getLocalPart()));
312 }
313 }
314
315 /***
316 * We need to write a complex type schema for Beans, so return true.
317 *
318 * @see org.codehaus.xfire.aegis.type.Type#isComplex()
319 */
320 public boolean isComplex()
321 {
322 return true;
323 }
324
325 public Set getDependencies()
326 {
327 Set deps = new HashSet();
328
329 BeanTypeInfo info = getTypeInfo();
330
331 for (Iterator itr = info.getAttributes(); itr.hasNext(); )
332 {
333 String name = (String) itr.next();
334
335 deps.add(info.getType(name));
336 }
337
338 for (Iterator itr = info.getElements(); itr.hasNext(); )
339 {
340 String name = (String) itr.next();
341
342 deps.add(info.getType(name));
343 }
344
345 return deps;
346 }
347
348 public BeanTypeInfo getTypeInfo()
349 {
350 if (_info == null)
351 {
352 _info = createTypeInfo();
353 }
354
355
356 if (!_info.isInitialized())
357 {
358 _info.initialize();
359 }
360
361 return _info;
362 }
363
364 public BeanTypeInfo createTypeInfo()
365 {
366 BeanTypeInfo info = new BeanTypeInfo(getTypeClass());
367
368 info.setTypeMapping(getTypeMapping());
369
370 return info;
371 }
372 }