Skip to content

Commit

Permalink
Add converter for AtomicReference.
Browse files Browse the repository at this point in the history
  • Loading branch information
joehni committed Dec 29, 2022
1 parent 8eac86e commit b03bec5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 5 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 @@ -109,7 +109,7 @@ <h1 id="upcoming-1.4.x">Upcoming 1.4.x maintenance release</h1>
<h2>Major changes</h2>

<ul>
<li>GHI:#308: Add converter for AtomicBoolean, AtomicInteger, and AtomicLong of
<li>GHI:#308: Add converter for AtomicBoolean, AtomicInteger, AtomicLong, and AtomicReference of
package java.util.concurrent.atomic.</li>
</ul>

Expand All @@ -126,6 +126,7 @@ <h2>API changes</h2>
<li>Added c.t.x.converters.extended.AtomicBooleanConverter.</li>
<li>Added c.t.x.converters.extended.AtomicIntegerConverter.</li>
<li>Added c.t.x.converters.extended.AtomicLongConverter.</li>
<li>Added c.t.x.converters.extended.AtomicReferenceConverter.</li>
</ul>

<h1 id="1.4.19">1.4.19</h1>
Expand Down
11 changes: 11 additions & 0 deletions xstream-distribution/src/content/converters.html
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,17 @@
<td>Available with Java 1.5 or greater.</td>
<td>normal</td>
</tr>
<tr>
<td><a href="javadoc/com/thoughtworks/xstream/converters/extended/AtomicReferenceConverter.html">AtomicReferenceConverter</a></td>
<td>java.util.concurrent.atomic.AtomicReference</td>
<td class="example">
&lt;atomic-reference&gt;<br/>
&nbsp;&nbsp;&lt;value&nbsp;class=&quot;string&quot;&gt;test&lt;/value&gt;<br/>
&lt;/atomic-reference&gt;
</td>
<td>Available with Java 1.5 or greater.</td>
<td>normal</td>
</tr>

<!-- .................................................................................................. -->
<tr>
Expand Down
12 changes: 8 additions & 4 deletions xstream/src/java/com/thoughtworks/xstream/XStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;

import com.thoughtworks.xstream.converters.ConversionException;
Expand Down Expand Up @@ -147,6 +148,7 @@
import com.thoughtworks.xstream.converters.extended.AtomicBooleanConverter;
import com.thoughtworks.xstream.converters.extended.AtomicIntegerConverter;
import com.thoughtworks.xstream.converters.extended.AtomicLongConverter;
import com.thoughtworks.xstream.converters.extended.AtomicReferenceConverter;
import com.thoughtworks.xstream.converters.extended.CharsetConverter;
import com.thoughtworks.xstream.converters.extended.ColorConverter;
import com.thoughtworks.xstream.converters.extended.CurrencyConverter;
Expand Down Expand Up @@ -722,10 +724,10 @@ protected void setupSecurity() {
allowTypeHierarchy(Path.class);

final Set<Class<?>> types = new HashSet<>();
types.addAll(Arrays.<Class<?>>asList(AtomicBoolean.class, AtomicInteger.class, AtomicLong.class, BitSet.class, Charset.class,
Class.class, Currency.class, Date.class, DecimalFormatSymbols.class, File.class, Locale.class, Object.class,
Pattern.class, StackTraceElement.class, String.class, StringBuffer.class, StringBuilder.class, URL.class,
URI.class, UUID.class));
types.addAll(Arrays.<Class<?>>asList(AtomicBoolean.class, AtomicInteger.class, AtomicLong.class,
AtomicReference.class, BitSet.class, Charset.class, Class.class, Currency.class, Date.class,
DecimalFormatSymbols.class, File.class, Locale.class, Object.class, Pattern.class, StackTraceElement.class,
String.class, StringBuffer.class, StringBuilder.class, URL.class, URI.class, UUID.class));
if (JVM.isSQLAvailable()) {
types.add(JVM.loadClassForName("java.sql.Timestamp"));
types.add(JVM.loadClassForName("java.sql.Time"));
Expand Down Expand Up @@ -833,6 +835,7 @@ protected void setupAliases() {
alias("atomic-boolean", AtomicBoolean.class);
alias("atomic-int", AtomicInteger.class);
alias("atomic-long", AtomicLong.class);
alias("atomic-reference", AtomicReference.class);

alias("enum-set", EnumSet.class);
alias("enum-map", EnumMap.class);
Expand Down Expand Up @@ -967,6 +970,7 @@ protected void setupConverters() {
registerConverter((Converter)new AtomicBooleanConverter(), PRIORITY_NORMAL);
registerConverter((Converter)new AtomicIntegerConverter(), PRIORITY_NORMAL);
registerConverter((Converter)new AtomicLongConverter(), PRIORITY_NORMAL);
registerConverter(new AtomicReferenceConverter(mapper), PRIORITY_NORMAL);

registerConverter(new ArrayConverter(mapper), PRIORITY_NORMAL);
registerConverter(new CharArrayConverter(), PRIORITY_NORMAL);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2022 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 29. November 2022 by Joerg Schaible
*/
package com.thoughtworks.xstream.converters.extended;

import java.util.concurrent.atomic.AtomicReference;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.core.util.HierarchicalStreams;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;


/**
* Converts an AtomicReference type.
*
* @since upcoming
*/
public class AtomicReferenceConverter implements Converter {

private final Mapper mapper;

public AtomicReferenceConverter(final Mapper mapper) {
this.mapper = mapper;
}

@Override
public boolean canConvert(final Class<?> type) {
return type != null && type == AtomicReference.class;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final AtomicReference<?> ref = (AtomicReference<?>)source;
writer.startNode(mapper.serializedMember(AtomicReference.class, "value"));

final Object object = ref.get();
final String name = mapper.serializedClass(object != null ? object.getClass() : null);
writer.addAttribute(mapper.aliasForSystemAttribute("class"), name);
context.convertAnother(ref.get());
writer.endNode();
}

@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
reader.moveDown();

final Class<?> type = HierarchicalStreams.readClassType(reader, mapper);
final Object value = context.convertAnother(context, type);
reader.moveUp();
return new AtomicReference<>(value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import com.thoughtworks.acceptance.objects.Original;
import com.thoughtworks.acceptance.objects.Replaced;
import com.thoughtworks.xstream.converters.collections.MapConverter;


Expand Down Expand Up @@ -94,4 +97,41 @@ public void testAtomicLongWithOldFormat() {
+ " <value>42</value>\n" //
+ "</java.util.concurrent.atomic.AtomicLong>").toString());
}

public void testAtomicReference() {
final AtomicReference<String> atomicRef = new AtomicReference<>("test");
assertBothWays(atomicRef, ("" //
+ "<atomic-reference>\n" //
+ " <value class='string'>test</value>\n" //
+ "</atomic-reference>").replace('\'', '"'));
}

@SuppressWarnings("unchecked")
public void testAtomicReferenceWithOldFormat() {
assertEquals(new AtomicReference<String>("test").get(), ((AtomicReference<String>)xstream.fromXML("" //
+ "<java.util.concurrent.atomic.AtomicReference>\n" //
+ " <value class='string'>test</value>\n" //
+ "</java.util.concurrent.atomic.AtomicReference>")).get());
}

public void testAtomicReferenceWithAlias() {
xstream.aliasField("junit", AtomicReference.class, "value");
final AtomicReference<String> atomicRef = new AtomicReference<>("test");
assertBothWays(atomicRef, ("" //
+ "<atomic-reference>\n" //
+ " <junit class='string'>test</junit>\n" //
+ "</atomic-reference>").replace('\'', '"'));
}

public void testAtomicReferenceWithReplaced() {
xstream.alias("original", Original.class);
xstream.alias("replaced", Replaced.class);
final AtomicReference<Original> atomicRef = new AtomicReference<>(new Original("test"));
assertBothWays(atomicRef, ("" //
+ "<atomic-reference>\n" //
+ " <value class='original' resolves-to='replaced'>\n"
+ " <replacedValue>TEST</replacedValue>\n"
+ " </value>\n" //
+ "</atomic-reference>").replace('\'', '"'));
}
}

0 comments on commit b03bec5

Please sign in to comment.