mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
HCOLL-259 fixed JSON<->CHM to support $$Native like BondVOInterface
This commit is contained in:
@@ -47,6 +47,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -58,6 +62,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static java.beans.Introspector.getBeanInfo;
|
||||
import static java.lang.Class.forName;
|
||||
import static java.lang.Long.numberOfTrailingZeros;
|
||||
import static java.lang.Math.max;
|
||||
@@ -771,19 +776,17 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
|
||||
String canonicalName = aClass.getCanonicalName();
|
||||
|
||||
if (canonicalName.startsWith("net.openhft.lang.values")) {
|
||||
return true;
|
||||
}
|
||||
return canonicalName.startsWith("net.openhft.lang.values")
|
||||
|| canonicalName.endsWith("$$Native");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext
|
||||
marshallingContext) {
|
||||
if (WriteThroughEntry.class.isAssignableFrom(o.getClass())) {
|
||||
final SimpleEntry e = (SimpleEntry) o;
|
||||
|
||||
final SimpleEntry e = (SimpleEntry) o;
|
||||
writer.startNode("chronicle-key");
|
||||
|
||||
Object key = e.getKey();
|
||||
@@ -797,6 +800,7 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
writeType(writer, value, vClass);
|
||||
marshallingContext.convertAnother(value);
|
||||
writer.endNode();
|
||||
|
||||
} else if (EntrySet.class
|
||||
.isAssignableFrom(o.getClass())) {
|
||||
for (Entry e : (EntrySet) o) {
|
||||
@@ -805,29 +809,114 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
writer.endNode();
|
||||
}
|
||||
}
|
||||
|
||||
if (o.getClass().getCanonicalName().startsWith("net.openhft.lang.values")
|
||||
&& (o.getClass().getCanonicalName().endsWith("$$Native") ||
|
||||
o.getClass().getCanonicalName().endsWith("$$Heap"))) {
|
||||
|
||||
Method[] methods = o.getClass().getMethods();
|
||||
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals("getValue") && method.getParameterTypes()
|
||||
.length == 0) {
|
||||
if (method.getName().equals("getValue") &&
|
||||
method.getParameterTypes().length == 0) {
|
||||
try {
|
||||
Object result = method.invoke(o);
|
||||
writer.setValue(result.toString());
|
||||
final Object result = method.invoke(o);
|
||||
// writer.startNode(o.getClass().getCanonicalName());
|
||||
marshallingContext.convertAnother(result);
|
||||
// writer.endNode();
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("class=" + o.getClass().getCanonicalName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (o.getClass().getCanonicalName().endsWith("$$Native")) {
|
||||
|
||||
try {
|
||||
BeanInfo info = getBeanInfo(o.getClass()); // new BeanGenerator
|
||||
|
||||
for (PropertyDescriptor p : info.getPropertyDescriptors()) {
|
||||
|
||||
if (p.getName().equals("Class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p.getReadMethod() != null && p.getWriteMethod() != null) {
|
||||
|
||||
try {
|
||||
|
||||
final Method readMethod = p.getReadMethod();
|
||||
|
||||
Object value = readMethod.invoke(o);
|
||||
if (value != null) {
|
||||
writer.startNode(p.getDisplayName());
|
||||
marshallingContext.convertAnother(value);
|
||||
writer.endNode();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("class=" + p.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
throw new RuntimeException("class=" + o.getClass().getCanonicalName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unmarshal(HierarchicalStreamReader reader,
|
||||
UnmarshallingContext unmarshallingContext) {
|
||||
|
||||
String canonicalName = unmarshallingContext.getRequiredType().getCanonicalName();
|
||||
if (canonicalName.endsWith("$$Native")) {
|
||||
|
||||
final String nodeName = canonicalName.substring(0, canonicalName.length() -
|
||||
"$$Native".length());
|
||||
Object o;
|
||||
|
||||
try {
|
||||
o = DataValueClasses.newDirectInstance(Class.forName(nodeName));
|
||||
|
||||
final BeanInfo beanInfo = getBeanInfo(o.getClass());
|
||||
|
||||
while (reader.hasMoreChildren()) {
|
||||
reader.moveDown();
|
||||
String name = reader.getNodeName();
|
||||
|
||||
for (PropertyDescriptor methodDescriptor : beanInfo.getPropertyDescriptors()) {
|
||||
if (methodDescriptor.getName().equals(name)) {
|
||||
Class<?>[] parameterTypes = methodDescriptor.getWriteMethod().getParameterTypes();
|
||||
|
||||
if (parameterTypes.length == 1) {
|
||||
Object o1 = unmarshallingContext.convertAnother(null, parameterTypes[0]);
|
||||
try {
|
||||
methodDescriptor.getWriteMethod().invoke(o, o1);
|
||||
} catch (Exception e) {
|
||||
LOG.error("", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
reader.moveUp();
|
||||
}
|
||||
return o;
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException("class=" + canonicalName, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
final String nodeName = reader.getNodeName();
|
||||
String type = reader.getAttribute("type");
|
||||
switch (nodeName) {
|
||||
case "chronicle-entries":
|
||||
|
||||
@@ -859,11 +948,14 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
|
||||
reader.moveUp();
|
||||
}
|
||||
break;
|
||||
|
||||
return null;
|
||||
case "chronicle-key":
|
||||
|
||||
if (kClass.getCanonicalName().startsWith("net.openhft.lang.values")) {
|
||||
return to$$Native(reader, vClass, true, VanillaChronicleMap.this);
|
||||
return to$$Native(reader, vClass, true, VanillaChronicleMap.this, unmarshallingContext);
|
||||
} else if (type.endsWith("$$Native")) {
|
||||
return XStreamHelper.to$$Native2(reader, vClass, false, VanillaChronicleMap.this, unmarshallingContext, type);
|
||||
} else {
|
||||
long keySize = keySizeMarshaller.readSize(buffer);
|
||||
ThreadLocalCopies copies = keyReaderProvider.getCopies(null);
|
||||
@@ -873,7 +965,9 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
case "chronicle-value":
|
||||
|
||||
if (vClass.getCanonicalName().startsWith("net.openhft.lang.values")) {
|
||||
return to$$Native(reader, vClass, false, VanillaChronicleMap.this);
|
||||
return to$$Native(reader, vClass, false, VanillaChronicleMap.this, unmarshallingContext);
|
||||
} else if (type.endsWith("$$Native")) {
|
||||
return XStreamHelper.to$$Native2(reader, vClass, false, VanillaChronicleMap.this, unmarshallingContext, type);
|
||||
} else {
|
||||
long valueSize = valueSizeMarshaller.readSize(buffer);
|
||||
ThreadLocalCopies copies = valueReaderProvider.getCopies(null);
|
||||
@@ -884,7 +978,6 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -916,13 +1009,14 @@ class VanillaChronicleMap<K, KI, MKI extends MetaBytesInterop<K, ? super KI>,
|
||||
return;
|
||||
|
||||
String simpleName = o.getClass().getCanonicalName();
|
||||
if (simpleName.endsWith("$$Native")) {
|
||||
if (simpleName.startsWith("net.openhft.lang.values")) {
|
||||
String nodeName = simpleName.substring(0, simpleName.length() -
|
||||
"$$Native".length());
|
||||
if (!nodeName.equals(clazz.getCanonicalName()))
|
||||
writer.addAttribute("type", nodeName);
|
||||
} else if (!clazz.equals(o.getClass()))
|
||||
} else if (simpleName.endsWith("$$Native") || !clazz.equals(o.getClass())) {
|
||||
writer.addAttribute("type", o.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,27 +17,65 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import com.thoughtworks.xstream.converters.ConversionException;
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
|
||||
import net.openhft.lang.model.DataValueClasses;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by Rob on 18/12/14.
|
||||
*/
|
||||
class XStreamHelper {
|
||||
static Object to$$Native2(HierarchicalStreamReader reader, final Class aClass2, final boolean isKey,
|
||||
ChronicleMap map, UnmarshallingContext unmarshallingContext, String type) {
|
||||
|
||||
static Object to$$Native(HierarchicalStreamReader reader, final Class aClass, final boolean isKey, ChronicleMap map) {
|
||||
|
||||
if (type.endsWith("$$Native")) {
|
||||
|
||||
Class<?> aClass = null;
|
||||
try {
|
||||
aClass = Class.forName(type);
|
||||
} catch (ClassNotFoundException e) {
|
||||
String clazz = type.substring(0, type.length() -
|
||||
"$$Native".length());
|
||||
try {
|
||||
DataValueClasses.newDirectInstance(Class.forName(clazz));
|
||||
} catch (ClassNotFoundException e1) {
|
||||
throw new ConversionException(e1);
|
||||
}
|
||||
try {
|
||||
aClass = Class.forName(type);
|
||||
} catch (ClassNotFoundException e1) {
|
||||
throw new ConversionException(e1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return unmarshallingContext.convertAnother(null, aClass);
|
||||
}
|
||||
|
||||
throw new ConversionException("setValue(..) method not found in class=" + type);
|
||||
}
|
||||
|
||||
static Object to$$Native(HierarchicalStreamReader reader, final Class aClass, final boolean isKey, ChronicleMap map, UnmarshallingContext unmarshallingContext) {
|
||||
|
||||
|
||||
final Object o = isKey ? map.newKeyInstance() : map.newValueInstance();
|
||||
|
||||
try {
|
||||
for (Method $$nativeMethod : o.getClass().getMethods()) {
|
||||
if ($$nativeMethod.getName().equals("setValue") && $$nativeMethod.getParameterTypes()
|
||||
.length == 1) {
|
||||
|
||||
Class<?> parameterType = $$nativeMethod.getParameterTypes()[0];
|
||||
String value = reader.getValue();
|
||||
final BeanInfo info = Introspector.getBeanInfo(o.getClass()); // new BeanGenerator
|
||||
|
||||
for (PropertyDescriptor p : info.getPropertyDescriptors()) {
|
||||
|
||||
if (p.getName().equals("value")) {
|
||||
|
||||
final String value = reader.getValue();
|
||||
Class<?> parameterType = p.getPropertyType();
|
||||
|
||||
if (parameterType.isPrimitive()) {
|
||||
|
||||
@@ -59,17 +97,18 @@ class XStreamHelper {
|
||||
|
||||
|
||||
final Method valueOf = parameterType.getMethod("valueOf", String.class);
|
||||
final Object invoke = valueOf.invoke(null, value);
|
||||
$$nativeMethod.invoke(o, invoke);
|
||||
return o;
|
||||
final Object boxedValue = valueOf.invoke(null, value);
|
||||
|
||||
p.getWriteMethod().invoke(o, boxedValue);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException("class=" + aClass.getCanonicalName(), e);
|
||||
//
|
||||
}
|
||||
|
||||
throw new ConversionException("setValue(..) method not found in class=" + aClass
|
||||
.getCanonicalName
|
||||
());
|
||||
.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig;
|
||||
import net.openhft.chronicle.map.fromdocs.BondVOInterface;
|
||||
import net.openhft.lang.model.DataValueClasses;
|
||||
import net.openhft.lang.values.LongValue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
@@ -186,7 +187,7 @@ public class ChronicleMapImportExportTest {
|
||||
public void testWithLongValue() throws IOException, InterruptedException {
|
||||
|
||||
File file = new File(TMP + "/chronicle-map-" + System.nanoTime() + ".json");
|
||||
file.deleteOnExit();
|
||||
//file.deleteOnExit();
|
||||
|
||||
System.out.println(file.getAbsolutePath());
|
||||
try (ChronicleMap<CharSequence, LongValue> expected = ChronicleMapBuilder.of(CharSequence.class, LongValue
|
||||
@@ -213,11 +214,11 @@ public class ChronicleMapImportExportTest {
|
||||
Assert.assertEquals(expected, actual);
|
||||
}
|
||||
} finally {
|
||||
file.delete();
|
||||
// file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("HCOLL-259 JSON<->CHM to support $$Native like BondVOInterface")
|
||||
|
||||
@Test
|
||||
public void testBondVOInterface() throws IOException, InterruptedException {
|
||||
|
||||
@@ -225,27 +226,26 @@ public class ChronicleMapImportExportTest {
|
||||
file.deleteOnExit();
|
||||
|
||||
System.out.println(file.getAbsolutePath());
|
||||
try (ChronicleMap<CharSequence, BondVOInterface> expected = ChronicleMapBuilder.of(CharSequence.class, BondVOInterface
|
||||
.class)
|
||||
.create()) {
|
||||
BondVOInterface value = expected.newValueInstance();
|
||||
try (ChronicleMap<CharSequence, BondVOInterface> expected =
|
||||
ChronicleMapBuilder.of(CharSequence.class, BondVOInterface.class).create()) {
|
||||
|
||||
final BondVOInterface value = expected.newValueInstance();
|
||||
|
||||
// this will add the entry
|
||||
try (WriteContext context = expected.acquireUsingLocked("one", value)) {
|
||||
value.setCoupon(8.98);
|
||||
assert value == context.value();
|
||||
|
||||
}
|
||||
|
||||
expected.getAll(file);
|
||||
|
||||
try (ChronicleMap<CharSequence, LongValue> actual = ChronicleMapBuilder.of(CharSequence.class, LongValue
|
||||
.class)
|
||||
.create()) {
|
||||
try (ChronicleMap<CharSequence, BondVOInterface> actual =
|
||||
ChronicleMapBuilder.of(CharSequence.class,
|
||||
DataValueClasses.directClassFor(BondVOInterface.class)).create()) {
|
||||
|
||||
actual.putAll(file);
|
||||
|
||||
Assert.assertEquals(expected, actual);
|
||||
Assert.assertEquals(expected.get("one").getCoupon(), actual.get("one").getCoupon(), 0);
|
||||
}
|
||||
} finally {
|
||||
file.delete();
|
||||
|
||||
Reference in New Issue
Block a user