001: /*
002: * Copyright (C) 2004, 2005, 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007, 2008 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 14. May 2004 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.converters.reflection;
013:
014: import java.lang.reflect.Field;
015: import java.lang.reflect.Modifier;
016: import java.util.ArrayList;
017: import java.util.Collections;
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.Map;
022: import java.util.WeakHashMap;
023:
024: import com.thoughtworks.xstream.core.JVM;
025: import com.thoughtworks.xstream.core.util.OrderRetainingMap;
026:
027: /**
028: * A field dictionary instance caches information about classes fields.
029: *
030: * @author Joe Walnes
031: * @author Jörg Schaible
032: * @author Guilherme Silveira
033: */
034: public class FieldDictionary {
035:
036: private transient Map keyedByFieldNameCache;
037: private transient Map keyedByFieldKeyCache;
038: private final FieldKeySorter sorter;
039:
040: public FieldDictionary() {
041: this (new ImmutableFieldKeySorter());
042: }
043:
044: public FieldDictionary(FieldKeySorter sorter) {
045: this .sorter = sorter;
046: init();
047: }
048:
049: private void init() {
050: keyedByFieldNameCache = new WeakHashMap();
051: keyedByFieldKeyCache = new WeakHashMap();
052: keyedByFieldNameCache.put(Object.class, Collections.EMPTY_MAP);
053: keyedByFieldKeyCache.put(Object.class, Collections.EMPTY_MAP);
054: }
055:
056: /**
057: * Returns an iterator for all fields for some class
058: *
059: * @param cls the class you are interested on
060: * @return an iterator for its fields
061: * @deprecated since 1.3, use {@link #fieldsFor(Class)} instead
062: */
063: public Iterator serializableFieldsFor(Class cls) {
064: return fieldsFor(cls);
065: }
066:
067: /**
068: * Returns an iterator for all fields for some class
069: *
070: * @param cls the class you are interested on
071: * @return an iterator for its fields
072: */
073: public Iterator fieldsFor(Class cls) {
074: return buildMap(cls, true).values().iterator();
075: }
076:
077: /**
078: * Returns an specific field of some class. If definedIn is null, it searches for the field
079: * named 'name' inside the class cls. If definedIn is different than null, tries to find the
080: * specified field name in the specified class cls which should be defined in class
081: * definedIn (either equals cls or a one of it's superclasses)
082: *
083: * @param cls the class where the field is to be searched
084: * @param name the field name
085: * @param definedIn the superclass (or the class itself) of cls where the field was defined
086: * @return the field itself
087: */
088: public Field field(Class cls, String name, Class definedIn) {
089: Map fields = buildMap(cls, definedIn != null);
090: Field field = (Field) fields
091: .get(definedIn != null ? (Object) new FieldKey(name,
092: definedIn, 0) : (Object) name);
093: if (field == null) {
094: throw new ObjectAccessException("No such field "
095: + cls.getName() + "." + name);
096: } else {
097: return field;
098: }
099: }
100:
101: private Map buildMap(final Class type, boolean tupleKeyed) {
102: Class cls = type;
103: synchronized (this ) {
104: if (!keyedByFieldNameCache.containsKey(type)) {
105: final List super Classes = new ArrayList();
106: while (!Object.class.equals(cls)) {
107: super Classes.add(0, cls);
108: cls = cls.getSuperclass();
109: }
110: Map lastKeyedByFieldName = Collections.EMPTY_MAP;
111: Map lastKeyedByFieldKey = Collections.EMPTY_MAP;
112: for (final Iterator iter = super Classes.iterator(); iter
113: .hasNext();) {
114: cls = (Class) iter.next();
115: if (!keyedByFieldNameCache.containsKey(cls)) {
116: final Map keyedByFieldName = new HashMap(
117: lastKeyedByFieldName);
118: final Map keyedByFieldKey = new OrderRetainingMap(
119: lastKeyedByFieldKey);
120: Field[] fields = cls.getDeclaredFields();
121: if (JVM.reverseFieldDefinition()) {
122: for (int i = fields.length >> 1; i-- > 0;) {
123: final int idx = fields.length - i - 1;
124: final Field field = fields[i];
125: fields[i] = fields[idx];
126: fields[idx] = field;
127: }
128: }
129: for (int i = 0; i < fields.length; i++) {
130: Field field = fields[i];
131: FieldKey fieldKey = new FieldKey(field
132: .getName(), field
133: .getDeclaringClass(), i);
134: field.setAccessible(true);
135: Field existent = (Field) keyedByFieldName
136: .get(field.getName());
137: if (existent == null
138: // do overwrite statics
139: || ((existent.getModifiers() & Modifier.STATIC) != 0)
140: // overwrite non-statics with non-statics only
141: || (existent != null && ((field
142: .getModifiers() & Modifier.STATIC) == 0))) {
143: keyedByFieldName.put(field.getName(),
144: field);
145: }
146: keyedByFieldKey.put(fieldKey, field);
147: }
148: keyedByFieldNameCache
149: .put(cls, keyedByFieldName);
150: keyedByFieldKeyCache.put(cls, sorter.sort(type,
151: keyedByFieldKey));
152: }
153: lastKeyedByFieldName = (Map) keyedByFieldNameCache
154: .get(cls);
155: lastKeyedByFieldKey = (Map) keyedByFieldKeyCache
156: .get(cls);
157: }
158: }
159: }
160: return (Map) (tupleKeyed ? keyedByFieldKeyCache.get(type)
161: : keyedByFieldNameCache.get(type));
162: }
163:
164: protected Object readResolve() {
165: init();
166: return this;
167: }
168:
169: }
|