All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dozermapper.protobuf.util.ProtoUtils Maven / Gradle / Ivy

There is a newer version: 6.5.2
Show newest version
/*
 * Copyright 2005-2018 Dozer Project
 *
 * 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.github.dozermapper.protobuf.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import com.github.dozermapper.core.MappingException;
import com.github.dozermapper.core.config.BeanContainer;
import com.github.dozermapper.core.util.MappingUtils;
import com.google.common.base.CaseFormat;
import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;
import com.google.protobuf.ProtocolMessageEnum;

import org.apache.commons.lang3.StringUtils;

/**
 * Protobuf utility methods
 */
public final class ProtoUtils {

    private ProtoUtils() {
    }

    /**
     * Gets the {@link Message.Builder} instance associated with the clazz
     *
     * @param clazz {@link Message} clazz
     * @return {@link Message.Builder} instance associated with the clazz
     */
    public static Message.Builder getBuilder(Class clazz) {
        final Message.Builder protoBuilder;

        try {
            Method newBuilderMethod = clazz.getMethod("newBuilder");
            protoBuilder = (Message.Builder)newBuilderMethod.invoke(null);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new MappingException("Failed to create Message.Builder for " + clazz.getCanonicalName(), e);
        }

        return protoBuilder;
    }

    /**
     * Gets a list of {@link Descriptors.FieldDescriptor} associated with the clazz
     *
     * @param clazz {@link Message} clazz
     * @return list of {@link Descriptors.FieldDescriptor} associated with the clazz
     */
    public static List getFieldDescriptors(Class clazz) {
        Message.Builder protoBuilder = getBuilder(clazz);

        return getFieldDescriptors(protoBuilder);
    }

    /**
     * Gets a list of {@link Descriptors.FieldDescriptor} associated with the {@link Message.Builder}
     *
     * @param protoBuilder {@link Message.Builder} instance
     * @return list of {@link Descriptors.FieldDescriptor} associated with the {@link Message.Builder}
     */
    private static List getFieldDescriptors(Message.Builder protoBuilder) {
        return protoBuilder.getDescriptorForType().getFields();
    }

    /**
     * Gets a {@link Descriptors.FieldDescriptor} associated with the clazz, which matches the fieldName,
     * either with an exact match, or after applying a transformation to camel-case.
     * 

* Returns null if there is no match. * * @param clazz {@link Message} clazz * @param fieldName fieldName to find * @return {@link Descriptors.FieldDescriptor} associated with the clazz and which matches the fieldName */ public static Descriptors.FieldDescriptor getFieldDescriptor(Class clazz, String fieldName) { final List protoFieldDescriptors = getFieldDescriptors(clazz); for (Descriptors.FieldDescriptor descriptor : protoFieldDescriptors) { if (sameField(fieldName, descriptor.getName())) { return descriptor; } } return null; } private static boolean sameField(String fieldName, String protoFieldName) { if (fieldName.equals(protoFieldName)) { return true; } // Try to compare field name with Protobuf official snake case syntax return fieldName.equals(toCamelCase(protoFieldName)); } /** * Gets the field value from the {@link Message}, which matches the fieldName, * either with an exact match, or after applying a transformation to camel-case. *

* Returns null if there is no match. * * @param message {@link Message} instance * @param fieldName fieldName to find * @return field value from the {@link Message} for the specified field name, or null if none found */ public static Object getFieldValue(Object message, String fieldName) { Object answer = null; Map fieldsMap = ((Message)message).getAllFields(); for (Map.Entry field : fieldsMap.entrySet()) { if (sameField(fieldName, field.getKey().getName())) { if (field.getKey().isMapField()) { // Capitalize the first letter of the string; String propertyName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); String methodName = String.format("get%sMap", propertyName); try { Method mapGetter = message.getClass().getMethod(methodName); answer = mapGetter.invoke(message); break; } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new MappingException("Could not introspect map field with method " + methodName, e); } } else { answer = field.getValue(); break; } } } return answer; } /** * Gets the class type of the {@link Descriptors.FieldDescriptor} * * @param descriptor {@link Descriptors.FieldDescriptor} instance * @param beanContainer {@link BeanContainer} instance * @return class type of the {@link Descriptors.FieldDescriptor} */ public static Class getJavaClass(final Descriptors.FieldDescriptor descriptor, BeanContainer beanContainer) { if (descriptor.isMapField()) { return Map.class; } if (descriptor.isRepeated()) { return List.class; } return getJavaClassIgnoreRepeated(descriptor, beanContainer); } /** * Gets the class type of the {@link Descriptors.FieldDescriptor} * * @param descriptor {@link Descriptors.FieldDescriptor} instance * @param beanContainer {@link BeanContainer} instance * @return class type of the {@link Descriptors.FieldDescriptor} or null, if {@link Descriptors.FieldDescriptor#isRepeated()} */ public static Class getJavaGenericClassForCollection(final Descriptors.FieldDescriptor descriptor, BeanContainer beanContainer) { if (!descriptor.isRepeated()) { return null; } return getJavaClassIgnoreRepeated(descriptor, beanContainer); } private static Class getJavaClassIgnoreRepeated(final Descriptors.FieldDescriptor descriptor, BeanContainer beanContainer) { switch (descriptor.getJavaType()) { case INT: return Integer.class; case LONG: return Long.class; case FLOAT: return Float.class; case DOUBLE: return Double.class; case BOOLEAN: return Boolean.class; case STRING: return String.class; case BYTE_STRING: return ByteString.class; //code duplicate, but GenericDescriptor interface is private in protobuf case ENUM: return getEnumClassByEnumDescriptor(descriptor.getEnumType(), beanContainer); case MESSAGE: return MappingUtils.loadClass(StringUtils.join( getFullyQualifiedClassName(descriptor.getMessageType().getFile().getOptions(), descriptor.getMessageType().getName()), '.'), beanContainer); default: throw new MappingException("Unable to find " + descriptor.getJavaType()); } } private static String[] getFullyQualifiedClassName(DescriptorProtos.FileOptions options, String name) { if (options.getJavaMultipleFiles()) { return new String[] {options.getJavaPackage(), name}; } return new String[] {options.getJavaPackage(), options.getJavaOuterClassname(), name}; } @SuppressWarnings("unchecked") private static Class getEnumClassByEnumDescriptor(Descriptors.EnumDescriptor descriptor, BeanContainer beanContainer) { String name = StringUtils.join(getFullyQualifiedClassName(descriptor.getFile().getOptions(), descriptor.getName()), '.'); return (Class)MappingUtils.loadClass(name, beanContainer); } /** * Wrap {@link ProtocolMessageEnum} or a {@link List} to a {@link Descriptors.EnumValueDescriptor} * If the value is neither {@link ProtocolMessageEnum} or a {@link List}, the value is returned. * * @param value {@link ProtocolMessageEnum} or a {@link List} * @return {@link Descriptors.EnumValueDescriptor} if value is {@link ProtocolMessageEnum}, else a {@link List} of {@link Descriptors.EnumValueDescriptor} */ @SuppressWarnings("unchecked") public static Object wrapEnums(Object value) { if (value instanceof ProtocolMessageEnum) { return ((ProtocolMessageEnum)value).getValueDescriptor(); } // There is no other collections using in proto, only list if (value instanceof List) { List modifiedList = new ArrayList(((List)value).size()); for (Object element : (List)value) { modifiedList.add(wrapEnums(element)); } return modifiedList; } return value; } /** * Unwrap {@link Descriptors.EnumValueDescriptor} or a {@link Collection} to a raw {@link Enum} * If the value is neither {@link Descriptors.EnumValueDescriptor} or a {@link Collection}, the value is returned. * * @param value {@link Descriptors.EnumValueDescriptor} or a {@link Collection} * @param beanContainer {@link BeanContainer} instance * @return {@link Enum} if value is {@link Descriptors.EnumValueDescriptor}, else a {@link Collection} of {@link Enum} */ @SuppressWarnings("unchecked") public static Object unwrapEnums(Object value, BeanContainer beanContainer) { if (value instanceof Descriptors.EnumValueDescriptor) { Descriptors.EnumValueDescriptor descriptor = (Descriptors.EnumValueDescriptor)value; Class enumClass = getEnumClassByEnumDescriptor(descriptor.getType(), beanContainer); for (Enum enumValue : enumClass.getEnumConstants()) { if (((Descriptors.EnumValueDescriptor)value).getName().equals(enumValue.name())) { return enumValue; } } return null; } if (value instanceof Collection) { Collection valueCollection = (Collection)value; List modifiedList = new ArrayList(valueCollection.size()); for (Object element : valueCollection) { modifiedList.add(unwrapEnums(element, beanContainer)); } return modifiedList; } return value; } /** * Converts name to CamelCase * * @param name name to convert * @return converted name to CamelCase */ public static String toCamelCase(String name) { return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy