Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport from 1.5-Branch: Optimize FieldDictionary #18

Merged
merged 1 commit into from
Jul 5, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,25 @@
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 14. May 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.converters.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

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


/**
* A field dictionary instance caches information about classes fields.
*
Expand All @@ -35,8 +30,9 @@
*/
public class FieldDictionary implements Caching {

private transient Map keyedByFieldNameCache;
private transient Map keyedByFieldKeyCache;
private static final DictionaryEntry OBJECT_DICTIONARY_ENTRY = new DictionaryEntry(Collections.EMPTY_MAP, Collections.EMPTY_MAP);

private transient Map dictionaryEntries;
private final FieldKeySorter sorter;

public FieldDictionary() {
Expand All @@ -48,11 +44,8 @@ public FieldDictionary(FieldKeySorter sorter) {
init();
}

private void init() {
keyedByFieldNameCache = new HashMap();
keyedByFieldKeyCache = new HashMap();
keyedByFieldNameCache.put(Object.class, Collections.EMPTY_MAP);
keyedByFieldKeyCache.put(Object.class, Collections.EMPTY_MAP);
private synchronized void init() {
dictionaryEntries = new HashMap();
}

/**
Expand Down Expand Up @@ -111,83 +104,116 @@ public Field field(Class cls, String name, Class definedIn) {
*/
public Field fieldOrNull(Class cls, String name, Class definedIn) {
Map fields = buildMap(cls, definedIn != null);
Field field = (Field)fields.get(definedIn != null
? (Object)new FieldKey(name, definedIn, -1)
: (Object)name);
Field field = (Field) fields.get(definedIn != null
? (Object) new FieldKey(name, definedIn, -1)
: (Object) name);
return field;
}

private Map buildMap(final Class type, boolean tupleKeyed) {
private Map buildMap(final Class type, final boolean tupleKeyed) {

Class cls = type;
synchronized (this) {
if (!keyedByFieldNameCache.containsKey(type)) {
final List superClasses = new ArrayList();
while (!Object.class.equals(cls) && cls != null) {
superClasses.add(0, cls);
cls = cls.getSuperclass();
}
Map lastKeyedByFieldName = Collections.EMPTY_MAP;
Map lastKeyedByFieldKey = Collections.EMPTY_MAP;
for (final Iterator iter = superClasses.iterator(); iter.hasNext();) {
cls = (Class)iter.next();
if (!keyedByFieldNameCache.containsKey(cls)) {
final Map keyedByFieldName = new HashMap(lastKeyedByFieldName);
final Map keyedByFieldKey = new OrderRetainingMap(lastKeyedByFieldKey);
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];
if (!field.isAccessible()) {
field.setAccessible(true);
}
FieldKey fieldKey = new FieldKey(
field.getName(), field.getDeclaringClass(), i);
Field existent = (Field)keyedByFieldName.get(field.getName());
if (existent == null
// do overwrite statics
|| ((existent.getModifiers() & Modifier.STATIC) != 0)
// overwrite non-statics with non-statics only
|| (existent != null && ((field.getModifiers() & Modifier.STATIC) == 0))) {
keyedByFieldName.put(field.getName(), field);
}
keyedByFieldKey.put(fieldKey, field);
}
final Map sortedFieldKeys = sorter.sort(cls, keyedByFieldKey);
keyedByFieldNameCache.put(cls, keyedByFieldName);
keyedByFieldKeyCache.put(cls, sortedFieldKeys);
lastKeyedByFieldName = keyedByFieldName;
lastKeyedByFieldKey = sortedFieldKeys;
} else {
lastKeyedByFieldName = (Map)keyedByFieldNameCache.get(cls);
lastKeyedByFieldKey = (Map)keyedByFieldKeyCache.get(cls);
}

DictionaryEntry lastDictionaryEntry = null;
final LinkedList superClasses = new LinkedList();
while (lastDictionaryEntry == null) {
if (Object.class.equals(cls) || cls == null) {
lastDictionaryEntry = OBJECT_DICTIONARY_ENTRY;
} else {
lastDictionaryEntry = getDictionaryEntry(cls);
}
if (lastDictionaryEntry == null) {
superClasses.addFirst(cls);
cls = cls.getSuperclass();
}
}

for (final Iterator iter = superClasses.iterator(); iter.hasNext();) {
cls = (Class) iter.next();
DictionaryEntry newDictionaryEntry = buildDictionaryEntryForClass(cls, lastDictionaryEntry);
synchronized (this) {
DictionaryEntry concurrentEntry = getDictionaryEntry(cls);
if (concurrentEntry == null) {
dictionaryEntries.put(cls, newDictionaryEntry);
} else {
newDictionaryEntry = concurrentEntry;
}
return tupleKeyed ? lastKeyedByFieldKey : lastKeyedByFieldName;
}
lastDictionaryEntry = newDictionaryEntry;
}

return tupleKeyed ? lastDictionaryEntry.getKeyedByFieldKey() : lastDictionaryEntry.getKeyedByFieldName();

}

private DictionaryEntry buildDictionaryEntryForClass(Class cls, DictionaryEntry lastDictionaryEntry) {
final Map keyedByFieldName = new HashMap(lastDictionaryEntry.getKeyedByFieldName());
final Map keyedByFieldKey = new OrderRetainingMap(lastDictionaryEntry.getKeyedByFieldKey());
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;
}
}
return (Map)(tupleKeyed
? keyedByFieldKeyCache.get(type)
: keyedByFieldNameCache.get(type));
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (!field.isAccessible()) {
field.setAccessible(true);
}
FieldKey fieldKey = new FieldKey(
field.getName(), field.getDeclaringClass(), i);
Field existent = (Field) keyedByFieldName.get(field.getName());
if (existent == null
// do overwrite statics
|| ((existent.getModifiers() & Modifier.STATIC) != 0)
// overwrite non-statics with non-statics only
|| (existent != null && ((field.getModifiers() & Modifier.STATIC) == 0))) {
keyedByFieldName.put(field.getName(), field);
}
keyedByFieldKey.put(fieldKey, field);
}
final Map sortedFieldKeys = sorter.sort(cls, keyedByFieldKey);
return new DictionaryEntry(keyedByFieldName, sortedFieldKeys);
}

private synchronized DictionaryEntry getDictionaryEntry(Class cls) {
return (DictionaryEntry) dictionaryEntries.get(cls);
}

public synchronized void flushCache() {
Set objectTypeSet = Collections.singleton(Object.class);
keyedByFieldNameCache.keySet().retainAll(objectTypeSet);
keyedByFieldKeyCache.keySet().retainAll(objectTypeSet);
dictionaryEntries.clear();
if (sorter instanceof Caching) {
((Caching)sorter).flushCache();
((Caching) sorter).flushCache();
}
}

protected Object readResolve() {
init();
return this;
}

private static final class DictionaryEntry {

private final Map keyedByFieldName;
private final Map keyedByFieldKey;

public DictionaryEntry(Map keyedByFieldName, Map keyedByFieldKey) {
super();
this.keyedByFieldName = keyedByFieldName;
this.keyedByFieldKey = keyedByFieldKey;
}

public Map getKeyedByFieldName() {
return keyedByFieldName;
}

public Map getKeyedByFieldKey() {
return keyedByFieldKey;
}

}

}