View Javadoc

    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 }