/projects/OG-Util/src/com/opengamma/util/fudgemsg/DirectBeanFudgeBuilder.java

https://github.com/gsteri1/OG-Platform · Java · 244 lines · 199 code · 15 blank · 30 comment · 73 complexity · 3e00d4360df1418463c423bf94a38c63 MD5 · raw file

  1. /**
  2. * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
  3. *
  4. * Please see distribution for license.
  5. */
  6. package com.opengamma.util.fudgemsg;
  7. import java.util.ArrayList;
  8. import java.util.HashMap;
  9. import java.util.LinkedHashSet;
  10. import java.util.LinkedList;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Queue;
  14. import java.util.Set;
  15. import org.fudgemsg.FudgeField;
  16. import org.fudgemsg.FudgeMsg;
  17. import org.fudgemsg.FudgeRuntimeException;
  18. import org.fudgemsg.MutableFudgeMsg;
  19. import org.fudgemsg.mapping.FudgeBuilder;
  20. import org.fudgemsg.mapping.FudgeDeserializer;
  21. import org.fudgemsg.mapping.FudgeSerializer;
  22. import org.fudgemsg.types.IndicatorType;
  23. import org.fudgemsg.wire.types.FudgeWireType;
  24. import org.joda.beans.Bean;
  25. import org.joda.beans.BeanBuilder;
  26. import org.joda.beans.JodaBeanUtils;
  27. import org.joda.beans.MetaBean;
  28. import org.joda.beans.MetaProperty;
  29. /**
  30. * Builder to convert DirectBean to and from Fudge.
  31. *
  32. * @param <T> the bean type
  33. */
  34. public final class DirectBeanFudgeBuilder<T extends Bean> implements FudgeBuilder<T> {
  35. /**
  36. * The meta bean for this instance.
  37. */
  38. private final MetaBean _metaBean;
  39. /**
  40. * Creates a builder from a class, using reflection to find the meta-bean.
  41. * @param <R> the bean type
  42. * @param cls the class to get the builder for, not null
  43. * @return the bean builder, not null
  44. */
  45. public static <R extends Bean> DirectBeanFudgeBuilder<R> of(final Class<R> cls) {
  46. MetaBean meta;
  47. try {
  48. meta = (MetaBean) cls.getMethod("meta").invoke(null);
  49. } catch (RuntimeException ex) {
  50. throw ex;
  51. } catch (Exception ex) {
  52. throw new RuntimeException(ex);
  53. }
  54. return new DirectBeanFudgeBuilder<R>(meta);
  55. }
  56. /**
  57. * Constructor.
  58. * @param metaBean the meta-bean, not null
  59. */
  60. public DirectBeanFudgeBuilder(MetaBean metaBean) {
  61. _metaBean = metaBean;
  62. }
  63. //-------------------------------------------------------------------------
  64. // TODO: FudgeFieldName and Ordinal annotations
  65. @Override
  66. public MutableFudgeMsg buildMessage(FudgeSerializer serializer, T bean) {
  67. try {
  68. MutableFudgeMsg msg = serializer.newMessage();
  69. for (MetaProperty<Object> prop : bean.metaBean().metaPropertyIterable()) {
  70. if (prop.readWrite().isReadable()) {
  71. Object obj = prop.get(bean);
  72. if (obj instanceof List<?>) {
  73. MutableFudgeMsg subMsg = buildMessageCollection(serializer, prop, bean.getClass(), (List<?>) obj);
  74. msg.add(prop.name(), null, FudgeWireType.SUB_MESSAGE, subMsg);
  75. } else if (obj instanceof Set<?>) {
  76. MutableFudgeMsg subMsg = buildMessageCollection(serializer, prop, bean.getClass(), new ArrayList<Object>((Set<?>) obj));
  77. msg.add(prop.name(), null, FudgeWireType.SUB_MESSAGE, subMsg);
  78. } else if (obj instanceof Map<?, ?>) {
  79. MutableFudgeMsg subMsg = buildMessageMap(serializer, bean.getClass(), prop, (Map<?, ?>) obj);
  80. msg.add(prop.name(), null, FudgeWireType.SUB_MESSAGE, subMsg);
  81. } else {
  82. serializer.addToMessageWithClassHeaders(msg, prop.name(), null, obj, prop.propertyType()); // ignores null
  83. }
  84. }
  85. }
  86. return msg;
  87. } catch (RuntimeException ex) {
  88. throw new FudgeRuntimeException("Unable to serialize: " + _metaBean.beanName(), ex);
  89. }
  90. }
  91. private MutableFudgeMsg buildMessageCollection(FudgeSerializer serializer, MetaProperty<Object> prop, Class<?> beanType, List<?> list) {
  92. Class<?> contentType = JodaBeanUtils.collectionType(prop, beanType);
  93. MutableFudgeMsg msg = serializer.newMessage();
  94. for (Object entry : list) {
  95. if (entry == null) {
  96. msg.add(null, null, FudgeWireType.INDICATOR, IndicatorType.INSTANCE);
  97. } else if (contentType == null) {
  98. serializer.addToMessage(msg, null, null, entry);
  99. } else {
  100. serializer.addToMessageWithClassHeaders(msg, null, null, entry, contentType);
  101. }
  102. }
  103. return msg;
  104. }
  105. private MutableFudgeMsg buildMessageMap(FudgeSerializer serializer, Class<?> beanType, MetaProperty<Object> prop, Map<?, ?> map) {
  106. Class<?> keyType = JodaBeanUtils.mapKeyType(prop, beanType);
  107. Class<?> valueType = JodaBeanUtils.mapValueType(prop, beanType);
  108. MutableFudgeMsg msg = serializer.newMessage();
  109. for (Map.Entry<?, ?> entry : map.entrySet()) {
  110. if (entry.getKey() == null) {
  111. msg.add(null, 1, FudgeWireType.INDICATOR, IndicatorType.INSTANCE);
  112. } else if (keyType == null) {
  113. serializer.addToMessage(msg, null, 1, entry.getKey());
  114. } else {
  115. serializer.addToMessageWithClassHeaders(msg, null, 1, entry.getKey(), keyType);
  116. }
  117. if (entry.getValue() == null) {
  118. msg.add(null, 2, FudgeWireType.INDICATOR, IndicatorType.INSTANCE);
  119. } else if (valueType == null) {
  120. serializer.addToMessage(msg, null, 2, entry.getValue());
  121. } else {
  122. serializer.addToMessageWithClassHeaders(msg, null, 2, entry.getValue(), valueType);
  123. }
  124. }
  125. return msg;
  126. }
  127. //-------------------------------------------------------------------------
  128. @SuppressWarnings("unchecked")
  129. @Override
  130. public T buildObject(FudgeDeserializer deserializer, FudgeMsg msg) {
  131. try {
  132. BeanBuilder<T> builder = (BeanBuilder<T>) _metaBean.builder();
  133. for (MetaProperty<Object> mp : _metaBean.metaPropertyIterable()) {
  134. if (mp.readWrite().isWritable()) {
  135. final FudgeField field = msg.getByName(mp.name());
  136. if (field != null) {
  137. Object value = null;
  138. if (List.class.isAssignableFrom(mp.propertyType())) {
  139. value = field.getValue();
  140. if (value instanceof FudgeMsg) {
  141. value = buildObjectList(deserializer, mp, _metaBean.beanType(), (FudgeMsg) value);
  142. }
  143. } else if (Set.class.isAssignableFrom(mp.propertyType())) {
  144. value = field.getValue();
  145. if (value instanceof FudgeMsg) {
  146. value = buildObjectSet(deserializer, mp, _metaBean.beanType(), (FudgeMsg) value);
  147. }
  148. } else if (Map.class.isAssignableFrom(mp.propertyType())) {
  149. value = field.getValue();
  150. if (value instanceof FudgeMsg) {
  151. value = buildObjectMap(deserializer, mp, _metaBean.beanType(), (FudgeMsg) value);
  152. }
  153. }
  154. if (value == null) {
  155. value = deserializer.fieldValueToObject(mp.propertyType(), field);
  156. }
  157. if (value != null || mp.propertyType().isPrimitive() == false) {
  158. builder.set(mp.name(), value);
  159. }
  160. }
  161. }
  162. }
  163. return builder.build();
  164. } catch (RuntimeException ex) {
  165. throw new FudgeRuntimeException("Unable to deserialize: " + _metaBean.beanName(), ex);
  166. }
  167. }
  168. private List<Object> buildObjectList(FudgeDeserializer deserializer, MetaProperty<Object> prop, Class<?> type, FudgeMsg msg) {
  169. Class<?> contentType = JodaBeanUtils.collectionType(prop, type);
  170. List<Object> list = new ArrayList<Object>(); // should be List<contentType>
  171. for (FudgeField field : msg) {
  172. if (field.getOrdinal() != null && field.getOrdinal() != 1) {
  173. throw new IllegalArgumentException("Sub-message doesn't contain a list (bad field " + field + ")");
  174. }
  175. Object obj = (contentType == null ? deserializer.fieldValueToObject(field) : deserializer.fieldValueToObject(contentType, field));
  176. list.add((obj instanceof IndicatorType) ? null : obj);
  177. }
  178. return list;
  179. }
  180. private Set<Object> buildObjectSet(FudgeDeserializer deserializer, MetaProperty<Object> prop, Class<?> type, FudgeMsg msg) {
  181. Class<?> contentType = JodaBeanUtils.collectionType(prop, type);
  182. Set<Object> set = new LinkedHashSet<Object>(); // should be Set<contentType>
  183. for (FudgeField field : msg) {
  184. if (field.getOrdinal() != null && field.getOrdinal() != 1) {
  185. throw new IllegalArgumentException("Sub-message doesn't contain a set (bad field " + field + ")");
  186. }
  187. Object obj = (contentType == null ? deserializer.fieldValueToObject(field) : deserializer.fieldValueToObject(contentType, field));
  188. set.add((obj instanceof IndicatorType) ? null : obj);
  189. }
  190. return set;
  191. }
  192. private Map<Object, Object> buildObjectMap(FudgeDeserializer deserializer, MetaProperty<Object> prop, Class<?> type, FudgeMsg msg) {
  193. Class<?> keyType = JodaBeanUtils.mapKeyType(prop, type);
  194. Class<?> valueType = JodaBeanUtils.mapValueType(prop, type);
  195. Map<Object, Object> map = new HashMap<Object, Object>(); // should be Map<keyType,contentType>
  196. Queue<Object> keys = new LinkedList<Object>();
  197. Queue<Object> values = new LinkedList<Object>();
  198. for (FudgeField field : msg) {
  199. if (field.getOrdinal() == 1) {
  200. Object fieldValue = (keyType == null ? deserializer.fieldValueToObject(field) : deserializer.fieldValueToObject(keyType, field));
  201. if (fieldValue instanceof IndicatorType) {
  202. fieldValue = null;
  203. }
  204. if (values.isEmpty()) {
  205. // no values ready, so store the key till next time
  206. keys.add(fieldValue);
  207. } else {
  208. // store key along with next value
  209. map.put(fieldValue, values.remove());
  210. }
  211. } else if (field.getOrdinal() == 2) {
  212. Object fieldValue = (valueType == null ? deserializer.fieldValueToObject(field) : deserializer.fieldValueToObject(valueType, field));
  213. if (fieldValue instanceof IndicatorType) {
  214. fieldValue = null;
  215. }
  216. if (keys.isEmpty()) {
  217. // no keys ready, so store the value till next time
  218. values.add(fieldValue);
  219. } else {
  220. // store value along with next key
  221. map.put(keys.remove(), fieldValue);
  222. }
  223. } else {
  224. throw new IllegalArgumentException("Sub-message doesn't contain a map (bad field " + field + ")");
  225. }
  226. }
  227. return map;
  228. }
  229. }