1 package org.codehaus.xfire.aegis.type.collection;
2
3
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.Map;
7
8 import javax.xml.namespace.QName;
9
10 import org.codehaus.xfire.MessageContext;
11 import org.codehaus.xfire.XFireRuntimeException;
12 import org.codehaus.xfire.aegis.MessageReader;
13 import org.codehaus.xfire.aegis.MessageWriter;
14 import org.codehaus.xfire.aegis.type.Type;
15 import org.codehaus.xfire.fault.XFireFault;
16 import org.codehaus.xfire.soap.SoapConstants;
17 import org.codehaus.xfire.util.NamespaceHelper;
18 import org.codehaus.yom.Attribute;
19 import org.codehaus.yom.Element;
20
21 public class MapType
22 extends Type
23 {
24 private Class keyClass;
25 private Class valueClass;
26 private QName keyName;
27 private QName valueName;
28 private QName entryName;
29
30 public MapType(QName schemaType, Class keyClass, Class valueClass)
31 {
32 super();
33
34 this.keyClass = keyClass;
35 this.valueClass = valueClass;
36
37 setSchemaType(schemaType);
38 setKeyName(new QName(schemaType.getNamespaceURI(), "key"));
39 setValueName(new QName(schemaType.getNamespaceURI(), "value"));
40 setEntryName(new QName(schemaType.getNamespaceURI(), "entry"));
41 }
42
43 public Object readObject(MessageReader reader, MessageContext context)
44 throws XFireFault
45 {
46 Map map = instantiateMap();
47 try
48 {
49 Type keyType = getKeyType();
50 Type valueType = getValueType();
51
52 Object key = null;
53 Object value = null;
54
55 while (reader.hasMoreElementReaders())
56 {
57 MessageReader entryReader = reader.getNextElementReader();
58
59 if (entryReader.getName().equals(getEntryName()))
60 {
61 while (entryReader.hasMoreElementReaders())
62 {
63 MessageReader evReader = entryReader.getNextElementReader();
64
65 if (evReader.getName().equals(getKeyName()))
66 {
67 key = keyType.readObject(evReader, context);
68 }
69 else if (evReader.getName().equals(getValueName()))
70 {
71 value = valueType.readObject(evReader, context);
72 }
73 else
74 {
75 readToEnd(evReader);
76 }
77 }
78
79 map.put(key, value);
80 }
81 else
82 {
83 readToEnd(entryReader);
84 }
85 }
86
87 return map;
88 }
89 catch (IllegalArgumentException e)
90 {
91 throw new XFireRuntimeException("Illegal argument.", e);
92 }
93 }
94
95 private void readToEnd(MessageReader childReader)
96 {
97 while (childReader.hasMoreElementReaders())
98 {
99 readToEnd(childReader.getNextElementReader());
100 }
101 }
102
103 /***
104 * Creates a map instance. If the type class is a <code>Map</code> or extends
105 * the <code>Map</code> interface a <code>HashMap</code> is created. Otherwise
106 * the map classs (i.e. LinkedHashMap) is instantiated using the default constructor.
107 *
108 * @return
109 */
110 protected Map instantiateMap()
111 {
112 Map map = null;
113
114 if (getTypeClass().equals(Map.class) || getTypeClass().isInterface())
115 {
116 map = new HashMap();
117 }
118 else
119 {
120 try
121 {
122 map = (Map) getTypeClass().newInstance();
123 }
124 catch (Exception e)
125 {
126 throw new XFireRuntimeException(
127 "Could not create map implementation: " + getTypeClass().getName(), e);
128 }
129 }
130
131 return map;
132 }
133
134 public void writeObject(Object object, MessageWriter writer, MessageContext context)
135 throws XFireFault
136 {
137 if (object == null)
138 return;
139
140 try
141 {
142 Map map = (Map) object;
143
144 Type keyType = getKeyType();
145 Type valueType = getValueType();
146
147 if (keyType == null)
148 throw new XFireRuntimeException("Couldn't find type for key class "
149 + keyType.getTypeClass() + ".");
150
151 if (valueType == null)
152 throw new XFireRuntimeException("Couldn't find type for value class "
153 + keyType.getTypeClass() + ".");
154
155
156 for (Iterator itr = map.entrySet().iterator(); itr.hasNext();)
157 {
158 Map.Entry entry = (Map.Entry) itr.next();
159
160 MessageWriter entryWriter = writer.getElementWriter(getEntryName());
161
162 MessageWriter keyWriter = entryWriter.getElementWriter(getKeyName());
163 keyType.writeObject(entry.getKey(), keyWriter, context);
164 keyWriter.close();
165
166 MessageWriter valueWriter = entryWriter.getElementWriter(getValueName());
167 valueType.writeObject(entry.getValue(), valueWriter, context);
168 valueWriter.close();
169
170 entryWriter.close();
171 }
172 }
173 catch (IllegalArgumentException e)
174 {
175 throw new XFireRuntimeException("Illegal argument.", e);
176 }
177 }
178
179 public void writeSchema(Element root)
180 {
181 String ctPref = SoapConstants.XSD_PREFIX + ":complexType";
182 String seqPref = SoapConstants.XSD_PREFIX + ":sequence";
183
184 Element complex = new Element(ctPref, SoapConstants.XSD);
185 complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
186 root.appendChild(complex);
187
188 Element seq = new Element(seqPref, SoapConstants.XSD);
189 complex.appendChild(seq);
190
191 Type keyType = getKeyType();
192 Type valueType = getValueType();
193
194 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
195 getSchemaType().getNamespaceURI());
196
197 String keyTypeName = prefix + ":" + keyType.getSchemaType().getLocalPart();
198 String valueTypeName = prefix + ":" + valueType.getSchemaType().getLocalPart();
199
200 Element element = new Element(SoapConstants.XSD_PREFIX + ":element", SoapConstants.XSD);
201 seq.appendChild(element);
202
203 element.addAttribute(new Attribute("name", getEntryName().getLocalPart()));
204 element.addAttribute(new Attribute("minOccurs", "0"));
205 element.addAttribute(new Attribute("maxOccurs", "unbounded"));
206
207 Element evComplex = new Element(ctPref, SoapConstants.XSD);
208 element.appendChild(evComplex);
209
210 Element evseq = new Element(seqPref, SoapConstants.XSD);
211 evComplex.appendChild(evseq);
212
213 createElement(evseq, getKeyName(), keyTypeName);
214 createElement(evseq, getValueName(), valueTypeName);
215 }
216
217 private void createElement(Element seq, QName name, String keyTypeName)
218 {
219 Element element = new Element(SoapConstants.XSD_PREFIX + ":element", SoapConstants.XSD);
220 seq.appendChild(element);
221
222 element.addAttribute(new Attribute("name", name.getLocalPart()));
223 element.addAttribute(new Attribute("type", keyTypeName));
224
225 element.addAttribute(new Attribute("minOccurs", "0"));
226 element.addAttribute(new Attribute("maxOccurs", "1"));
227 }
228
229 public Type getKeyType()
230 {
231 return getOrCreateType(keyClass);
232 }
233
234 private Type getOrCreateType(Class clazz)
235 {
236 Type type = getTypeMapping().getType(clazz);
237 if (type == null)
238 {
239 System.out.println("Couldn't find type for " + clazz);
240 type = getTypeMapping().getTypeCreator().createType(clazz);
241 getTypeMapping().register(type);
242 }
243 return type;
244 }
245
246 public Type getValueType()
247 {
248 return getOrCreateType(valueClass);
249 }
250
251 public Class getKeyClass()
252 {
253 return keyClass;
254 }
255
256 public void setKeyClass(Class keyClass)
257 {
258 this.keyClass = keyClass;
259 }
260
261 public QName getKeyName()
262 {
263 return keyName;
264 }
265
266 public void setKeyName(QName keyName)
267 {
268 this.keyName = keyName;
269 }
270
271 public Class getValueClass()
272 {
273 return valueClass;
274 }
275
276 public void setValueClass(Class valueClass)
277 {
278 this.valueClass = valueClass;
279 }
280
281 public QName getValueName()
282 {
283 return valueName;
284 }
285
286 public void setValueName(QName valueName)
287 {
288 this.valueName = valueName;
289 }
290
291 public QName getEntryName()
292 {
293 return entryName;
294 }
295
296 public void setEntryName(QName entryName)
297 {
298 this.entryName = entryName;
299 }
300 }