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 }