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 if (keyType == null)
150 throw new XFireRuntimeException("Couldn't find type for key class "
151 + keyType.getTypeClass() + ".");
152
153 if (valueType == null)
154 throw new XFireRuntimeException("Couldn't find type for value class "
155 + keyType.getTypeClass() + ".");
156
157
158 for (Iterator itr = map.entrySet().iterator(); itr.hasNext();)
159 {
160 Map.Entry entry = (Map.Entry) itr.next();
161
162 MessageWriter entryWriter = writer.getElementWriter(getEntryName());
163
164 MessageWriter keyWriter = entryWriter.getElementWriter(getKeyName());
165 keyType.writeObject(entry.getKey(), keyWriter, context);
166 keyWriter.close();
167
168 MessageWriter valueWriter = entryWriter.getElementWriter(getValueName());
169 valueType.writeObject(entry.getValue(), valueWriter, context);
170 valueWriter.close();
171
172 entryWriter.close();
173 }
174 }
175 catch (IllegalArgumentException e)
176 {
177 throw new XFireRuntimeException("Illegal argument.", e);
178 }
179 }
180
181 public void writeSchema(Element root)
182 {
183 String ctPref = SoapConstants.XSD_PREFIX + ":complexType";
184 String seqPref = SoapConstants.XSD_PREFIX + ":sequence";
185
186 Element complex = new Element(ctPref, SoapConstants.XSD);
187 complex.addAttribute(new Attribute("name", getSchemaType().getLocalPart()));
188 root.appendChild(complex);
189
190 Element seq = new Element(seqPref, SoapConstants.XSD);
191 complex.appendChild(seq);
192
193 Type keyType = getKeyType();
194 Type valueType = getValueType();
195
196 String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(),
197 getSchemaType().getNamespaceURI());
198
199 String keyTypeName = prefix + ":" + keyType.getSchemaType().getLocalPart();
200 String valueTypeName = prefix + ":" + valueType.getSchemaType().getLocalPart();
201
202 Element element = new Element(SoapConstants.XSD_PREFIX + ":element", SoapConstants.XSD);
203 seq.appendChild(element);
204
205 element.addAttribute(new Attribute("name", getEntryName().getLocalPart()));
206 element.addAttribute(new Attribute("minOccurs", "0"));
207 element.addAttribute(new Attribute("maxOccurs", "unbounded"));
208
209 Element evComplex = new Element(ctPref, SoapConstants.XSD);
210 element.appendChild(evComplex);
211
212 Element evseq = new Element(seqPref, SoapConstants.XSD);
213 evComplex.appendChild(evseq);
214
215 createElement(evseq, getKeyName(), keyTypeName);
216 createElement(evseq, getValueName(), valueTypeName);
217 }
218
219 private void createElement(Element seq, QName name, String keyTypeName)
220 {
221 Element element = new Element(SoapConstants.XSD_PREFIX + ":element", SoapConstants.XSD);
222 seq.appendChild(element);
223
224 element.addAttribute(new Attribute("name", name.getLocalPart()));
225 element.addAttribute(new Attribute("type", keyTypeName));
226
227 element.addAttribute(new Attribute("minOccurs", "0"));
228 element.addAttribute(new Attribute("maxOccurs", "1"));
229 }
230
231 public Type getKeyType()
232 {
233 return getOrCreateType(keyClass);
234 }
235
236 private Type getOrCreateType(Class clazz)
237 {
238 Type type = getTypeMapping().getType(clazz);
239 if (type == null)
240 {
241 type = getTypeMapping().getTypeCreator().createType(clazz);
242 getTypeMapping().register(type);
243 }
244 return type;
245 }
246
247 public Set getDependencies()
248 {
249 HashSet deps = new HashSet();
250 deps.add(getKeyType());
251 deps.add(getValueType());
252 return deps;
253 }
254
255 public boolean isComplex()
256 {
257 return true;
258 }
259
260 public Type getValueType()
261 {
262 return getOrCreateType(valueClass);
263 }
264
265 public Class getKeyClass()
266 {
267 return keyClass;
268 }
269
270 public void setKeyClass(Class keyClass)
271 {
272 this.keyClass = keyClass;
273 }
274
275 public QName getKeyName()
276 {
277 return keyName;
278 }
279
280 public void setKeyName(QName keyName)
281 {
282 this.keyName = keyName;
283 }
284
285 public Class getValueClass()
286 {
287 return valueClass;
288 }
289
290 public void setValueClass(Class valueClass)
291 {
292 this.valueClass = valueClass;
293 }
294
295 public QName getValueName()
296 {
297 return valueName;
298 }
299
300 public void setValueName(QName valueName)
301 {
302 this.valueName = valueName;
303 }
304
305 public QName getEntryName()
306 {
307 return entryName;
308 }
309
310 public void setEntryName(QName entryName)
311 {
312 this.entryName = entryName;
313 }
314 }