package com.thoughtworks.xstream.converters.reflection;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.thoughtworks.xstream.core.JVM;
import com.thoughtworks.xstream.core.util.OrderRetainingMap;


/**
 * A field dictionary instance caches information about classes fields.
 */
public class FieldDictionary {

    private final Map keyedByFieldNameCache = new HashMap();
    private final Map keyedByFieldKeyCache = new HashMap();
	private final FieldKeySorter sorter;

    public FieldDictionary() {
		this(new DefaultFieldKeySorter());
	}

    public FieldDictionary(FieldKeySorter sorter) {
		this.sorter = sorter;
	}

	/**
     * Returns an iterator for all serializable fields for some class
     *
     * @param cls the class you are interested on
     * @return an iterator for its serializable fields
     */
    public Iterator serializableFieldsFor(Class cls) {
        return buildMap(cls, true).values().iterator();
    }

    /**
     * Returns an specific field of some class. If definedIn is null, it searchs for the field
     * named 'name' inside the class cls. If definedIn is different than null, tries to find the
     * specified field name in the specified class cls which should be defined in class
     * definedIn (either equals cls or a one of it's superclasses)
     *
     * @param cls the class where the field is to be searched
     * @param name the field name
     * @param definedIn the superclass (or the class itself) of cls where the field was defined
     * @return the field itself
     */
    public Field field(Class cls, String name, Class definedIn) {
        Map fields = buildMap(cls, definedIn != null);
        Field field = (Field)fields.get(definedIn != null ? (Object)new FieldKey(
            name, definedIn, 0) : (Object)name);
        if (field == null) {
            throw new ObjectAccessException("No such field " + cls.getName() + "." + name);
        } else {
            return field;
        }
    }

    private Map buildMap(final Class type, boolean tupleKeyed) {
    	Class cls = type;
        final String clsName = cls.getName();
        synchronized (keyedByFieldNameCache) {
            synchronized (keyedByFieldKeyCache) {
                if (!keyedByFieldNameCache.containsKey(clsName)) {
                    final Map keyedByFieldName = new HashMap();
                    final Map keyedByFieldKey = new OrderRetainingMap();
                    while (!Object.class.equals(cls)) {
                        Field[] fields = cls.getDeclaredFields();
                        if (JVM.reverseFieldDefinition()) {
                            for (int i = fields.length >> 1; i-- > 0;) {
                                final int idx = fields.length - i - 1;
                                final Field field = fields[i];
                                fields[i] = fields[idx];
                                fields[idx] = field;
                            }
                        }
                        for (int i = 0; i < fields.length; i++) {
                            Field field = fields[i];
                            FieldKey fieldKey = new FieldKey(field.getName(), field
                                    .getDeclaringClass(), i);
                            field.setAccessible(true);
                            if (!keyedByFieldName.containsKey(field.getName())) {
                                keyedByFieldName.put(field.getName(), field);
                            }
                            keyedByFieldKey.put(fieldKey, field);
                        }
                        cls = cls.getSuperclass();
                    }
                    keyedByFieldNameCache.put(clsName, keyedByFieldName);
                    keyedByFieldKeyCache.put(clsName, sorter.sort(type, keyedByFieldKey));
                }
            }
        }
        return (Map)(tupleKeyed ? keyedByFieldKeyCache.get(clsName) : keyedByFieldNameCache
            .get(clsName));
    }

}

@@ -156,16 +156,46 @@
 til.Map;
+%0Aimport java.util.WeakHashMap;
 %0A%0Aimport
@@ -447,32 +447,36 @@
 NameCache = new 
+Weak
 HashMap();%0A    p
@@ -511,32 +511,36 @@
 dKeyCache = new 
+Weak
 HashMap();%0A%09priv
@@ -534,17 +534,20 @@
 hMap();%0A
-%09
+    
 private 
@@ -603,26 +603,32 @@
 tionary() %7B%0A
-%09%09
+        
 this(new Def
@@ -650,17 +650,20 @@
 ter());%0A
-%09
+    
 %7D%0A%0A    p
@@ -713,10 +713,16 @@
 ) %7B%0A
-%09%09
+        
 this
@@ -739,17 +739,20 @@
 sorter;%0A
-%09
+    
 %7D%0A%0A%09/**%0A
@@ -2178,54 +2178,8 @@
 pe;%0A
-        final String clsName = cls.getName();%0A
     
@@ -2326,22 +2326,19 @@
 ainsKey(
-clsNam
+typ
 e)) %7B%0A  
@@ -3258,28 +3258,24 @@
             
-    
 .getDeclarin
@@ -3744,22 +3744,19 @@
 che.put(
-clsNam
+typ
 e, keyed
@@ -3814,22 +3814,19 @@
 che.put(
-clsNam
+typ
 e, sorte
@@ -3958,22 +3958,19 @@
 che.get(
-clsNam
+typ
 e) : key
@@ -4009,14 +4009,11 @@
 get(
-clsNam
+typ
 e));

Use WeakHashMaps to allow gc to collect classes.