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             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 }