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);
68
69 if (type != null)
70 {
71 Object writeObj = type.readObject(childReader, context);
72
73 writeProperty(name, 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);
84
85 if (type != null)
86 {
87 Object writeObj = type.readObject(childReader, context);
88
89 writeProperty(name, 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(QName name, Object object, Object property)
121 throws XFireFault
122 {
123 try
124 {
125 PropertyDescriptor desc = getTypeInfo().getPropertyDescriptor(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 QName name = (QName) 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 = writer.getAttributeWriter(name);
160
161 type.writeObject(value, cwriter, context);
162
163 cwriter.close();
164 }
165 }
166
167 for (Iterator itr = info.getElements(); itr.hasNext(); )
168 {
169 QName name = (QName) itr.next();
170
171 Object value = readProperty(object, name);
172 MessageWriter cwriter = writer.getElementWriter(name);
173
174 if ( value != null)
175 {
176 Type type = getType( info, name );
177
178 if ( type == null )
179 throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name );
180
181 type.writeObject(value, cwriter, context);
182 }
183 else if (info.isNillable(name))
184 {
185 MessageWriter attWriter = cwriter.getAttributeWriter("nil", SoapConstants.XSI_NS);
186 attWriter.writeValue("true");
187 attWriter.close();
188 }
189
190 cwriter.close();
191 }
192 }
193
194 protected Object readProperty(Object object, QName name)
195 {
196 try
197 {
198 PropertyDescriptor desc = getTypeInfo().getPropertyDescriptor(name);
199 return desc.getReadMethod().invoke(object, new Object[0]);
200 }
201 catch (Exception e)
202 {
203 throw new XFireRuntimeException( "Couldn't get property " + name, e );
204 }
205 }
206
207 /***
208 * @see org.codehaus.xfire.aegis.type.Type#writeSchema()
209 */
210 public void writeSchema(Element root)
211 {
212 BeanTypeInfo info = getTypeInfo();
213
214 Element complex = new Element(SoapConstants.XSD_PREFIX + ":complexType",
215 SoapConstants.XSD);
216 complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
217 root.appendChild(complex);
218
219 Element seq = null;
220
221 for (Iterator itr = info.getElements(); itr.hasNext();)
222 {
223 if (seq == null)
224 {
225 seq = new Element(SoapConstants.XSD_PREFIX + ":sequence", SoapConstants.XSD);
226 complex.appendChild(seq);
227 }
228
229 QName name = (QName) itr.next();
230
231 Element element = new Element(SoapConstants.XSD_PREFIX + ":element",
232 SoapConstants.XSD);
233 seq.appendChild(element);
234
235 Type type = getType(info, name);
236
237 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
238 type.getSchemaType().getNamespaceURI() );
239
240 writeTypeReference(name, element, type, prefix);
241 }
242
243 for (Iterator itr = info.getAttributes(); itr.hasNext();)
244 {
245 QName name = (QName) itr.next();
246
247 Element element = new Element(SoapConstants.XSD_PREFIX + ":attribute",
248 SoapConstants.XSD);
249 complex.appendChild(element);
250
251 Type type = getType(info, name);
252
253 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
254 type.getSchemaType().getNamespaceURI() );
255
256 element.addAttribute(new Attribute("name", name.getLocalPart()));
257 element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
258
259 if (info.isNillable(name))
260 {
261 element.addAttribute(new Attribute("nillable", "true"));
262 }
263 }
264 }
265
266 private Type getType(BeanTypeInfo info, QName name)
267 {
268 Type type = info.getType(name);
269
270 if (type == null)
271 {
272 throw new NullPointerException("Couldn't find type for" + name + " in class " +
273 getTypeClass().getName());
274 }
275
276 return type;
277 }
278
279 private void writeTypeReference(QName name, Element element, Type type, String prefix)
280 {
281 if (type.isAbstract())
282 {
283 element.addAttribute(new Attribute("name", name.getLocalPart()));
284 element.addAttribute(new Attribute("type", prefix + ":" + type.getSchemaType().getLocalPart()));
285
286 if (type.isNillable())
287 {
288 element.addAttribute(new Attribute("nillable", "true"));
289 }
290 }
291 else
292 {
293 element.addAttribute(new Attribute("ref", prefix + ":" + type.getSchemaType().getLocalPart()));
294 }
295 }
296
297 /***
298 * We need to write a complex type schema for Beans, so return true.
299 *
300 * @see org.codehaus.xfire.aegis.type.Type#isComplex()
301 */
302 public boolean isComplex()
303 {
304 return true;
305 }
306
307 public Set getDependencies()
308 {
309 Set deps = new HashSet();
310
311 BeanTypeInfo info = getTypeInfo();
312
313 for (Iterator itr = info.getAttributes(); itr.hasNext(); )
314 {
315 QName name = (QName) itr.next();
316
317 deps.add(info.getType(name));
318 }
319
320 for (Iterator itr = info.getElements(); itr.hasNext(); )
321 {
322 QName name = (QName) itr.next();
323
324 deps.add(info.getType(name));
325 }
326
327 return deps;
328 }
329
330 public BeanTypeInfo getTypeInfo()
331 {
332 if (_info == null)
333 {
334 _info = createTypeInfo();
335 }
336
337
338 if (!_info.isInitialized())
339 {
340 _info.initialize();
341 }
342
343 return _info;
344 }
345
346 public BeanTypeInfo createTypeInfo()
347 {
348 BeanTypeInfo info = new BeanTypeInfo(getTypeClass(), getSchemaType().getNamespaceURI());
349
350 info.setTypeMapping(getTypeMapping());
351
352 return info;
353 }
354 }