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

org.glassfish.jersey.jackson.internal.JacksonObjectProvider Maven / Gradle / Ivy

/*
 * Copyright (c) 2015, 2018 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 v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.jackson.internal;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.glassfish.jersey.message.filtering.spi.AbstractObjectProvider;
import org.glassfish.jersey.message.filtering.spi.ObjectGraph;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.BeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyFilter;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;

/**
 * @author Michal Gajdos
 */
final class JacksonObjectProvider extends AbstractObjectProvider {

    @Override
    public FilterProvider transform(final ObjectGraph graph) {
        // Root entity.
        final FilteringPropertyFilter root = new FilteringPropertyFilter(graph.getEntityClass(),
                graph.getFields(),
                createSubfilters(graph.getEntityClass(), graph.getSubgraphs()));

        return new FilteringFilterProvider(root);
    }

    private Map createSubfilters(final Class entityClass,
                                                                  final Map entitySubgraphs) {
        final Map subfilters = new HashMap<>();

        for (final Map.Entry entry : entitySubgraphs.entrySet()) {
            final String fieldName = entry.getKey();
            final ObjectGraph graph = entry.getValue();

            // Subgraph Fields.
            final Map subgraphs = graph.getSubgraphs(fieldName);

            Map subSubfilters = new HashMap<>();
            if (!subgraphs.isEmpty()) {
                final Class subEntityClass = graph.getEntityClass();
                final Set processed = Collections.singleton(subgraphIdentifier(entityClass, fieldName, subEntityClass));

                subSubfilters = createSubfilters(fieldName, subEntityClass, subgraphs, processed);
            }

            final FilteringPropertyFilter filter = new FilteringPropertyFilter(graph.getEntityClass(),
                    graph.getFields(fieldName), subSubfilters);

            subfilters.put(fieldName, filter);
        }

        return subfilters;
    }

    private Map createSubfilters(final String parent, final Class entityClass,
                                                                  final Map entitySubgraphs,
                                                                  final Set processed) {
        final Map subfilters = new HashMap<>();

        for (final Map.Entry entry : entitySubgraphs.entrySet()) {
            final String fieldName = entry.getKey();
            final ObjectGraph graph = entry.getValue();

            final String path = parent + "." + fieldName;

            // Subgraph Fields.
            final Map subgraphs = graph.getSubgraphs(path);

            final Class subEntityClass = graph.getEntityClass();
            final String processedSubgraph = subgraphIdentifier(entityClass, fieldName, subEntityClass);

            Map subSubfilters = new HashMap<>();
            if (!subgraphs.isEmpty() && !processed.contains(processedSubgraph)) {
                // duplicate processed set so that elements in different subtrees aren't skipped (JERSEY-2892)
                final Set subProcessed = immutableSetOf(processed, processedSubgraph);

                subSubfilters = createSubfilters(path, subEntityClass, subgraphs, subProcessed);
            }

            subfilters.put(fieldName, new FilteringPropertyFilter(graph.getEntityClass(), graph.getFields(path), subSubfilters));
        }

        return subfilters;
    }

    private static class FilteringFilterProvider extends FilterProvider {

        private final FilteringPropertyFilter root;
        private final Stack stack = new Stack<>();

        public FilteringFilterProvider(final FilteringPropertyFilter root) {
            this.root = root;
        }

        @Override
        public BeanPropertyFilter findFilter(final Object filterId) {
            throw new UnsupportedOperationException("Access to deprecated filters not supported");
        }

        @Override
        public PropertyFilter findPropertyFilter(final Object filterId, final Object valueToFilter) {
            if (filterId instanceof String) {
                final String id = (String) filterId;

                // FilterId should represent a class only in case of root entity is marshalled.
                if (id.equals(root.getEntityClass().getName())) {
                    stack.clear();
                    return stack.push(root);
                }

                while (!stack.isEmpty()) {
                    final FilteringPropertyFilter peek = stack.peek();
                    final FilteringPropertyFilter subfilter = peek.findSubfilter(id);

                    if (subfilter != null) {
                        stack.push(subfilter);

                        // Need special handling for maps here - map keys can be filtered as well so we just say that every key is
                        // allowed.
                        if (valueToFilter instanceof Map) {
                            final Map map = (Map) valueToFilter;
                            return new FilteringPropertyFilter(Map.class, map.keySet(),
                                    Collections.emptyMap());
                        }
                        return subfilter;
                    } else {
                        stack.pop();
                    }
                }
            }
            return SimpleBeanPropertyFilter.filterOutAllExcept();
        }
    }

    private static final class FilteringPropertyFilter implements PropertyFilter {

        private final Class entityClass;

        private final Set fields;
        private final Map subfilters;

        private FilteringPropertyFilter(final Class entityClass,
                                        final Set fields, final Map subfilters) {
            this.entityClass = entityClass;

            this.fields = fields;
            this.subfilters = subfilters;
        }

        private boolean include(final String fieldName) {
            return fields.contains(fieldName) || subfilters.containsKey(fieldName);
        }

        @Override
        public void serializeAsField(final Object pojo,
                                     final JsonGenerator jgen,
                                     final SerializerProvider prov,
                                     final PropertyWriter writer) throws Exception {
            if (include(writer.getName())) {
                writer.serializeAsField(pojo, jgen, prov);
            }
        }

        @Override
        public void serializeAsElement(final Object elementValue,
                                       final JsonGenerator jgen,
                                       final SerializerProvider prov,
                                       final PropertyWriter writer) throws Exception {
            if (include(writer.getName())) {
                writer.serializeAsElement(elementValue, jgen, prov);
            }
        }

        @Override
        public void depositSchemaProperty(final PropertyWriter writer,
                                          final ObjectNode propertiesNode,
                                          final SerializerProvider provider) throws JsonMappingException {
            if (include(writer.getName())) {
                writer.depositSchemaProperty(propertiesNode, provider);
            }
        }

        @Override
        public void depositSchemaProperty(final PropertyWriter writer,
                                          final JsonObjectFormatVisitor objectVisitor,
                                          final SerializerProvider provider) throws JsonMappingException {
            if (include(writer.getName())) {
                writer.depositSchemaProperty(objectVisitor, provider);
            }
        }

        public FilteringPropertyFilter findSubfilter(final String fieldName) {
            return subfilters.get(fieldName);
        }

        public Class getEntityClass() {
            return entityClass;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy