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

org.eclipse.yasson.internal.ComponentMatcher Maven / Gradle / Ivy

There is a newer version: 3.0.4
Show newest version
/*******************************************************************************
 * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 * Roman Grigoriadi
 ******************************************************************************/

package org.eclipse.yasson.internal;

import org.eclipse.yasson.internal.components.*;
import org.eclipse.yasson.internal.model.JsonBindingModel;

import javax.json.bind.JsonbConfig;
import javax.json.bind.adapter.JsonbAdapter;
import javax.json.bind.serializer.JsonbDeserializer;
import javax.json.bind.serializer.JsonbSerializer;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Searches for a registered components or Serializer for a given type.
 *
 * @author Roman Grigoriadi
 */
public class ComponentMatcher {

    private final JsonbContext jsonbContext;

    /**
     * Supplier for component binging.
     * @param  component binding class
     */
    private interface ComponentSupplier {

        T getComponent(ComponentBindings componentBindings);
    }

    private final ConcurrentMap userComponents;

    /**
     * Create component matcher.
     * @param context mandatory
     */
    ComponentMatcher(JsonbContext context) {
        Objects.requireNonNull(context);
        this.jsonbContext = context;
        userComponents = new ConcurrentHashMap<>();
        init();
    }

    /**
     * Called during context creation, introspecting user components provided with JsonbConfig.
     */
    void init() {
        final JsonbSerializer[] serializers = (JsonbSerializer[])jsonbContext.getConfig().getProperty(JsonbConfig.SERIALIZERS).orElseGet(()->new JsonbSerializer[]{});
        for (JsonbSerializer serializer : serializers) {
            introspectSerializerBinding(serializer.getClass(), serializer);
        }
        final JsonbDeserializer[] deserializers = (JsonbDeserializer[])jsonbContext.getConfig().getProperty(JsonbConfig.DESERIALIZERS).orElseGet(()->new JsonbDeserializer[]{});
        for (JsonbDeserializer deserializer : deserializers) {
            introspectDeserializerBinding(deserializer.getClass(), deserializer);
        }

        final JsonbAdapter[] adapters = (JsonbAdapter[]) jsonbContext.getConfig().getProperty(JsonbConfig.ADAPTERS).orElseGet(()->new JsonbAdapter[]{});
        for (JsonbAdapter adapter : adapters) {
            introspectAdapterBinding(adapter.getClass(), adapter);
        }
    }

    private ComponentBindings getBindingInfo(Type type) {
        return userComponents.compute(type, (type1, bindingInfo) -> bindingInfo != null ? bindingInfo : new ComponentBindings(type1));
    }

    private void addSerializer(Type bindingType, SerializerBinding serializer) {
        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
            if (bindings.getSerializer() != null) {
                return bindings;
            }
            registerGeneric(bindingType);
            return new ComponentBindings(bindingType, serializer, bindings.getDeserializer(), bindings.getAdapterInfo());
        });
    }

    private void addDeserializer(Type bindingType, DeserializerBinding deserializer) {
        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
            if (bindings.getDeserializer() != null) {
                return bindings;
            }
            registerGeneric(bindingType);
            return new ComponentBindings(bindingType, bindings.getSerializer(), deserializer, bindings.getAdapterInfo());
        });
    }

    private void addAdapter(Type bindingType, AdapterBinding adapter) {
        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
            if (bindings.getAdapterInfo() != null) {
                return bindings;
            }
            registerGeneric(bindingType);
            return new ComponentBindings(bindingType, bindings.getSerializer(), bindings.getDeserializer(), adapter);
        });
    }

    /**
     * If type is not parametrized runtime component resolution doesn't has to happen.
     *
     * @param bindingType component binding type
     */
    private void registerGeneric(Type bindingType) {
        if (bindingType instanceof ParameterizedType && !jsonbContext.genericComponentsPresent()) {
            jsonbContext.registerGenericComponentFlag();
        }
    }

    /**
     * Lookup serializer binding for a given property runtime type.
     * @param propertyRuntimeType runtime type of a property
     * @param propertyModel model of a property
     * @return serializer optional
     */
    @SuppressWarnings("unchecked")
    public Optional> getSerializerBinding(Type propertyRuntimeType, JsonBindingModel propertyModel) {
        if (propertyModel == null || propertyModel.getCustomization() == null || propertyModel.getCustomization().getSerializerBinding() == null) {
            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializer);
        }
        return getComponentBinding(propertyRuntimeType, propertyModel.getCustomization().getSerializerBinding());
    }

    /**
     * Lookup deserializer binding for a given property runtime type.
     * @param propertyRuntimeType runtime type of a property
     * @param model model of a property
     * @return serializer optional
     */
    @SuppressWarnings("unchecked")
    public Optional> getDeserializerBinding(Type propertyRuntimeType, JsonBindingModel model) {
        if (model == null || model.getCustomization() == null || model.getCustomization().getDeserializerBinding() == null) {
            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getDeserializer);
        }
        return getComponentBinding(propertyRuntimeType, model.getCustomization().getDeserializerBinding());
    }

    /**
     * Get components from property model (if declared by annotation and runtime type matches),
     * or return components searched by runtime type
     *
     * @param propertyRuntimeType runtime type not null
     * @param model model nullable
     * @return components info if present
     */
    public Optional getAdapterBinding(Type propertyRuntimeType, JsonBindingModel model) {
        if (model == null || model.getCustomization() == null ||  model.getCustomization().getAdapterBinding() == null) {
            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo);
        }
        return getComponentBinding(propertyRuntimeType, model.getCustomization().getAdapterBinding());
    }

    private  Optional getComponentBinding(Type propertyRuntimeType, T componentBinding) {
        //need runtime check, ParameterizedType property may have generic components assigned which is not compatible
        //for given runtime type
        if (matches(propertyRuntimeType, componentBinding.getBindingType())) {
            return Optional.of(componentBinding);
        }
        return Optional.empty();
    }

    private  Optional searchComponentBinding(Type runtimeType, ComponentSupplier supplier) {
        for (ComponentBindings componentBindings : userComponents.values()) {
            final T component = supplier.getComponent(componentBindings);
            if (component != null && matches(runtimeType, componentBindings.getBindingType())) {
                return Optional.of(component);
            }
        }
        return Optional.empty();
    }

    private boolean matches(Type runtimeType, Type componentBindingType) {
        if (componentBindingType.equals(runtimeType)) {
            return true;
        }
        //don't try to runtime generic scan if not needed
        if (!jsonbContext.genericComponentsPresent()) {
            return false;
        }
        if (componentBindingType instanceof Class && runtimeType instanceof Class) {
            //for polymorphic adapters
            return ((Class) componentBindingType).isAssignableFrom((Class) runtimeType);
        }
        return runtimeType instanceof ParameterizedType && componentBindingType instanceof ParameterizedType &&
                ReflectionUtils.getRawType(runtimeType) == ReflectionUtils.getRawType(componentBindingType) &&
                matchTypeArguments((ParameterizedType) runtimeType, (ParameterizedType) componentBindingType);
    }

    /**
     * If runtimeType to adapt is a ParametrizedType, check all type args to match against components args.
     */
    private boolean matchTypeArguments(ParameterizedType requiredType, ParameterizedType componentBound) {
        final Type[] requiredTypeArguments = requiredType.getActualTypeArguments();
        final Type[] adapterBoundTypeArguments = componentBound.getActualTypeArguments();
        if (requiredTypeArguments.length != adapterBoundTypeArguments.length) {
            return false;
        }
        for(int i = 0; i< requiredTypeArguments.length; i++) {
            Type adapterTypeArgument = adapterBoundTypeArguments[i];
            if (!requiredTypeArguments[i].equals(adapterTypeArgument)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Introspect components generic information and put resolved types into metadata wrapper.
     *
     * @param adapterClass class of an components
     * @param instance components instance
     * @return introspected info with resolved typevar types.
     */
    AdapterBinding introspectAdapterBinding(Class adapterClass, JsonbAdapter instance) {
        final ParameterizedType adapterRuntimeType = ReflectionUtils.findParameterizedType(adapterClass, JsonbAdapter.class);
        final Type[] adapterTypeArguments = adapterRuntimeType.getActualTypeArguments();
        Type adaptFromType = resolveTypeArg(adapterTypeArguments[0], adapterClass);
        Type adaptToType = resolveTypeArg(adapterTypeArguments[1], adapterClass);
        final ComponentBindings componentBindings = getBindingInfo(adaptFromType);
        if (componentBindings.getAdapterInfo() != null && componentBindings.getAdapterInfo().getAdapter().getClass().equals(adapterClass)) {
            return componentBindings.getAdapterInfo();
        }
        JsonbAdapter newAdapter = instance != null ? instance : jsonbContext.getComponentInstanceCreator().getOrCreateComponent(adapterClass);
        final AdapterBinding adapterInfo = new AdapterBinding(adaptFromType, adaptToType, newAdapter);
        addAdapter(adaptFromType, adapterInfo);
        return adapterInfo;
    }

    /**
     * If an instance of deserializerClass is present in context and is bound for same type, return that instance.
     * Otherwise create new instance and set it to context.
     *
     * @param deserializerClass class of deserializer
     * @param instance instance to use if not cached already
     * @return wrapper used in property models
     */
    DeserializerBinding introspectDeserializerBinding(Class deserializerClass, JsonbDeserializer instance) {
        final ParameterizedType deserializerRuntimeType = ReflectionUtils.findParameterizedType(deserializerClass, JsonbDeserializer.class);
        Type deserializerBindingType = resolveTypeArg(deserializerRuntimeType.getActualTypeArguments()[0], deserializerClass.getClass());
        final ComponentBindings componentBindings = getBindingInfo(deserializerBindingType);
        if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getClass().equals(deserializerClass)) {
            return componentBindings.getDeserializer();
        } else {
            JsonbDeserializer deserializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator()
                    .getOrCreateComponent(deserializerClass);
            final DeserializerBinding deserializerBinding = new DeserializerBinding(deserializerBindingType, deserializer);
            addDeserializer(deserializerBindingType, deserializerBinding);
            return deserializerBinding;
        }
    }

    /**
     * If an instance of serializerClass is present in context and is bound for same type, return that instance.
     * Otherwise create new instance and set it to context.
     *
     * @param serializerClass class of deserializer
     * @param instance instance to use if not cached
     * @return wrapper used in property models
     */
    SerializerBinding introspectSerializerBinding(Class serializerClass, JsonbSerializer instance) {
        final ParameterizedType serializerRuntimeType = ReflectionUtils.findParameterizedType(serializerClass, JsonbSerializer.class);
        Type serBindingType = resolveTypeArg(serializerRuntimeType.getActualTypeArguments()[0], serializerClass.getClass());
        final ComponentBindings componentBindings = getBindingInfo(serBindingType);
        if (componentBindings.getSerializer() != null && componentBindings.getSerializer().getClass().equals(serializerClass)) {
            return componentBindings.getSerializer();
        } else {
            JsonbSerializer serializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator()
                    .getOrCreateComponent(serializerClass);
            final SerializerBinding serializerBinding = new SerializerBinding(serBindingType, serializer);
            addSerializer(serBindingType, serializerBinding);
            return serializerBinding;
        }

    }


    private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) {
        if(adapterTypeArg instanceof ParameterizedType) {
            return ReflectionUtils.resolveTypeArguments((ParameterizedType) adapterTypeArg, adapterType);
        } else if (adapterTypeArg instanceof TypeVariable) {
            return ReflectionUtils.resolveItemVariableType(new RuntimeTypeHolder(null, adapterType), (TypeVariable) adapterTypeArg);
        } else {
            return adapterTypeArg;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy