HCOLL-259 fixed JSON<->CHM to support $$Native like BondVOInterface

This commit is contained in:
Rob Austin
2014-12-21 17:28:35 +00:00
parent 59add5e42f
commit b2f6ebbba0
3 changed files with 172 additions and 39 deletions
@@ -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();