package org.yogurt.protobufftools;

import java.lang.reflect.Field;
import java.util.Set;

public class ReflectiveEncoder implements IMessageEncoder {
    public byte[] encode(Object o) throws Exception {
        Object builder = populateBuilder(new ReflectiveObject(o));
        Object built = builder.getClass().getDeclaredMethod("build").invoke(builder);
        byte[] payload = (byte[]) built.getClass().getMethod("toByteArray").invoke(built);
        return new MessageWrapper().wrap(o.getClass().getCanonicalName(), payload);
    }

    public Object decode(byte[] bytes) throws Exception {
        Message message = new MessageWrapper().unwrap(bytes);
        Object o = Class.forName(message.getMessageType()).newInstance();

        Class<?> bufferClass = getProtoBufferClass(o);

        Object buffer = bufferClass.getMethod("parseFrom", byte[].class).invoke(null, message.getPayload());
        return extractFromBuffer(new ReflectiveObject(o), new ReflectiveObject(buffer));
    }

    private Object populateBuilder(ReflectiveObject o) throws Exception {
        ReflectiveObject builder = new ReflectiveObject(getProtoBufferClass(o.getObject()).getMethod("newBuilder").invoke(null));

        for (Field field : o.getFieldsAnnotatedWith(ProtoBufferField.class)) {
            String fieldName = field.getAnnotation(ProtoBufferField.class).fieldName();
            ReflectiveObject invoked = o.smartGet(field.getName());
            if (fieldShouldBeRecursed(field)) {
                builder.smartSet(fieldName, populateBuilder(invoked));
            } else {
                builder.smartSet(fieldName, invoked);
            }
        }

        return builder.getObject();
    }

    private Object extractFromBuffer(ReflectiveObject o, ReflectiveObject buffer) throws Exception {

        Set<Field> bufferFields = o.getFieldsAnnotatedWith(ProtoBufferField.class);
        for (Field field : bufferFields) {
            ReflectiveObject fieldValue = buffer.smartGet(field.getAnnotation(ProtoBufferField.class).fieldName());
            if (fieldShouldBeRecursed(field)) {
                o.smartSet(field.getName(), extractFromBuffer(new ReflectiveObject(field.getType()), fieldValue));
            } else {
                o.smartSet(field.getName(), fieldValue);
            }

        }
        return o.getObject();
    }

    private boolean fieldShouldBeRecursed(Field field) {
        return field.getType().getAnnotation(ProtoBufferData.class) != null;
    }

    private Class<?> getProtoBufferClass(Object o) {
        return o.getClass().getAnnotation(ProtoBufferData.class).protoBuffer();
    }
}

@@ -40,31 +40,137 @@
 ort 
-java.lang.reflect.Field
+org.apache.commons.lang3.reflect.MethodUtils;%0A%0Aimport java.lang.reflect.Field;%0Aimport java.lang.reflect.InvocationTargetException
 ;%0Aim
@@ -763,24 +763,34 @@
 s);%0A        
+Reflective
 Object o = C
@@ -788,16 +788,37 @@
 ect o = 
+new ReflectiveObject(
 Class.fo
@@ -862,16 +862,17 @@
 stance()
+)
 ;%0A%0A     
@@ -878,140 +878,61 @@
     
-Class%3C?%3E bufferClass = getProtoBufferClass(o);%0A%0A        Object buffer = bufferClass.getMethod(%22parseFrom%22, byte%5B%5D.class).invoke(null
+ReflectiveObject buffer = createBuffer(o, %22parseFrom%22
 , me
@@ -989,62 +989,18 @@
 fer(
-new ReflectiveObject(o), new ReflectiveObject(
+o, 
 buffer)
-)
 ;%0A  
@@ -1117,100 +1117,35 @@
 r = 
-new ReflectiveObject(getProtoBufferClass(o.getObject()).getMethod(%22newBuilder%22).invoke(null)
+createBuffer(o,%22newBuilder%22
 );%0A%0A
@@ -2281,32 +2281,269 @@
 bject();%0A    %7D%0A%0A
+    private ReflectiveObject createBuffer(ReflectiveObject o, String methodName, Object... params) throws Exception%7B%0A        return new ReflectiveObject(MethodUtils.invokeStaticMethod(getProtoBufferClass(o), methodName, params));%0A    %7D%0A%0A
     private bool
@@ -2708,16 +2708,26 @@
 erClass(
+Reflective
 Object o
@@ -2747,16 +2747,28 @@
 eturn o.
+getObject().
 getClass

use more reflective objects