View Javadoc

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 }