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 }