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