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

com.blazebit.persistence.integration.jackson.EntityViewAwareObjectMapper Maven / Gradle / Ivy

There is a newer version: 1.6.12
Show newest version
/*
 * Copyright 2014 - 2020 Blazebit.
 *
 * 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.blazebit.persistence.integration.jackson;

import com.blazebit.persistence.view.EntityViewManager;
import com.blazebit.persistence.view.metamodel.ManagedViewType;
import com.blazebit.persistence.view.metamodel.ViewType;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;

/**
 * @author Christian Beikov
 * @since 1.4.0
 */
public class EntityViewAwareObjectMapper {

    private final EntityViewManager entityViewManager;
    private final ObjectMapper objectMapper;

    public EntityViewAwareObjectMapper(final EntityViewManager entityViewManager, final ObjectMapper objectMapper, final EntityViewIdValueAccessor entityViewIdValueAccessor) {
        this.entityViewManager = entityViewManager;
        SimpleModule module = new SimpleModule();

        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) {
                ManagedViewType view = entityViewManager.getMetamodel().managedView(beanDesc.getBeanClass());
                if (view != null) {
                    return new EntityViewReferenceDeserializer(entityViewManager, view, objectMapper, beanDesc.getIgnoredPropertyNames(), entityViewIdValueAccessor);
                }
                return deserializer;
            }
        });
        objectMapper.registerModule(module);
        // We need this property, otherwise Jackson thinks it can use non-visible setters as mutators
        objectMapper.configure(MapperFeature.INFER_PROPERTY_MUTATORS, false);
        // Obviously, we don't want fields to be set which are final because these are read-only
        objectMapper.configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS, false);
        objectMapper.setVisibility(new VisibilityChecker.Std(JsonAutoDetect.Visibility.DEFAULT) {

            // This is for de-serialization so we do metamodel.managedView(m.getDeclaringClass()) == null because de-serialization is only supported when setters are available
            // The id attribute is the exception. We also consider the id a property even if it doesn't have a setter

            @Override
            public boolean isGetterVisible(Method m) {
                ManagedViewType managedViewType = entityViewManager.getMetamodel().managedView(m.getDeclaringClass());
                return (managedViewType == null || managedViewType instanceof ViewType && ((ViewType) managedViewType).getIdAttribute().getJavaMethod().getName().equals(m.getName()))
                        && super.isGetterVisible(m);
            }

            @Override
            public boolean isGetterVisible(AnnotatedMethod m) {
                ManagedViewType managedViewType = entityViewManager.getMetamodel().managedView(m.getDeclaringClass());
                return (managedViewType == null || managedViewType instanceof ViewType && ((ViewType) managedViewType).getIdAttribute().getJavaMethod().getName().equals(m.getName()))
                        && super.isGetterVisible(m);
            }

            @Override
            public boolean isIsGetterVisible(Method m) {
                return entityViewManager.getMetamodel().managedView(m.getDeclaringClass()) == null && super.isGetterVisible(m);
            }

            @Override
            public boolean isIsGetterVisible(AnnotatedMethod m) {
                return entityViewManager.getMetamodel().managedView(m.getDeclaringClass()) == null && super.isGetterVisible(m);
            }

            // We make setters for collections "invisible" so that is uses a SetterlessProperty to always merge values into existing collections

            @Override
            public boolean isSetterVisible(Method m) {
                Class rawParameterType;
                return super.isSetterVisible(m) && !Collection.class.isAssignableFrom(rawParameterType = m.getParameterTypes()[0]) && !Map.class.isAssignableFrom(rawParameterType);
            }

            @Override
            public boolean isSetterVisible(AnnotatedMethod m) {
                Class rawParameterType;
                return super.isSetterVisible(m) && !Collection.class.isAssignableFrom(rawParameterType = m.getRawParameterType(0)) && !Map.class.isAssignableFrom(rawParameterType);
            }
        });

        this.objectMapper = objectMapper;
    }

    public EntityViewManager getEntityViewManager() {
        return entityViewManager;
    }

    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public boolean canRead(Class clazz) {
        return entityViewManager.getMetamodel().managedView(clazz) != null;
    }

    public boolean canRead(JavaType javaType) {
        if (!javaType.isContainerType()) {
            return canRead(javaType.getRawClass());
        } else if (javaType.isCollectionLikeType()) {
            return canRead(javaType.getContentType().getRawClass());
        }
        return false;
    }

    public ObjectReader readerFor(JavaType javaType) {
        if (Collection.class.isAssignableFrom(javaType.getRawClass())) {
            return objectMapper.readerFor(javaType);
        } else {
            return readerFor(javaType.getRawClass());
        }
    }

    public ObjectReader readerFor(Class type) {
        return objectMapper.readerFor(type);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy