diff --git a/src/main/java/net/openhft/xstreem/convertors/DataValueConverter.java b/src/main/java/net/openhft/xstreem/convertors/DataValueConverter.java index 4f71ceecc..c743b7900 100644 --- a/src/main/java/net/openhft/xstreem/convertors/DataValueConverter.java +++ b/src/main/java/net/openhft/xstreem/convertors/DataValueConverter.java @@ -13,7 +13,9 @@ import org.slf4j.LoggerFactory; import java.beans.*; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Iterator; import static java.beans.Introspector.getBeanInfo; @@ -69,9 +71,8 @@ public class DataValueConverter implements Converter { if (p instanceof IndexedPropertyDescriptor) { try { - IndexedPropertyDescriptor indexedPropertyDescriptor = (IndexedPropertyDescriptor) p; - - Method indexedReadMethod = indexedPropertyDescriptor.getIndexedReadMethod(); + final IndexedPropertyDescriptor indexedPropertyDescriptor = (IndexedPropertyDescriptor) p; + final Method indexedReadMethod = indexedPropertyDescriptor.getIndexedReadMethod(); if (indexedReadMethod == null) continue; @@ -82,29 +83,34 @@ public class DataValueConverter implements Converter { continue; } - String simpleName = returnType.getSimpleName(); - - String fieldName = "_" + toCamelCase(simpleName); + final String simpleName = returnType.getSimpleName(); + final String fieldName = "_" + toCamelCase(simpleName); Field field = o.getClass().getDeclaredField(fieldName); field.setAccessible(true); - Object[] o1 = (Object[]) field.get(o); + final Object[] o1 = (Object[]) field.get(o); + if (o1 == null) continue; + writer.startNode(p.getName()); int i = 0; for (Object f : o1) { + if (f == null) { + continue; + } writer.startNode(Integer.toString(i++)); context.convertAnother(f); writer.endNode(); } + writer.endNode(); + } catch (NoSuchFieldException | IllegalAccessException e1) { LOG.error("", e1); } } - if (p.getName().equals("Class") || p.getReadMethod() == null || p.getWriteMethod() == null) @@ -151,7 +157,9 @@ public class DataValueConverter implements Converter { @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public Object unmarshal(HierarchicalStreamReader reader, + UnmarshallingContext context) { + final String canonicalName = context.getRequiredType().getCanonicalName(); boolean isNative = canonicalName.endsWith("$$Native"); @@ -160,7 +168,7 @@ public class DataValueConverter implements Converter { if (!isNative && !isHeap) return null; - if (context.getRequiredType().getCanonicalName().startsWith + if (context.getRequiredType().getName().startsWith ("net.openhft.lang.values")) return toNativeValueObjects(reader, context.getRequiredType(), context); @@ -180,37 +188,7 @@ public class DataValueConverter implements Converter { DataValueClasses.newDirectInstance(interfaceClass) : DataValueClasses.newInstance(interfaceClass); - final BeanInfo beanInfo = getBeanInfo(result.getClass()); - - while (reader.hasMoreChildren()) { - reader.moveDown(); - - final String name = reader.getNodeName(); - - for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) { - - if (!descriptor.getName().equals(name)) - continue; - - final Class[] parameterTypes = descriptor.getWriteMethod() - .getParameterTypes(); - - if (parameterTypes.length != 1) - continue; - - final Object object = context.convertAnother(null, parameterTypes[0]); - - try { - descriptor.getWriteMethod().invoke(result, object); - } catch (Exception e) { - LOG.error("", e); - } - - break; - } - - reader.moveUp(); - } + fillInObject(reader, context, result); return result; @@ -218,9 +196,114 @@ public class DataValueConverter implements Converter { throw new ConversionException("class=" + canonicalName, e); } - } + private void fillInObject(HierarchicalStreamReader reader, UnmarshallingContext context, Object using) throws IntrospectionException { + final BeanInfo beanInfo = getBeanInfo(using.getClass()); + + while (reader.hasMoreChildren()) { + reader.moveDown(); + + final String name = reader.getNodeName(); + + for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) { + + if (!descriptor.getName().equals(name)) + continue; + + if ((descriptor instanceof IndexedPropertyDescriptor)) { + IndexedPropertyDescriptor indexedPropertyDescriptor = (IndexedPropertyDescriptor) descriptor; + while (reader.hasMoreChildren()) { + + reader.moveDown(); + try { + String index = reader.getNodeName(); + int i = Integer.parseInt(index); + Method writeMethod = indexedPropertyDescriptor.getIndexedWriteMethod(); + Class[] parameterTypes = writeMethod.getParameterTypes(); + + if (parameterTypes.length == 2) { + final Method indexedReadMethod = indexedPropertyDescriptor.getIndexedReadMethod(); + final Object instance = indexedReadMethod.invoke(using, i); + fillInObject(reader, context, instance); + } + } catch (IllegalAccessException | InvocationTargetException e) { + LOG.error("", e); + } + + reader.moveUp(); + } + continue; + } + + final Class[] parameterTypes = descriptor.getWriteMethod().getParameterTypes(); + + if (parameterTypes.length != 1) + continue; + + final Object object = context.convertAnother(null, parameterTypes[0]); + + try { + descriptor.getWriteMethod().invoke(using, object); + } catch (Exception e) { + LOG.error("", e); + } + + break; + } + + reader.moveUp(); + } + } + + + void convertUsing(final UnmarshallingContext context, final Object using, HierarchicalStreamReader reader) throws IllegalAccessException, IntrospectionException, InvocationTargetException { + UnmarshallingContext context1 = new UnmarshallingContext() { + + @Override + public Object get(Object o) { + return context.get(o); + } + + @Override + public void put(Object o, Object o1) { + context.put(o, o1); + } + + @Override + public Iterator keys() { + return context.keys(); + } + + @Override + public Object convertAnother(Object o, Class aClass) { + return context.convertAnother(o, aClass); + } + + @Override + public Object convertAnother(Object o, Class aClass, Converter converter) { + return context.convertAnother(o, aClass, converter); + } + + @Override + public Object currentObject() { + return using; + } + + @Override + public Class getRequiredType() { + return using.getClass(); + } + + @Override + public void addCompletionCallback(Runnable runnable, int i) { + context.addCompletionCallback(runnable, i); + } + }; + + fillInObject(reader, context1, using); + + } static Object toNativeValueObjects(HierarchicalStreamReader reader, final Class aClass, diff --git a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java index e11d95d03..62526c0e9 100644 --- a/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java +++ b/src/test/java/net/openhft/chronicle/map/CHMUseCasesTest.java @@ -2,6 +2,7 @@ package net.openhft.chronicle.map; import com.google.common.primitives.Chars; import net.openhft.chronicle.hash.replication.TcpTransportAndNetworkConfig; +import net.openhft.chronicle.map.fromdocs.BondVOInterface; import net.openhft.lang.io.ByteBufferBytes; import net.openhft.lang.io.serialization.impl.*; import net.openhft.lang.model.constraints.MaxSize; @@ -25,6 +26,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static java.util.Arrays.asList; +import static net.openhft.chronicle.map.fromdocs.OpenJDKAndHashMapExamplesTest.parseYYYYMMDD; import static org.junit.Assert.*; /** @@ -321,6 +323,35 @@ public class CHMUseCasesTest { } + @Test + public void bondExample() throws IOException, InterruptedException { + + if (typeOfMap == TypeOfMap.STATELESS) + return; // acquireUsingLocked not supported by the STATELESS client + + ChronicleMapBuilder builder = ChronicleMapBuilder.of(String.class, BondVOInterface.class) + .keySize(10); + + try (ChronicleMap chm = newInstance(builder)) { + BondVOInterface bondVO = chm.newValueInstance(); + try (WriteContext wc = chm.acquireUsingLocked("369604103", bondVO)) { + bondVO.setIssueDate(parseYYYYMMDD("20130915")); + bondVO.setMaturityDate(parseYYYYMMDD("20140915")); + bondVO.setCoupon(5.0 / 100); // 5.0% + + BondVOInterface.MarketPx mpx930 = bondVO.getMarketPxIntraDayHistoryAt(0); + mpx930.setAskPx(109.2); + mpx930.setBidPx(106.9); + + BondVOInterface.MarketPx mpx1030 = bondVO.getMarketPxIntraDayHistoryAt(1); + mpx1030.setAskPx(109.7); + mpx1030.setBidPx(107.6); + } + + } + + } + @Test public void testLargeCharSequenceValueWriteOnly() throws ExecutionException, InterruptedException, IOException { @@ -2452,7 +2483,6 @@ public class CHMUseCasesTest { return ret; } - @Ignore("HCOLL-271 JSON<->Map fails for data generated inner classes") @Test public void testGeneratedDataValue() throws IOException { diff --git a/src/test/java/net/openhft/chronicle/map/fromdocs/OpenJDKAndHashMapExamplesTest.java b/src/test/java/net/openhft/chronicle/map/fromdocs/OpenJDKAndHashMapExamplesTest.java index 13cd3dc0f..ed0dbcf24 100644 --- a/src/test/java/net/openhft/chronicle/map/fromdocs/OpenJDKAndHashMapExamplesTest.java +++ b/src/test/java/net/openhft/chronicle/map/fromdocs/OpenJDKAndHashMapExamplesTest.java @@ -22,7 +22,6 @@ import net.openhft.chronicle.map.ChronicleMap; import net.openhft.chronicle.map.ChronicleMapBuilder; import net.openhft.chronicle.map.ReadContext; import net.openhft.chronicle.map.WriteContext; -import net.openhft.lang.model.DataValueClasses; import org.junit.Test; import java.io.File; @@ -41,7 +40,7 @@ public class OpenJDKAndHashMapExamplesTest { private static final String TMP = System.getProperty("java.io.tmpdir"); - private static long parseYYYYMMDD(String s) { + public static long parseYYYYMMDD(String s) { try { return YYYYMMDD.parse(s).getTime(); } catch (ParseException e) {