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 }