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

org.mapfish.print.parser.OneOfTracker Maven / Gradle / Ivy

package org.mapfish.print.parser;

import com.google.common.collect.Collections2;
import org.locationtech.jts.util.Assert;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;

/**
 * Keeps track of which OneOf groups there are and which ones are satisfied.
 */
final class OneOfTracker {
    private Map mapping = new HashMap<>();

    /**
     * Check if a field is part of a  {@link org.mapfish.print.parser.OneOf} relationship and add if
     * necessary.
     *
     * @param field the field to register.
     */
    public void register(final Field field) {
        final OneOf oneOfAnnotation = field.getAnnotation(OneOf.class);

        String groupName = null;
        if (oneOfAnnotation != null) {
            groupName = oneOfAnnotation.value();
        } else {
            final CanSatisfyOneOf canSatisfyOneOf = field.getAnnotation(CanSatisfyOneOf.class);
            if (canSatisfyOneOf != null) {
                groupName = canSatisfyOneOf.value();
            }
        }

        if (groupName != null) {
            OneOfGroup oneOfGroup = this.mapping.get(groupName);
            if (oneOfGroup == null) {
                oneOfGroup = new OneOfGroup(groupName);
                this.mapping.put(groupName, oneOfGroup);
            }
            oneOfGroup.choices.add(field);
        }
    }

    /**
     * Check if a field is part of a {@link org.mapfish.print.parser.OneOf} relationship and mark the group as
     * satisfied.
     *
     * @param field the field that is done.
     */
    public void markAsVisited(final Field field) {
        final OneOf oneOfAnnotation = field.getAnnotation(OneOf.class);
        if (oneOfAnnotation != null) {
            final OneOfGroup oneOfGroup = this.mapping.get(oneOfAnnotation.value());
            oneOfGroup.satisfiedBy.add(new OneOfSatisfier(field, false));
        }
        final CanSatisfyOneOf canSatisfyOneOf = field.getAnnotation(CanSatisfyOneOf.class);
        if (canSatisfyOneOf != null) {
            final OneOfGroup oneOfGroup = this.mapping.get(canSatisfyOneOf.value());
            oneOfGroup.satisfiedBy.add(new OneOfSatisfier(field, true));
        }
    }

    /**
     * Check that each group is satisfied by one and only one field.
     *
     * @param currentPath the json path to the element being checked
     */
    public void checkAllGroupsSatisfied(final String currentPath) {
        StringBuilder errors = new StringBuilder();

        for (OneOfGroup group: this.mapping.values()) {

            if (group.satisfiedBy.isEmpty()) {
                errors.append("\n");
                errors.append("\t* The OneOf choice: ").append(group.name)
                        .append(" was not satisfied.  One (and only one) of the ");
                errors.append("following fields is required in the request data: ")
                        .append(toNames(group.choices));
            }

            Collection oneOfSatisfiers =
                    Collections2.filter(group.satisfiedBy, input -> !input.isCanSatisfy);
            if (oneOfSatisfiers.size() > 1) {
                errors.append("\n");
                errors.append("\t* The OneOf choice: ").append(group.name)
                        .append(" was satisfied by too many fields.  Only one choice ");
                errors.append("may be in the request data.  The fields found were: ")
                        .append(toNames(toFields(group.satisfiedBy)));
            }
        }

        Assert.equals(0, errors.length(),
                      "\nErrors were detected when analysing the @OneOf dependencies of '" + currentPath +
                              "': \n" + errors);
    }

    private Collection toFields(final Set satisfiedBy) {
        return Collections2.transform(satisfiedBy, (@Nonnull final OneOfSatisfier input) -> input.field);
    }

    private String toNames(final Collection choices) {
        StringBuilder names = new StringBuilder();
        for (Field choice: choices) {
            if (names.length() > 0) {
                names.append(", ");
            }
            String type = choice.getType().getName();
            if (choice.getType().isArray()) {
                type = choice.getType().getComponentType().getName() + "[]";
            }
            names.append(type).append(' ').append(choice.getName());
        }
        return names.toString();
    }

    private static final class OneOfGroup {
        private String name;
        private Collection choices = new ArrayList<>();
        private Set satisfiedBy = new HashSet<>();

        private OneOfGroup(final String name) {
            this.name = name;
        }
    }

    private static final class OneOfSatisfier {
        private final Field field;
        private final boolean isCanSatisfy;

        private OneOfSatisfier(
                @Nonnull final Field field,
                final boolean isCanSatisfy) {
            this.field = field;
            this.isCanSatisfy = isCanSatisfy;
        }

        // CHECKSTYLE:OFF
        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            OneOfSatisfier that = (OneOfSatisfier) o;

            if (!field.equals(that.field)) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            return field.hashCode();
        }
        // CHECKSTYLE:ON
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy