blob: 732711933ab0d6b2d5eabb84baa22c0b45dab705 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.servicecomb.foundation.protobuf.ProtoMapper;
import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst;
import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptor;
import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JavaType;
import io.protostuff.InputEx;
import io.protostuff.OutputEx;
import io.protostuff.SchemaEx;
import io.protostuff.compiler.model.Field;
import io.protostuff.compiler.model.Message;
import io.protostuff.runtime.FieldMapEx;
import io.protostuff.runtime.FieldSchema;
import io.protostuff.runtime.RuntimeEnv;
import io.protostuff.runtime.RuntimeEnv.Instantiator;
/**
* <pre>
* map.put("user", new User())
* root write from map, but user should write from pojo
* so one schema should support dynamic and concrete logic at the same time
* </pre>
*/
public class MessageReadSchema<T> implements SchemaEx<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageReadSchema.class);
protected ProtoMapper protoMapper;
protected Message message;
private FieldMapEx<T> fieldMap;
private Instantiator<T> instantiator;
private JavaType javaType;
Map<String, Type> argumentsTypes;
private boolean argumentsRoot = false;
@SuppressWarnings("unchecked")
public MessageReadSchema(ProtoMapper protoMapper, Message message, Map<String, Type> argumentsTypes) {
this.argumentsRoot = true;
this.protoMapper = protoMapper;
this.message = message;
this.argumentsTypes = argumentsTypes;
this.instantiator = RuntimeEnv.newInstantiator((Class<T>) ProtoConst.MAP_TYPE.getRawClass());
}
@SuppressWarnings("unchecked")
public MessageReadSchema(ProtoMapper protoMapper, Message message, JavaType javaType) {
this.protoMapper = protoMapper;
this.message = message;
this.javaType = javaType;
if (javaType.isJavaLangObject() || Map.class.isAssignableFrom(javaType.getRawClass())) {
javaType = ProtoConst.MAP_TYPE;
}
this.instantiator = RuntimeEnv.newInstantiator((Class<T>) javaType.getRawClass());
}
public Message getMessage() {
return message;
}
@Override
public T newMessage() {
return instantiator.newInstance();
}
@Override
public String messageName() {
return message.getName();
}
public FieldMapEx<T> getFieldMap() {
return fieldMap;
}
@SuppressWarnings("unchecked")
@Override
public void init() {
if (argumentsRoot) {
this.fieldMap = (FieldMapEx<T>) protoMapper.getDeserializerSchemaManager()
.createMapFields(message, argumentsTypes);
return;
}
if (Map.class.isAssignableFrom(javaType.getRawClass())) {
this.fieldMap = (FieldMapEx<T>) protoMapper.getDeserializerSchemaManager()
.createMapFields(message);
return;
}
this.createFieldMap();
}
private void createFieldMap() {
DeserializerSchemaManager deserializerSchemaManager = protoMapper.getDeserializerSchemaManager();
BeanDescriptor beanDescriptor = protoMapper.getBeanDescriptorManager().getOrCreateBeanDescriptor(javaType);
List<FieldSchema<T>> fieldSchemas = new ArrayList<>();
for (PropertyDescriptor propertyDescriptor : beanDescriptor.getPropertyDescriptors().values()) {
Field protoField = message.getField(propertyDescriptor.getName());
if (protoField == null) {
LOGGER.info("java field {}:{} not exist in proto message {}, ignore it.",
beanDescriptor.getJavaType().getRawClass().getName(),
propertyDescriptor.getName(), message.getCanonicalName());
continue;
}
if (propertyDescriptor.getSetter() == null) {
LOGGER.info("no setter for java field {}:{} in proto message {}, ignore it.",
beanDescriptor.getJavaType().getRawClass().getName(),
propertyDescriptor.getName(), message.getCanonicalName());
continue;
}
FieldSchema<T> fieldSchema = deserializerSchemaManager.createSchemaField(protoField, propertyDescriptor);
fieldSchemas.add(fieldSchema);
}
this.fieldMap = FieldMapEx.createFieldMap(fieldSchemas);
}
@Override
public void mergeFrom(InputEx input, T message) throws IOException {
FieldSchema<T> fieldSchema = null;
try {
for (int n = input.readFieldNumber(); n != 0; ) {
fieldSchema = fieldMap.getFieldByNumber(n);
if (fieldSchema != null) {
n = fieldSchema.mergeFrom(input, message);
continue;
}
input.handleUnknownField(n);
n = input.readFieldNumber();
}
} catch (Throwable e) {
if (fieldSchema != null) {
Field protoField = fieldSchema.getProtoField();
LOGGER.error("Failed to mergeFrom, field={}:{}, type={}, error {}",
protoField.getType().getCanonicalName(),
protoField.getName(),
protoField.getTypeName(),
e.getMessage());
}
throw e;
}
}
@Override
public void writeTo(OutputEx output, Object value) throws IOException {
throw new UnsupportedOperationException();
}
}