Package com.google.nigori.common

Source Code of com.google.nigori.common.TypeAdapterProtobuf

/*
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.nigori.common;

import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.GeneratedMessage;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Gson type adapter for protocol buffers
*
* @author Inderjeet Singh
*/
public class TypeAdapterProtobuf implements JsonSerializer<GeneratedMessage>,
JsonDeserializer<GeneratedMessage> {

  private JsonElement serializeWithEnumRewrite(JsonSerializationContext context, Object o) {
    if (o instanceof EnumValueDescriptor) {
      return context.serialize(((EnumValueDescriptor)o).getName());
    } else {
      return context.serialize(o);
    }
  }

  @Override
  public JsonElement serialize(GeneratedMessage src, Type typeOfSrc,
      JsonSerializationContext context) {
    JsonObject ret = new JsonObject();
    final Map<FieldDescriptor, Object> fields = src.getAllFields();
    for (Map.Entry<FieldDescriptor, Object> fieldPair : fields.entrySet()) {
      final FieldDescriptor desc = fieldPair.getKey();
      if (desc.isRepeated()) {
        List<?> fieldList = (List<?>) fieldPair.getValue();
        if (fieldList.size() != 0) {
          JsonArray array = new JsonArray();
          for (Object o : fieldList) {
            array.add(serializeWithEnumRewrite(context, o));
          }
          ret.add(desc.getName(), array);
          }
        } else {
          ret.add(desc.getName(), serializeWithEnumRewrite(context, fieldPair.getValue()));
        }
      }
      return ret;
    }

    private String camelCaseField(String underscoreName) {
      if (underscoreName == null)
        return null;
      char[] input = underscoreName.toCharArray();
      StringBuffer output = new StringBuffer();
      for(int i = 0; i < input.length; i++) {
        if (input[i] == '_' && (i + 1) < input.length) {
          output.append(Character.toUpperCase(input[++i]));
        } else {
          output.append(input[i]);
        }
      }
      return output.toString();
    }

    @Override
    public GeneratedMessage deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException {

      JsonObject jsonObject = json.getAsJsonObject();
      @SuppressWarnings("unchecked")
      Class<? extends GeneratedMessage> protoClass =
        (Class<? extends GeneratedMessage>) typeOfT;

      try {
        // Invoke the ProtoClass.newBuilder() method
        Object protoBuilder = getCachedMethod(protoClass, "newBuilder")
        .invoke(null);
        Class<?> builderClass = protoBuilder.getClass();

        Descriptor protoDescriptor = (Descriptor) getCachedMethod(
            protoClass, "getDescriptor").invoke(null);
        // Call setters on all of the available fields
        for (FieldDescriptor fieldDescriptor : protoDescriptor.getFields()) {
          String name = fieldDescriptor.getName();
          if (jsonObject.has(name)) {
            JsonElement jsonElement = jsonObject.get(name);
            String fieldName = camelCaseField(name + "_");
            Field field = protoClass.getDeclaredField(fieldName);
            Type fieldType = field.getGenericType();
            if (fieldType.equals(Object.class)){
            // TODO(drt24): this is very evil.
            // In NigoriMessages protobuf strings are stored in a field of type Object so that they
            // can use either String of ByteString as the implementation, however this causes a type
            // error when calling the set method. So we make a (potentially false) assumption that
            // all fields of type Object in NigoriMessages have that type because they actually
            // should have Strings set.
              fieldType = String.class;
            }
            Object fieldValue = context.deserialize(jsonElement, fieldType);
            if(fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) {
              Method methodVD = getCachedMethod(fieldValue.getClass(), "getValueDescriptor");
              fieldValue = methodVD.invoke(fieldValue);
            }        
            Method method = getCachedMethod(
                builderClass, "setField", FieldDescriptor.class, Object.class);
            method.invoke(protoBuilder, fieldDescriptor, fieldValue);
          }
        }
        // Invoke the build method to return the final proto
        return (GeneratedMessage) getCachedMethod(builderClass, "build")
        .invoke(protoBuilder);
      } catch (SecurityException e) {
        throw new JsonParseException(e);
      } catch (NoSuchMethodException e) {
        throw new JsonParseException(e);
      } catch (IllegalArgumentException e) {
        throw new JsonParseException(e);
      } catch (IllegalAccessException e) {
        throw new JsonParseException(e);
      } catch (InvocationTargetException e) {
        throw new JsonParseException(e);
      } catch (NoSuchFieldException e) {
        throw new JsonParseException(e);
      }
    }

    private static Method getCachedMethod(Class<?> clazz, String methodName,
        Class<?>... methodParamTypes) throws NoSuchMethodException {
      Map<Class<?>, Method> mapOfMethods = mapOfMapOfMethods.get(methodName);
      if (mapOfMethods == null) {
        mapOfMethods = new HashMap<Class<?>, Method>();
        mapOfMapOfMethods.put(methodName, mapOfMethods);
      }
      Method method = mapOfMethods.get(clazz);
      if (method == null) {
        method = clazz.getMethod(methodName, methodParamTypes);
        mapOfMethods.put(clazz, method);
      }
      return method;
    }

    private static Map<String, Map<Class<?>, Method>> mapOfMapOfMethods =
      new HashMap<String, Map<Class<?>, Method>>();
  }
TOP

Related Classes of com.google.nigori.common.TypeAdapterProtobuf

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.