package org.codehaus.xfire.aegis.type.collection; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.XFireRuntimeException; import org.codehaus.xfire.aegis.MessageReader; import org.codehaus.xfire.aegis.MessageWriter; import org.codehaus.xfire.aegis.type.Type; import org.codehaus.xfire.aegis.type.java5.Java5TypeCreator; import org.codehaus.xfire.fault.XFireFault; import org.codehaus.xfire.soap.SoapConstants; import org.codehaus.xfire.util.NamespaceHelper; import org.jdom.Attribute; import org.jdom.Element; public class MapType extends Type { private java.lang.reflect.Type keyClass; // changed from Class to handle map of collection. private java.lang.reflect.Type valueClass; //changed from Class private QName keyName; private QName valueName; private QName entryName; /* * new constructor which take java.lang type instead of Class * */ public MapType(QName schemaType, java.lang.reflect.Type keyClass, java.lang.reflect.Type valueClass) { super(); this.keyClass = keyClass; this.valueClass = valueClass; setSchemaType(schemaType); setKeyName(new QName(schemaType.getNamespaceURI(), "key")); setValueName(new QName(schemaType.getNamespaceURI(), "value")); setEntryName(new QName(schemaType.getNamespaceURI(), "entry")); } public Object readObject(MessageReader reader, MessageContext context) throws XFireFault { Map map = instantiateMap(); try { Type keyType = getKeyType(); Type valueType = getValueType(); Object key = null; Object value = null; while (reader.hasMoreElementReaders()) { MessageReader entryReader = reader.getNextElementReader(); if (entryReader.getName().equals(getEntryName())) { while (entryReader.hasMoreElementReaders()) { MessageReader evReader = entryReader.getNextElementReader(); if (evReader.getName().equals(getKeyName())) { key = keyType.readObject(evReader, context); } else if (evReader.getName().equals(getValueName())) { value = valueType.readObject(evReader, context); } else { readToEnd(evReader); } } map.put(key, value); } else { readToEnd(entryReader); } } return map; } catch (IllegalArgumentException e) { throw new XFireRuntimeException("Illegal argument.", e); } } private void readToEnd(MessageReader childReader) { while (childReader.hasMoreElementReaders()) { readToEnd(childReader.getNextElementReader()); } } /** * Creates a map instance. If the type class is a Map or extends * the Map interface a HashMap is created. Otherwise * the map classs (i.e. LinkedHashMap) is instantiated using the default constructor. * * @return */ protected Map instantiateMap() { Map map = null; if (getTypeClass().equals(Map.class)) { map = new HashMap(); } else if (getTypeClass().equals(Hashtable.class)) { map = new Hashtable(); } else if(getTypeClass().isInterface()) { map = new HashMap(); } else { try { map = (Map) getTypeClass().newInstance(); } catch (Exception e) { throw new XFireRuntimeException( "Could not create map implementation: " + getTypeClass().getName(), e); } } return map; } public void writeObject(Object object, MessageWriter writer, MessageContext context) throws XFireFault { if (object == null) return; try { Map map = (Map) object; Type keyType = getKeyType(); Type valueType = getValueType(); for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) { Map.Entry entry = (Map.Entry) itr.next(); MessageWriter entryWriter = writer.getElementWriter(getEntryName()); MessageWriter keyWriter = entryWriter.getElementWriter(getKeyName()); keyType.writeObject(entry.getKey(), keyWriter, context); keyWriter.close(); MessageWriter valueWriter = entryWriter.getElementWriter(getValueName()); valueType.writeObject(entry.getValue(), valueWriter, context); valueWriter.close(); entryWriter.close(); } } catch (IllegalArgumentException e) { throw new XFireRuntimeException("Illegal argument.", e); } } public void writeSchema(Element root) { Element complex = new Element("complexType", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.setAttribute(new Attribute("name", getSchemaType().getLocalPart())); root.addContent(complex); Element seq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.addContent(seq); Type keyType = getKeyType(); Type valueType = getValueType(); Element element = new Element("element", SoapConstants.XSD_PREFIX, SoapConstants.XSD); seq.addContent(element); element.setAttribute(new Attribute("name", getEntryName().getLocalPart())); element.setAttribute(new Attribute("minOccurs", "0")); element.setAttribute(new Attribute("maxOccurs", "unbounded")); Element evComplex = new Element("complexType", SoapConstants.XSD_PREFIX, SoapConstants.XSD); element.addContent(evComplex); Element evseq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD); evComplex.addContent(evseq); createElement(root, evseq, getKeyName(), keyType); createElement(root, evseq, getValueName(), valueType); } /** * Creates a element in a sequence for the key type and the value type. */ private void createElement(Element root, Element seq, QName name, Type type) { Element element = new Element("element", SoapConstants.XSD_PREFIX, SoapConstants.XSD); seq.addContent(element); String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), type.getSchemaType().getNamespaceURI()); String typeName = prefix + ":" + type.getSchemaType().getLocalPart(); element.setAttribute(new Attribute("name", name.getLocalPart())); element.setAttribute(new Attribute("type", typeName)); element.setAttribute(new Attribute("minOccurs", "0")); element.setAttribute(new Attribute("maxOccurs", "1")); } public Type getKeyType() { Type keyType = getOrCreateType(keyClass); if (keyType == null) throw new XFireRuntimeException("Couldn't find type for key class " + keyClass + "."); return keyType; } /** * this function is used to get a aegis Type from a type * changed to manage maps of collection.. * */ private Type getOrCreateType(java.lang.reflect.Type clazz) { Type type = null; if (clazz instanceof Class) { type = getTypeMapping().getType((Class) clazz); } if (type == null) { type = getTypeMapping().getTypeCreator().createType(clazz);// use createType( Type) getTypeMapping().register(type); // instead of create Type Class } return type; } public Set getDependencies() { HashSet deps = new HashSet(); deps.add(getKeyType()); deps.add(getValueType()); return deps; } public boolean isComplex() { return true; } public Type getValueType() { Type valueType = getOrCreateType(valueClass); if (valueType == null) throw new XFireRuntimeException("Couldn't find type for key class " + valueClass + "."); return valueType; } public java.lang.reflect.Type getKeyClass() { return keyClass; } public void setKeyClass(java.lang.reflect.Type keyClass) { this.keyClass = keyClass; } public QName getKeyName() { return keyName; } public void setKeyName(QName keyName) { this.keyName = keyName; } public java.lang.reflect.Type getValueClass() { return valueClass; } public void setValueClass(java.lang.reflect.Type valueClass) { this.valueClass = valueClass; } public QName getValueName() { return valueName; } public void setValueName(QName valueName) { this.valueName = valueName; } public QName getEntryName() { return entryName; } public void setEntryName(QName entryName) { this.entryName = entryName; } }