package student.web.internal.converters; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter; import com.thoughtworks.xstream.converters.collections.TreeMapConverter; import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import student.web.internal.Snapshot; /** * Converts a java.util.Map to XML, specifying an 'entry' element with 'key' and * 'value' children. * <p> * Note: 'key' and 'value' is not the name of the generated tag. The children * are serialized as normal elements and the implementation expects them in the * order 'key'/'value'. * </p> * <p> * Supports java.util.HashMap, java.util.Hashtable and java.util.LinkedHashMap. * </p> * * @author Joe Walnes */ public class MapConverter extends AbstractCollectionConverter { private TreeMapConverter mapConverter; // Snapshot newSnapshot; // Snapshot oldSnapshot; public MapConverter(Mapper mapper) { super(mapper); mapConverter = new TreeMapConverter(mapper); } // public void setupSnapshots(Snapshot newSnap, // Snapshot oldSnap) { // newSnapshot = newSnap; // oldSnapshot = oldSnap; // } @SuppressWarnings("rawtypes") public boolean canConvert(Class type) { if (type == null) { return false; } return type.equals(HashMap.class) || type.equals(Hashtable.class) || type.getName().equals("java.util.LinkedHashMap") || type.getName().equals("sun.font.AttributeMap") // Used by // java.awt.Font // in JDK 6 ; } public void marshal( Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Map<?, ?> map = (Map<?, ?>)source; // UUID oldId = Snapshot.findId(map, newSnapshot, oldSnapshot); UUID id = Snapshot.lookupId(source, true); writer.addAttribute(XMLConstants.ID_ATTRIBUTE, id.toString()); updateMap((Map<Object, Object>)map); for (Iterator<?> iterator = map.entrySet().iterator(); iterator.hasNext(); ) { Entry<?, ?> entry = (Entry<?, ?>)iterator.next(); ExtendedHierarchicalStreamWriterHelper.startNode( writer, mapper().serializedClass(Map.Entry.class), Map.Entry.class); UUID keyId = Snapshot.lookupId(entry.getKey(), true); writer.addAttribute(XMLConstants.ID_ATTRIBUTE, keyId.toString()); writeItem(entry.getKey(), context, writer); if (entry.getValue() instanceof NullableClass) { ((NullableClass)entry.getValue()) .writeHiddenClass(mapConverter, writer, context); } else { writeItem(entry.getValue(), context, writer); } writer.endNode(); } } public static void updateMap(Map<Object, Object> source) { // Lookup the id for this map UUID mapId = Snapshot.lookupId(source, false); // Get the newest copy of this map Map newestMap = (Map)Snapshot.getNewest().findObject(mapId); // If there is something newer we should start the update process if (newestMap != null) { // Look at every key in the new map for (Object newObject : newestMap.keySet()) { // Do we know about this object locally? UUID id = Snapshot.getNewest().findId(newObject); Object localLookup = Snapshot.getLocal().findObject(id); // If the id is null, we know that we have never seen this // item before. Push it into the list. if (localLookup == null) { source.put(newObject, newestMap.get(newObject)); } // else // If the id is not null we have seen this item before. The // current logic is that if we have seen it, we have either // removed it intentionally or it still exists in the map. If // it is still in the map then we should just let the flexible // field set converter deal with it. } } } public Object unmarshal( HierarchicalStreamReader reader, UnmarshallingContext context) { @SuppressWarnings("unchecked") Map<Object, Object> map = (Map<Object, Object>)createCollection( context.getRequiredType()); UUID id = UUID.fromString( reader.getAttribute(XMLConstants.ID_ATTRIBUTE)); populateMap(reader, context, map); Snapshot.getLocal().resolveObject(id, map, (Map<String, Object>)null); return map; } protected void populateMap( HierarchicalStreamReader reader, UnmarshallingContext context, Map<Object, Object> map) { while (reader.hasMoreChildren()) { reader.moveDown(); UUID id = UUID.fromString( reader.getAttribute(XMLConstants.ID_ATTRIBUTE)); reader.moveDown(); Object key = readItem(reader, context, map); reader.moveUp(); reader.moveDown(); Object value = readItem(reader, context, map); reader.moveUp(); map.put(key, value); Snapshot.getLocal().resolveObject( id, key, (Map<String, Object>)null); reader.moveUp(); } } }