View Javadoc

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 }