com.chm.converter.protobuf.Protobuf Maven / Gradle / Ivy
The newest version!
package com.chm.converter.protobuf;
import com.chm.converter.core.Converter;
import com.chm.converter.core.utils.ClassUtil;
import com.chm.converter.core.utils.MapUtil;
import com.chm.converter.protostuff.codec.ByteArrayInput;
import com.chm.converter.protostuff.codec.ProtostuffCodec;
import com.chm.converter.protostuff.codec.ProtostuffCodecGenerate;
import com.google.protobuf.BoolValue;
import com.google.protobuf.BytesValue;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.Empty;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.Parser;
import com.google.protobuf.StringValue;
import io.protostuff.Input;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufOutput;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentMap;
/**
* @author caihongming
* @version v1.0
* @date 2021-11-18
**/
public class Protobuf {
private final ConcurrentMap marshallers = MapUtil.newConcurrentHashMap();
private final ExtensionRegistryLite globalRegistry = ExtensionRegistryLite.getEmptyRegistry();
{
// Built-in types need to be registered in advance
marshaller(Empty.getDefaultInstance());
marshaller(BoolValue.getDefaultInstance());
marshaller(Int32Value.getDefaultInstance());
marshaller(Int64Value.getDefaultInstance());
marshaller(FloatValue.getDefaultInstance());
marshaller(DoubleValue.getDefaultInstance());
marshaller(BytesValue.getDefaultInstance());
marshaller(StringValue.getDefaultInstance());
}
private final ProtostuffCodecGenerate protostuffCodecGenerate;
public Protobuf(Converter> converter) {
this.protostuffCodecGenerate = ProtostuffCodecGenerate.newDefault(converter);
}
public static boolean isSupported(Type type) {
if (type == null) {
return false;
}
Class> cls = ClassUtil.getClassByType(type);
if (MessageLite.class.isAssignableFrom(cls)) {
return true;
}
return false;
}
public byte[] serialize(Object value) throws IOException {
if (value == null) {
return new byte[0];
}
Class> cls = value.getClass();
if (isSupported(cls)) {
MessageLite messageLite = (MessageLite) value;
return messageLite.toByteArray();
}
ProtostuffCodec protostuffCodec = protostuffCodecGenerate.get(cls);
ProtobufOutput output = new ProtobufOutput(LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
protostuffCodec.writeTo(output, value);
return output.toByteArray();
}
@SuppressWarnings("unchecked")
public T deserialize(byte[] source, Type targetType) throws IOException {
if (isSupported(targetType) && !marshallers.containsKey(targetType)) {
try {
Class> cls = ClassUtil.getClassByType(targetType);
Method method = cls.getDeclaredMethod("getDefaultInstance");
MessageLite defaultInstance = (MessageLite) method.invoke(null);
marshallers.put(targetType, new MessageMarshaller<>(defaultInstance));
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ignored) {
}
}
MessageMarshaller> marshaller = marshallers.get(targetType);
if (marshaller != null) {
return (T) marshaller.parse(source);
}
ProtostuffCodec protostuffCodec = protostuffCodecGenerate.get(targetType);
Input input = new ByteArrayInput(new io.protostuff.ByteArrayInput(source, false));
return protostuffCodec.mergeFrom(input);
}
public void marshaller(T defaultInstance) {
marshallers.put(defaultInstance.getClass(), new MessageMarshaller<>(defaultInstance));
}
private final class MessageMarshaller {
private final Parser parser;
private final T defaultInstance;
@SuppressWarnings("unchecked")
MessageMarshaller(T defaultInstance) {
this.defaultInstance = defaultInstance;
parser = (Parser) defaultInstance.getParserForType();
}
@SuppressWarnings("unchecked")
public Class getMessageClass() {
// Precisely T since protobuf doesn't let messages extend other messages.
return (Class) defaultInstance.getClass();
}
public T getMessagePrototype() {
return defaultInstance;
}
public T parse(byte[] source) throws InvalidProtocolBufferException {
if (source == null) {
return null;
}
return parser.parseFrom(source, globalRegistry);
}
}
}