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

net.dongliu.prettypb.runtime.ProtoBufDecoder Maven / Gradle / Ivy

There is a newer version: 0.3.5
Show newest version
package net.dongliu.prettypb.runtime;

import net.dongliu.prettypb.runtime.code.ProtoFieldInfo;
import net.dongliu.prettypb.runtime.code.ProtoInfo;
import net.dongliu.prettypb.runtime.code.ProtoBufReader;
import net.dongliu.prettypb.runtime.exception.IllegalBeanException;
import net.dongliu.prettypb.runtime.exception.IllegalDataException;
import net.dongliu.prettypb.runtime.exception.ProtoDeSerializeException;
import net.dongliu.prettypb.runtime.include.Extendable;
import net.dongliu.prettypb.runtime.include.ExtensionField;
import net.dongliu.prettypb.runtime.include.ProtoBean;
import net.dongliu.prettypb.runtime.utils.BeanParser;
import net.dongliu.prettypb.runtime.utils.ExtensionFieldUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * protobuf decoder
 *
 * @author Dong Liu
 */
public class ProtoBufDecoder {

    private ProtoBufReader protoBufReader;

    private ProtoBufDecoder(InputStream in) {
        protoBufReader = new ProtoBufReader(in);
    }

    /**
     * deSerialize value from byte array
     */
    public static  T fromBytes(Class clazz, byte[] bytes) {
        try (InputStream in = new ByteArrayInputStream(bytes)) {
            return fromStream(clazz, in);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * deSerialize value from byte array
     */
    public static  T fromBytes(Class clazz, byte[] bytes, int offset, int len) {
        try (InputStream in = new ByteArrayInputStream(bytes, offset, len)) {
            return fromStream(clazz, in);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * deSerialize value from byte array
     */
    public static  T fromBytes(Class clazz, byte[] bytes,
                                  ExtensionRegistry extensionRegistry) {
        try (InputStream in = new ByteArrayInputStream(bytes)) {
            return fromStream(clazz, in, extensionRegistry);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * deSerialize value from byte array
     */
    public static  T fromBytes(Class clazz, byte[] bytes, int offset, int len,
                                  ExtensionRegistry extensionRegistry) {
        try (InputStream in = new ByteArrayInputStream(bytes, offset, len)) {
            return fromStream(clazz, in, extensionRegistry);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * deSerialize value from input stream
     *
     * @throws IOException
     */
    public static  T fromStream(Class clazz, InputStream in) throws IOException {
        return fromStream(clazz, in, null);
    }

    /**
     * deSerialize value from input stream
     *
     * @throws IOException
     */
    public static  T fromStream(Class clazz, InputStream in,
                                   ExtensionRegistry extensionRegistry) throws IOException {
        ProtoBufDecoder protoBufDecoder = new ProtoBufDecoder(in);
        try {
            return protoBufDecoder.readMessage(clazz, extensionRegistry);
        } catch (IllegalBeanException | IllegalAccessException | InstantiationException
                | IllegalDataException e) {
            throw new ProtoDeSerializeException(e);
        }
    }

    private  T readMessage(Class clazz, ExtensionRegistry extensionRegistry)
            throws IOException, IllegalBeanException, IllegalAccessException,
            InstantiationException, IllegalDataException {
        ProtoInfo protoInfo = BeanParser.getProtoInfo(clazz);

        T bean = clazz.newInstance();
        boolean extend = Extendable.class.isAssignableFrom(clazz);
        while (true) {
            int[] tag = protoBufReader.decodeTag();
            if (tag == null) {
                break;
            }
            int wireType = tag[0];
            int idx = tag[1];
            deSerializeField(clazz, extensionRegistry, protoInfo, bean, extend, wireType, idx);
        }


        // check required field
        for (ProtoFieldInfo info : protoInfo.getProtoFieldInfos()) {
            if (info.required() && !info.hasDefault() && !info.hasField(bean)) {
                throw new IllegalDataException("Required field not exists, " + "bean:"
                        + clazz.getName() + ", field:" + info.getName());
            }
        }

        return bean;
    }

    private  void deSerializeField(Class clazz, ExtensionRegistry extensionRegistry,
                                      ProtoInfo protoInfo, T bean, boolean extend,
                                      int wireType, int idx)
            throws IOException, IllegalDataException {
        ProtoFieldInfo protoFieldInfo = protoInfo.getProtoFieldInfo(idx);
        if (protoFieldInfo != null) {
            // bean field
            try {
                protoFieldInfo.deSerializeField(bean, protoBufReader, extensionRegistry);
            } catch (ProtoDeSerializeException e) {
                throw e;
            } catch (IllegalAccessException | IllegalDataException | RuntimeException e) {
                throw new ProtoDeSerializeException("DeSerialize field failed,bean:"
                        + clazz.getName() + ", field:" + protoFieldInfo.getName(), e);
            }
        } else if (extend) {
            Extendable extendable = (Extendable) bean;
            if (!extendable.tagInExtensions(idx)) {
                // just ignore this field
                protoBufReader.skip(wireType);
            }
            if (extensionRegistry != null) {
                ProtoBean protoBean = clazz.getAnnotation(ProtoBean.class);
                ExtensionField extensionField =
                        extensionRegistry.getRegistered(protoBean, idx);
                if (extensionField != null) {
                    //de-serialize extension
                    ExtensionFieldUtils.readField(extensionField, protoBufReader, extendable,
                            extensionRegistry);
                } else {
                    // skip
                    protoBufReader.skip(wireType);
                }
            } else {
                // skip this field
                protoBufReader.skip(wireType);
            }
        } else {
            throw new IllegalDataException("Proto field with num:" + idx + " not found");
        }
    }

}