Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit abe035c

Browse files
committedOct 22, 2024·
Replace FastField with nested maps. Closes #350.
1 parent 27345db commit abe035c

File tree

13 files changed

+281
-215
lines changed

13 files changed

+281
-215
lines changed
 

‎xstream-distribution/src/content/changes.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ <h1 id="upcoming-1.4.x">Upcoming 1.4.x maintenance release</h1>
111111
<h2>Major changes</h2>
112112

113113
<ul>
114+
<li>GHPR:#350: Optimize memory allocation (by Vladislav Rassokhin of JetBrains).</li>
114115
<li>Add a converter for the WeakHashMap which does not write any elements of the map. Avoids also access to the
115116
ReentrantLock contained in the WeakHashMap since Java 19.</li>
116117
</ul>
@@ -122,7 +123,7 @@ <h2>Minor changes</h2>
122123
<li>GHPR:#331, GHI:#326: Fix handling of empty java.util.concurrent.atomic.AtomicReference (by Alex Blekhman of Atlassian).</li>
123124
<li>GHPR:#334: Fix remaining buffer size calculation in QuickWriter (by Higuchi Yuta).</li>
124125
<li>GHI:#342: Optimize internal handling of children in DomReader avoiding O(n²) access times for siblings (by Shiang-Yun Yang).</li>
125-
<li>GHPR:#349: Fix support of lambda objects for Java 21 and above (Tobias Gierke).</li>
126+
<li>GHPR:#349: Fix support of lambda objects for Java 21 and above (by Tobias Gierke).</li>
126127
<li>GHI:#359: Add KEYS file with public keys to verify signed artifacts.</li>
127128
<li>Detect input manipulation in c.t.x.io.binary.BinaryStreamReader.</li>
128129
</ul>

‎xstream-distribution/src/content/team.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<html>
22
<!--
33
Copyright (C) 2005, 2006 Joe Walnes.
4-
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2022 XStream committers.
4+
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2022, 2024 XStream committers.
55
All rights reserved.
66
77
The software in this package is published under the terms of the BSD
@@ -132,6 +132,7 @@ <h2 id="contributors">Contributors</h2>
132132
<li>Chris Hegarty of Oracle</li>
133133
<li>Emanuel Alves</li>
134134
<li>Basil Crow</li>
135+
<li>Vladislav Rassokhin of JetBrains</li>
135136
</ul>
136137

137138
<p>Please direct all correspondence about XStream to the <a href="mailing-lists.html">mailing list</a>

‎xstream/src/java/com/thoughtworks/xstream/converters/extended/ToAttributedValueConverter.java

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright (C) 2011, 2013, 2014, 2015, 2016 XStream Committers.
2+
* Copyright (C) 2011, 2013, 2014, 2015, 2016, 2024 XStream Committers.
33
* All rights reserved.
44
*
55
* The software in this package is published under the terms of the BSD
66
* style license a copy of which has been included with this distribution in
77
* the LICENSE.txt file.
8-
*
8+
*
99
* Created on 30. July 2011 by Joerg Schaible
1010
*/
1111

@@ -28,8 +28,8 @@
2828
import com.thoughtworks.xstream.converters.UnmarshallingContext;
2929
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.DuplicateFieldException;
3030
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
31-
import com.thoughtworks.xstream.core.util.FastField;
3231
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
32+
import com.thoughtworks.xstream.core.util.MemberDictionary;
3333
import com.thoughtworks.xstream.core.util.Primitives;
3434
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
3535
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
@@ -45,7 +45,7 @@
4545
* field name itself. Therefore it is possible to define an inherited field as value. It is also possible to provide no
4646
* value field at all, so that all fields are written as attributes.
4747
* </p>
48-
*
48+
*
4949
* @author J&ouml;rg Schaible
5050
* @since 1.4
5151
*/
@@ -59,10 +59,9 @@ public class ToAttributedValueConverter implements Converter {
5959
private final Field valueField;
6060

6161
/**
62-
* Creates a new ToAttributedValueConverter instance.
63-
*
64-
* All field elements will be attributes, the element itself will have no value.
65-
*
62+
* Creates a new ToAttributedValueConverter instance. All field elements will be attributes, the element itself will
63+
* have no value.
64+
*
6665
* @param type the type that is handled by this converter instance
6766
* @param mapper the mapper in use
6867
* @param reflectionProvider the reflection provider in use
@@ -77,7 +76,7 @@ public ToAttributedValueConverter(
7776

7877
/**
7978
* Creates a new ToAttributedValueConverter instance.
80-
*
79+
*
8180
* @param type the type that is handled by this converter instance
8281
* @param mapper the mapper in use
8382
* @param reflectionProvider the reflection provider in use
@@ -92,7 +91,7 @@ public ToAttributedValueConverter(
9291

9392
/**
9493
* Creates a new ToAttributedValueConverter instance.
95-
*
94+
*
9695
* @param type the type that is handled by this converter instance
9796
* @param mapper the mapper in use
9897
* @param reflectionProvider the reflection provider in use
@@ -140,33 +139,33 @@ public void marshal(final Object source, final HierarchicalStreamWriter writer,
140139
final Class<?>[] definingType = new Class[1];
141140
reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
142141
@Override
143-
public void visit(final String fieldName, final Class<?> type, final Class<?> definedIn, final Object value) {
142+
public void visit(final String fieldName, final Class<?> type, final Class<?> definedIn,
143+
final Object value) {
144144
if (!mapper.shouldSerializeMember(definedIn, fieldName)) {
145145
return;
146146
}
147147

148-
final FastField field = new FastField(definedIn, fieldName);
149148
final String alias = mapper.serializedMember(definedIn, fieldName);
150149
if (!defaultFieldDefinition.containsKey(alias)) {
151150
final Class<?> lookupType = sourceType;
152151
defaultFieldDefinition.put(alias, reflectionProvider.getField(lookupType, fieldName));
153-
} else if (!fieldIsEqual(field)) {
152+
} else if (!fieldIsEqual(definedIn, fieldName)) {
154153
final ConversionException exception = new ConversionException(
155154
"Cannot write attribute twice for object");
156155
exception.add("alias", alias);
157156
exception.add("type", sourceType.getName());
158157
throw exception;
159158
}
160159

161-
ConverterMatcher converter = Enum.class.isAssignableFrom(type) ? (ConverterMatcher)enumMapper
162-
.getConverterFromItemType(null, type, null) : (ConverterMatcher)mapper.getLocalConverter(definedIn,
163-
fieldName);
160+
ConverterMatcher converter = Enum.class.isAssignableFrom(type)
161+
? (ConverterMatcher)enumMapper.getConverterFromItemType(null, type, null)
162+
: (ConverterMatcher)mapper.getLocalConverter(definedIn, fieldName);
164163
if (converter == null) {
165164
converter = lookup.lookupConverterForType(type);
166165
}
167166

168167
if (value != null) {
169-
final boolean isValueField = valueField != null && fieldIsEqual(field);
168+
final boolean isValueField = valueField != null && fieldIsEqual(definedIn, fieldName);
170169
if (isValueField) {
171170
definingType[0] = definedIn;
172171
fieldType[0] = type;
@@ -222,7 +221,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
222221
final Object result = reflectionProvider.newInstance(context.getRequiredType());
223222
final Class<?> resultType = result.getClass();
224223

225-
final Set<FastField> seenFields = new HashSet<>();
224+
final MemberDictionary seenFields = new MemberDictionary();
226225
final Iterator<String> it = reader.getAttributeNames();
227226

228227
final Set<String> systemAttributes = new HashSet<>();
@@ -244,9 +243,9 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
244243

245244
Class<?> type = field.getType();
246245
final Class<?> declaringClass = field.getDeclaringClass();
247-
ConverterMatcher converter = Enum.class.isAssignableFrom(type) ? (ConverterMatcher)enumMapper
248-
.getConverterFromItemType(null, type, null) : (ConverterMatcher)mapper.getLocalConverter(
249-
declaringClass, fieldName);
246+
ConverterMatcher converter = Enum.class.isAssignableFrom(type)
247+
? (ConverterMatcher)enumMapper.getConverterFromItemType(null, type, null)
248+
: (ConverterMatcher)mapper.getLocalConverter(declaringClass, fieldName);
250249
if (converter == null) {
251250
converter = lookup.lookupConverterForType(type);
252251
}
@@ -273,7 +272,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
273272
}
274273

275274
reflectionProvider.writeField(result, fieldName, value, declaringClass);
276-
if (!seenFields.add(new FastField(declaringClass, fieldName))) {
275+
if (!seenFields.add(declaringClass, fieldName)) {
277276
throw new DuplicateFieldException(fieldName + " [" + declaringClass.getName() + "]");
278277
}
279278
}
@@ -317,15 +316,15 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
317316
}
318317

319318
reflectionProvider.writeField(result, fieldName, value, classDefiningField);
320-
if (!seenFields.add(new FastField(classDefiningField, fieldName))) {
319+
if (!seenFields.add(classDefiningField, fieldName)) {
321320
throw new DuplicateFieldException(fieldName + " [" + classDefiningField.getName() + "]");
322321
}
323322
}
324323
return result;
325324
}
326325

327-
private boolean fieldIsEqual(final FastField field) {
328-
return valueField.getName().equals(field.getName())
329-
&& valueField.getDeclaringClass().getName().equals(field.getDeclaringClass());
326+
private boolean fieldIsEqual(final Class<?> definedIn, final String name) {
327+
return valueField.getName().equals(name)
328+
&& valueField.getDeclaringClass().getName().equals(definedIn.getName());
330329
}
331330
}

‎xstream/src/java/com/thoughtworks/xstream/converters/javabean/JavaBeanConverter.java

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (C) 2005 Joe Walnes.
3-
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018 XStream Committers.
3+
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2024 XStream Committers.
44
* All rights reserved.
55
*
66
* The software in this package is published under the terms of the BSD
@@ -11,15 +11,12 @@
1111
*/
1212
package com.thoughtworks.xstream.converters.javabean;
1313

14-
import java.util.HashSet;
15-
import java.util.Set;
16-
1714
import com.thoughtworks.xstream.converters.ConversionException;
1815
import com.thoughtworks.xstream.converters.Converter;
1916
import com.thoughtworks.xstream.converters.MarshallingContext;
2017
import com.thoughtworks.xstream.converters.UnmarshallingContext;
2118
import com.thoughtworks.xstream.converters.reflection.MissingFieldException;
22-
import com.thoughtworks.xstream.core.util.FastField;
19+
import com.thoughtworks.xstream.core.util.MemberDictionary;
2320
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
2421
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
2522
import com.thoughtworks.xstream.mapper.Mapper;
@@ -107,16 +104,7 @@ private void writeNullField(final String propertyName) {
107104
@Override
108105
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
109106
final Object result = instantiateNewInstance(context);
110-
@SuppressWarnings("serial")
111-
final Set<FastField> seenProperties = new HashSet<FastField>() {
112-
@Override
113-
public boolean add(final FastField e) {
114-
if (!super.add(e)) {
115-
throw new DuplicatePropertyException(e.getName());
116-
}
117-
return true;
118-
}
119-
};
107+
final MemberDictionary seenProperties = new MemberDictionary();
120108

121109
final Class<?> resultType = result.getClass();
122110
while (reader.hasMoreChildren()) {
@@ -131,7 +119,9 @@ public boolean add(final FastField e) {
131119
final Class<?> type = determineType(reader, result, propertyName);
132120
final Object value = context.convertAnother(result, type);
133121
beanProvider.writeProperty(result, propertyName, value);
134-
seenProperties.add(new FastField(resultType, propertyName));
122+
if (!seenProperties.add(resultType, propertyName)) {
123+
throw new DuplicatePropertyException(propertyName);
124+
}
135125
} else if (!mapper.isIgnoredElement(propertyName)) {
136126
throw new MissingFieldException(resultType.getName(), propertyName);
137127
}

‎xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
22
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
3-
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2022 XStream Committers.
3+
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2022, 2024 XStream Committers.
44
* All rights reserved.
5-
*
5+
*
66
* The software in this package is published under the terms of the BSD
77
* style license a copy of which has been included with this distribution in
88
* the LICENSE.txt file.
@@ -33,9 +33,9 @@
3333
import com.thoughtworks.xstream.core.Caching;
3434
import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
3535
import com.thoughtworks.xstream.core.util.ArrayIterator;
36-
import com.thoughtworks.xstream.core.util.FastField;
3736
import com.thoughtworks.xstream.core.util.Fields;
3837
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
38+
import com.thoughtworks.xstream.core.util.MemberDictionary;
3939
import com.thoughtworks.xstream.core.util.Primitives;
4040
import com.thoughtworks.xstream.core.util.SerializationMembers;
4141
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
@@ -190,8 +190,7 @@ public void writeItem(final Object item) {
190190
}
191191
};
192192

193-
final Map<String, Set<Mapper.ImplicitCollectionMapping>> hiddenMappers =
194-
new HashMap<>();
193+
final Map<String, Set<Mapper.ImplicitCollectionMapping>> hiddenMappers = new HashMap<>();
195194
for (final FieldInfo info : fields) {
196195
if (info.value != null) {
197196
final boolean isCollection = info.value instanceof Collection;
@@ -279,16 +278,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
279278
public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader,
280279
final UnmarshallingContext context) {
281280
final Class<?> resultType = result.getClass();
282-
@SuppressWarnings("serial")
283-
final Set<FastField> seenFields = new HashSet<FastField>() {
284-
@Override
285-
public boolean add(final FastField e) {
286-
if (!super.add(e)) {
287-
throw new DuplicateFieldException(e.getName());
288-
}
289-
return true;
290-
}
291-
};
281+
final MemberDictionary seenFields = new MemberDictionary();
292282

293283
// process attributes before recursing into child elements.
294284
final Iterator<String> it = reader.getAttributeNames();
@@ -318,7 +308,9 @@ public boolean add(final FastField e) {
318308
exception.add("target-type", type.getName());
319309
throw exception;
320310
}
321-
seenFields.add(new FastField(classDefiningField, attrName));
311+
if (!seenFields.add(classDefiningField, attrName)) {
312+
throw new DuplicateFieldException(attrName);
313+
}
322314
reflectionProvider.writeField(result, attrName, value, classDefiningField);
323315
}
324316
}
@@ -445,13 +437,18 @@ public boolean add(final FastField e) {
445437

446438
if (field != null) {
447439
reflectionProvider.writeField(result, fieldName, value, field.getDeclaringClass());
448-
seenFields.add(new FastField(field.getDeclaringClass(), fieldName));
440+
if (!seenFields.add(field.getDeclaringClass(), fieldName)) {
441+
throw new DuplicateFieldException(fieldName);
442+
}
449443
} else if (type != null) {
450444
if (implicitFieldName == null) {
451445
// look for implicit field
452446
implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, value != null
453447
? value.getClass()
454448
: Mapper.Null.class, originalNodeName);
449+
implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, value != null
450+
? value.getClass()
451+
: Mapper.Null.class, originalNodeName);
455452
}
456453
if (implicitCollectionsForCurrentObject == null) {
457454
implicitCollectionsForCurrentObject = new HashMap<>();
@@ -625,10 +622,7 @@ public boolean equals(final Object obj) {
625622
if (this == obj) {
626623
return true;
627624
}
628-
if (obj == null) {
629-
return false;
630-
}
631-
if (getClass() != obj.getClass()) {
625+
if ((obj == null) || (getClass() != obj.getClass())) {
632626
return false;
633627
}
634628
final FieldLocation other = (FieldLocation)obj;

‎xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
3-
* Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018 XStream Committers.
3+
* Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2024 XStream Committers.
44
* All rights reserved.
55
*
66
* The software in this package is published under the terms of the BSD
@@ -396,7 +396,7 @@ public Map<String, Object> readFieldsFromStream() {
396396

397397
@Override
398398
public void defaultReadObject() {
399-
if (serializationMembers.getSerializablePersistentFields(currentType[0]) != null) {
399+
if (serializationMembers.hasSerializablePersistentFields(currentType[0])) {
400400
readFieldsFromStream();
401401
return;
402402
}

0 commit comments

Comments
 (0)