Package com.opengamma.util.fudgemsg

Source Code of com.opengamma.util.fudgemsg.InnerClassFudgeBuilder

/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.fudgemsg;

import static com.google.common.collect.Lists.newArrayList;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeRuntimeException;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeBuilder;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import org.fudgemsg.wire.types.FudgeWireType;

import com.opengamma.OpenGammaRuntimeException;

/**
* Builder to convert inner classes to and from Fudge.
*
* @param <T> the bean type
* @param <K> the type
*/
public final class InnerClassFudgeBuilder<T extends AutoFudgable<K>, K> implements FudgeBuilder<T> {

  /**
   * Creates a builder for inner class
   */
  public InnerClassFudgeBuilder() {}

  @Override
  public MutableFudgeMsg buildMessage(FudgeSerializer serializer, final T auto) {
    MutableFudgeMsg outerMsg = serializer.newMessage();
    outerMsg.add(null, FudgeSerializer.TYPES_HEADER_ORDINAL, FudgeWireType.STRING, AutoFudgable.class.getName());
    final K inner = auto.object();
    assertValid(inner.getClass());
    try {
      MutableFudgeMsg msg = outerMsg.addSubMessage("inner", null);
      //save the internal class name
      msg.add(null, FudgeSerializer.TYPES_HEADER_ORDINAL, FudgeWireType.STRING, inner.getClass().getName());

      //save the ctor parameters
      List<Object> parameters = AccessController.doPrivileged(new PrivilegedAction<List<Object>>() {
        @Override
        public List<Object> run() {
          try {
            final Constructor<?>[] ctors = inner.getClass().getDeclaredConstructors();
            if (ctors.length != 1) {
              throw new IllegalArgumentException("Inner class does not have a single constructor: " + inner.getClass());
            }
            // find all declared parameters of the inner class
            final List<Field> fs = new ArrayList<Field>(Arrays.asList(inner.getClass().getDeclaredFields()));
            // remove the field representing the parent object from the list
            // only want the compiler synthesized fields which corresponds to ctor parameters
            for (Iterator<Field> it = fs.iterator(); it.hasNext(); ) {
              Field field = it.next();
              if (field.getType().isAssignableFrom(inner.getClass().getEnclosingClass()) || "$jacocoData".equals(field.getName())) {
                it.remove();
              }
            }
            final List<Object> parameters = newArrayList();
            for (Field paramField : fs) {
              paramField.setAccessible(true);
              parameters.add(paramField.get(inner));
            }
            return parameters;
           
          } catch (IllegalAccessException ex) {
            throw new OpenGammaRuntimeException(ex.getMessage());
          }
        }
      });
     
      for (Object parameter : parameters) {
        //save the ctor parameter                 
        serializer.addToMessageWithClassHeaders(msg, null, 1, parameter);
      }
      return outerMsg;
     
    } catch (RuntimeException ex) {
      throw new FudgeRuntimeException("Unable to serialize: " + inner.getClass().getName(), ex);
    }
  }

  //-------------------------------------------------------------------------
  @SuppressWarnings("unchecked")
  @Override
  public T buildObject(FudgeDeserializer deserializer, FudgeMsg outerMsg) {
    FudgeField fudgeField = outerMsg.getByName("inner");
    FudgeMsg msg = (FudgeMsg) fudgeField.getValue();

    final FudgeField classNameField = msg.getByOrdinal(FudgeSerializer.TYPES_HEADER_ORDINAL);
    final String className = (String) classNameField.getValue();
    try {
      final List<Object> parameters = newArrayList();
      parameters.add(null)//the omitted enclosing object
      for (FudgeField parameterField : msg.getAllByOrdinal(1)) {
        parameters.add(deserializer.fieldValueToObject(parameterField));
      }
     
      return (T) AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
          final Object[] array = parameters.toArray();
          // find constructor
          Constructor<?> ctor = null;
          try {
            final Class<?> innerClass = Class.forName(className);
            final Constructor<?>[] ctors = innerClass.getDeclaredConstructors();
            if (ctors.length != 1) {
              throw new IllegalArgumentException("Inner class does not have a single constructor: " + className);
            }
            ctor = ctors[0];
            ctor.setAccessible(true);   // solution
          } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
          }
         
          // invoke constructor
          try {
            Object inner = ctor.newInstance(array);
            return new AutoFudgable<Object>(inner);
           
          } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
          } catch (InstantiationException ex) {
            throw new RuntimeException(ex);
          } catch (InvocationTargetException ex) {
            throw new RuntimeException(ex);
          }
        }
      });

    } catch (RuntimeException ex) {
      throw new FudgeRuntimeException("Unable to deserialize: " + className, ex);
    }
  }

  private void assertValid(final Class<?> clazz) {
    if (clazz.getEnclosingClass() == null) {
      throw new OpenGammaRuntimeException("AutoFudgable can be used only with inner classes");
    }
    if (clazz.getSuperclass().getEnclosingClass() != null) {
      throw new OpenGammaRuntimeException("AutoFudgable can be used only with inner classes which enclosing classes are not inner themselves.");
    }
    if (!hasSingleZeroArgConstructor(clazz.getSuperclass())) {
      throw new OpenGammaRuntimeException("AutoFudgable can be used only with inner classes which enclosing classes have single, zero argument constructor.");
    }
  }

  private static boolean hasSingleZeroArgConstructor(final Class<?> clazz) {
    return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
      @Override
      public Boolean run() {
        Constructor<?>[] ctors = clazz.getDeclaredConstructors();
        return ctors.length == 1 && ctors[0].getParameterTypes().length == 0;
      }
    });
  }

}
TOP

Related Classes of com.opengamma.util.fudgemsg.InnerClassFudgeBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.