1
10 package com.thoughtworks.xstream.converters.reflection;
11
12 import java.lang.reflect.Field;
13 import java.lang.reflect.Modifier;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.Iterator;
17 import java.util.LinkedList;
18 import java.util.Map;
19
20 import com.thoughtworks.xstream.core.Caching;
21 import com.thoughtworks.xstream.core.JVM;
22 import com.thoughtworks.xstream.core.util.OrderRetainingMap;
23
24
25
32 public class FieldDictionary implements Caching {
33
34 private static final DictionaryEntry OBJECT_DICTIONARY_ENTRY = new DictionaryEntry(Collections.EMPTY_MAP,
35 Collections.EMPTY_MAP);
36
37 private transient Map dictionaryEntries;
38 private transient FieldUtil fieldUtil;
39 private final FieldKeySorter sorter;
40
41 public FieldDictionary() {
42 this(new ImmutableFieldKeySorter());
43 }
44
45 public FieldDictionary(final FieldKeySorter sorter) {
46 this.sorter = sorter;
47 init();
48 }
49
50 private void init() {
51 dictionaryEntries = new HashMap();
52 if (JVM.is15())
53 try {
54 fieldUtil = (FieldUtil)JVM.loadClassForName("com.thoughtworks.xstream.converters.reflection.FieldUtil15", true).newInstance();
55 } catch (Exception e) {
56 ;
57 }
58 if (fieldUtil == null)
59 fieldUtil = new FieldUtil14();
60 }
61
62
69 public Iterator serializableFieldsFor(final Class cls) {
70 return fieldsFor(cls);
71 }
72
73
79 public Iterator fieldsFor(final Class cls) {
80 return buildMap(cls, true).values().iterator();
81 }
82
83
94 public Field field(final Class cls, final String name, final Class definedIn) {
95 final Field field = fieldOrNull(cls, name, definedIn);
96 if (field == null) {
97 throw new MissingFieldException(cls.getName(), name);
98 } else {
99 return field;
100 }
101 }
102
103
114 public Field fieldOrNull(final Class cls, final String name, final Class definedIn) {
115 final Map fields = buildMap(cls, definedIn != null);
116 final Field field = (Field)fields.get(definedIn != null
117 ? (Object)new FieldKey(name, definedIn, -1)
118 : (Object)name);
119 return field;
120 }
121
122 private Map buildMap(final Class type, final boolean tupleKeyed) {
123
124 Class cls = type;
125
126 DictionaryEntry lastDictionaryEntry = null;
127 final LinkedList superClasses = new LinkedList();
128 while (lastDictionaryEntry == null) {
129 if (Object.class.equals(cls) || cls == null) {
130 lastDictionaryEntry = OBJECT_DICTIONARY_ENTRY;
131 } else {
132 lastDictionaryEntry = getDictionaryEntry(cls);
133 }
134 if (lastDictionaryEntry == null) {
135 superClasses.addFirst(cls);
136 cls = cls.getSuperclass();
137 }
138 }
139
140 for (final Iterator iter = superClasses.iterator(); iter.hasNext();) {
141 cls = (Class)iter.next();
142 DictionaryEntry newDictionaryEntry = buildDictionaryEntryForClass(cls, lastDictionaryEntry);
143 synchronized (this) {
144 final DictionaryEntry concurrentEntry = getDictionaryEntry(cls);
145 if (concurrentEntry == null) {
146 dictionaryEntries.put(cls, newDictionaryEntry);
147 } else {
148 newDictionaryEntry = concurrentEntry;
149 }
150 }
151 lastDictionaryEntry = newDictionaryEntry;
152 }
153
154 return tupleKeyed ? lastDictionaryEntry.getKeyedByFieldKey() : lastDictionaryEntry.getKeyedByFieldName();
155
156 }
157
158 private DictionaryEntry buildDictionaryEntryForClass(final Class cls, final DictionaryEntry lastDictionaryEntry) {
159 final Map keyedByFieldName = new HashMap(lastDictionaryEntry.getKeyedByFieldName());
160 final Map keyedByFieldKey = new OrderRetainingMap(lastDictionaryEntry.getKeyedByFieldKey());
161 final Field[] fields = cls.getDeclaredFields();
162 if (JVM.reverseFieldDefinition()) {
163 for (int i = fields.length >> 1; i-- > 0;) {
164 final int idx = fields.length - i - 1;
165 final Field field = fields[i];
166 fields[i] = fields[idx];
167 fields[idx] = field;
168 }
169 }
170 for (int i = 0; i < fields.length; i++) {
171 final Field field = fields[i];
172 if (fieldUtil.isSynthetic(field) && field.getName().startsWith("$jacoco")) {
173 continue;
174 }
175 if (!field.isAccessible()) {
176 field.setAccessible(true);
177 }
178 final FieldKey fieldKey = new FieldKey(field.getName(), field.getDeclaringClass(), i);
179 final Field existent = (Field)keyedByFieldName.get(field.getName());
180 if (existent == null
181
182 || (existent.getModifiers() & Modifier.STATIC) != 0
183
184 || (existent != null && (field.getModifiers() & Modifier.STATIC) == 0)) {
185 keyedByFieldName.put(field.getName(), field);
186 }
187 keyedByFieldKey.put(fieldKey, field);
188 }
189 final Map sortedFieldKeys = sorter.sort(cls, keyedByFieldKey);
190 return new DictionaryEntry(keyedByFieldName, sortedFieldKeys);
191 }
192
193 private synchronized DictionaryEntry getDictionaryEntry(final Class cls) {
194 return (DictionaryEntry)dictionaryEntries.get(cls);
195 }
196
197 public synchronized void flushCache() {
198 dictionaryEntries.clear();
199 if (sorter instanceof Caching) {
200 ((Caching)sorter).flushCache();
201 }
202 }
203
204 protected Object readResolve() {
205 init();
206 return this;
207 }
208
209 interface FieldUtil {
210 boolean isSynthetic(Field field);
211 }
212
213 private static final class DictionaryEntry {
214
215 private final Map keyedByFieldName;
216 private final Map keyedByFieldKey;
217
218 public DictionaryEntry(final Map keyedByFieldName, final Map keyedByFieldKey) {
219 super();
220 this.keyedByFieldName = keyedByFieldName;
221 this.keyedByFieldKey = keyedByFieldKey;
222 }
223
224 public Map getKeyedByFieldName() {
225 return keyedByFieldName;
226 }
227
228 public Map getKeyedByFieldKey() {
229 return keyedByFieldKey;
230 }
231 }
232 }
233