Skip to content

Commit

Permalink
Replace FastField with nested maps. Closes #350.
Browse files Browse the repository at this point in the history
  • Loading branch information
joehni committed Oct 22, 2024
1 parent 27345db commit abe035c
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 215 deletions.
3 changes: 2 additions & 1 deletion xstream-distribution/src/content/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ <h1 id="upcoming-1.4.x">Upcoming 1.4.x maintenance release</h1>
<h2>Major changes</h2>

<ul>
<li>GHPR:#350: Optimize memory allocation (by Vladislav Rassokhin of JetBrains).</li>
<li>Add a converter for the WeakHashMap which does not write any elements of the map. Avoids also access to the
ReentrantLock contained in the WeakHashMap since Java 19.</li>
</ul>
Expand All @@ -122,7 +123,7 @@ <h2>Minor changes</h2>
<li>GHPR:#331, GHI:#326: Fix handling of empty java.util.concurrent.atomic.AtomicReference (by Alex Blekhman of Atlassian).</li>
<li>GHPR:#334: Fix remaining buffer size calculation in QuickWriter (by Higuchi Yuta).</li>
<li>GHI:#342: Optimize internal handling of children in DomReader avoiding O(n²) access times for siblings (by Shiang-Yun Yang).</li>
<li>GHPR:#349: Fix support of lambda objects for Java 21 and above (Tobias Gierke).</li>
<li>GHPR:#349: Fix support of lambda objects for Java 21 and above (by Tobias Gierke).</li>
<li>GHI:#359: Add KEYS file with public keys to verify signed artifacts.</li>
<li>Detect input manipulation in c.t.x.io.binary.BinaryStreamReader.</li>
</ul>
Expand Down
3 changes: 2 additions & 1 deletion xstream-distribution/src/content/team.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>
<!--
Copyright (C) 2005, 2006 Joe Walnes.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2022 XStream committers.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 2022, 2024 XStream committers.
All rights reserved.
The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -132,6 +132,7 @@ <h2 id="contributors">Contributors</h2>
<li>Chris Hegarty of Oracle</li>
<li>Emanuel Alves</li>
<li>Basil Crow</li>
<li>Vladislav Rassokhin of JetBrains</li>
</ul>

<p>Please direct all correspondence about XStream to the <a href="mailing-lists.html">mailing list</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2011, 2013, 2014, 2015, 2016 XStream Committers.
* Copyright (C) 2011, 2013, 2014, 2015, 2016, 2024 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 30. July 2011 by Joerg Schaible
*/

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

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

/**
* Creates a new ToAttributedValueConverter instance.
*
*
* @param type the type that is handled by this converter instance
* @param mapper the mapper in use
* @param reflectionProvider the reflection provider in use
Expand All @@ -92,7 +91,7 @@ public ToAttributedValueConverter(

/**
* Creates a new ToAttributedValueConverter instance.
*
*
* @param type the type that is handled by this converter instance
* @param mapper the mapper in use
* @param reflectionProvider the reflection provider in use
Expand Down Expand Up @@ -140,33 +139,33 @@ public void marshal(final Object source, final HierarchicalStreamWriter writer,
final Class<?>[] definingType = new Class[1];
reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
@Override
public void visit(final String fieldName, final Class<?> type, final Class<?> definedIn, final Object value) {
public void visit(final String fieldName, final Class<?> type, final Class<?> definedIn,
final Object value) {
if (!mapper.shouldSerializeMember(definedIn, fieldName)) {
return;
}

final FastField field = new FastField(definedIn, fieldName);
final String alias = mapper.serializedMember(definedIn, fieldName);
if (!defaultFieldDefinition.containsKey(alias)) {
final Class<?> lookupType = sourceType;
defaultFieldDefinition.put(alias, reflectionProvider.getField(lookupType, fieldName));
} else if (!fieldIsEqual(field)) {
} else if (!fieldIsEqual(definedIn, fieldName)) {
final ConversionException exception = new ConversionException(
"Cannot write attribute twice for object");
exception.add("alias", alias);
exception.add("type", sourceType.getName());
throw exception;
}

ConverterMatcher converter = Enum.class.isAssignableFrom(type) ? (ConverterMatcher)enumMapper
.getConverterFromItemType(null, type, null) : (ConverterMatcher)mapper.getLocalConverter(definedIn,
fieldName);
ConverterMatcher converter = Enum.class.isAssignableFrom(type)
? (ConverterMatcher)enumMapper.getConverterFromItemType(null, type, null)
: (ConverterMatcher)mapper.getLocalConverter(definedIn, fieldName);
if (converter == null) {
converter = lookup.lookupConverterForType(type);
}

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

final Set<FastField> seenFields = new HashSet<>();
final MemberDictionary seenFields = new MemberDictionary();
final Iterator<String> it = reader.getAttributeNames();

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

Class<?> type = field.getType();
final Class<?> declaringClass = field.getDeclaringClass();
ConverterMatcher converter = Enum.class.isAssignableFrom(type) ? (ConverterMatcher)enumMapper
.getConverterFromItemType(null, type, null) : (ConverterMatcher)mapper.getLocalConverter(
declaringClass, fieldName);
ConverterMatcher converter = Enum.class.isAssignableFrom(type)
? (ConverterMatcher)enumMapper.getConverterFromItemType(null, type, null)
: (ConverterMatcher)mapper.getLocalConverter(declaringClass, fieldName);
if (converter == null) {
converter = lookup.lookupConverterForType(type);
}
Expand All @@ -273,7 +272,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
}

reflectionProvider.writeField(result, fieldName, value, declaringClass);
if (!seenFields.add(new FastField(declaringClass, fieldName))) {
if (!seenFields.add(declaringClass, fieldName)) {
throw new DuplicateFieldException(fieldName + " [" + declaringClass.getName() + "]");
}
}
Expand Down Expand Up @@ -317,15 +316,15 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
}

reflectionProvider.writeField(result, fieldName, value, classDefiningField);
if (!seenFields.add(new FastField(classDefiningField, fieldName))) {
if (!seenFields.add(classDefiningField, fieldName)) {
throw new DuplicateFieldException(fieldName + " [" + classDefiningField.getName() + "]");
}
}
return result;
}

private boolean fieldIsEqual(final FastField field) {
return valueField.getName().equals(field.getName())
&& valueField.getDeclaringClass().getName().equals(field.getDeclaringClass());
private boolean fieldIsEqual(final Class<?> definedIn, final String name) {
return valueField.getName().equals(name)
&& valueField.getDeclaringClass().getName().equals(definedIn.getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2024 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand All @@ -11,15 +11,12 @@
*/
package com.thoughtworks.xstream.converters.javabean;

import java.util.HashSet;
import java.util.Set;

import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.reflection.MissingFieldException;
import com.thoughtworks.xstream.core.util.FastField;
import com.thoughtworks.xstream.core.util.MemberDictionary;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
Expand Down Expand Up @@ -107,16 +104,7 @@ private void writeNullField(final String propertyName) {
@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
final Object result = instantiateNewInstance(context);
@SuppressWarnings("serial")
final Set<FastField> seenProperties = new HashSet<FastField>() {
@Override
public boolean add(final FastField e) {
if (!super.add(e)) {
throw new DuplicatePropertyException(e.getName());
}
return true;
}
};
final MemberDictionary seenProperties = new MemberDictionary();

final Class<?> resultType = result.getClass();
while (reader.hasMoreChildren()) {
Expand All @@ -131,7 +119,9 @@ public boolean add(final FastField e) {
final Class<?> type = determineType(reader, result, propertyName);
final Object value = context.convertAnother(result, type);
beanProvider.writeProperty(result, propertyName, value);
seenProperties.add(new FastField(resultType, propertyName));
if (!seenProperties.add(resultType, propertyName)) {
throw new DuplicatePropertyException(propertyName);
}
} else if (!mapper.isIgnoredElement(propertyName)) {
throw new MissingFieldException(resultType.getName(), propertyName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2022 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2022, 2024 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.
Expand Down Expand Up @@ -33,9 +33,9 @@
import com.thoughtworks.xstream.core.Caching;
import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
import com.thoughtworks.xstream.core.util.ArrayIterator;
import com.thoughtworks.xstream.core.util.FastField;
import com.thoughtworks.xstream.core.util.Fields;
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
import com.thoughtworks.xstream.core.util.MemberDictionary;
import com.thoughtworks.xstream.core.util.Primitives;
import com.thoughtworks.xstream.core.util.SerializationMembers;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
Expand Down Expand Up @@ -190,8 +190,7 @@ public void writeItem(final Object item) {
}
};

final Map<String, Set<Mapper.ImplicitCollectionMapping>> hiddenMappers =
new HashMap<>();
final Map<String, Set<Mapper.ImplicitCollectionMapping>> hiddenMappers = new HashMap<>();
for (final FieldInfo info : fields) {
if (info.value != null) {
final boolean isCollection = info.value instanceof Collection;
Expand Down Expand Up @@ -279,16 +278,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader,
final UnmarshallingContext context) {
final Class<?> resultType = result.getClass();
@SuppressWarnings("serial")
final Set<FastField> seenFields = new HashSet<FastField>() {
@Override
public boolean add(final FastField e) {
if (!super.add(e)) {
throw new DuplicateFieldException(e.getName());
}
return true;
}
};
final MemberDictionary seenFields = new MemberDictionary();

// process attributes before recursing into child elements.
final Iterator<String> it = reader.getAttributeNames();
Expand Down Expand Up @@ -318,7 +308,9 @@ public boolean add(final FastField e) {
exception.add("target-type", type.getName());
throw exception;
}
seenFields.add(new FastField(classDefiningField, attrName));
if (!seenFields.add(classDefiningField, attrName)) {
throw new DuplicateFieldException(attrName);
}
reflectionProvider.writeField(result, attrName, value, classDefiningField);
}
}
Expand Down Expand Up @@ -445,13 +437,18 @@ public boolean add(final FastField e) {

if (field != null) {
reflectionProvider.writeField(result, fieldName, value, field.getDeclaringClass());
seenFields.add(new FastField(field.getDeclaringClass(), fieldName));
if (!seenFields.add(field.getDeclaringClass(), fieldName)) {
throw new DuplicateFieldException(fieldName);
}
} else if (type != null) {
if (implicitFieldName == null) {
// look for implicit field
implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, value != null
? value.getClass()
: Mapper.Null.class, originalNodeName);
implicitFieldName = mapper.getFieldNameForItemTypeAndName(fieldDeclaringClass, value != null
? value.getClass()
: Mapper.Null.class, originalNodeName);
}
if (implicitCollectionsForCurrentObject == null) {
implicitCollectionsForCurrentObject = new HashMap<>();
Expand Down Expand Up @@ -625,10 +622,7 @@ public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if ((obj == null) || (getClass() != obj.getClass())) {
return false;
}
final FieldLocation other = (FieldLocation)obj;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005, 2006 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2018, 2024 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -396,7 +396,7 @@ public Map<String, Object> readFieldsFromStream() {

@Override
public void defaultReadObject() {
if (serializationMembers.getSerializablePersistentFields(currentType[0]) != null) {
if (serializationMembers.hasSerializablePersistentFields(currentType[0])) {
readFieldsFromStream();
return;
}
Expand Down
Loading

0 comments on commit abe035c

Please sign in to comment.