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

fr.ird.observe.maven.plugins.toolbox.ValidatorsCache Maven / Gradle / Ivy

There is a newer version: 4.34
Show newest version
package fr.ird.observe.maven.plugins.toolbox;

/*-
 * #%L
 * ObServe Toolkit :: Maven plugin
 * %%
 * Copyright (C) 2017 - 2020 IRD, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import fr.ird.observe.validation.ValidatorDto;
import io.ultreia.java4all.lang.Objects2;
import org.apache.maven.plugin.logging.Log;
import org.nuiton.validator.NuitonValidatorScope;
import org.nuiton.validator.bean.simple.SimpleBeanValidator;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * Created on 31/08/16.
 *
 * @author Tony Chemit - [email protected]
 */
public class ValidatorsCache {

    private final static ValidatorsCache instance = new ValidatorsCache();
    private final Multimap validators = HashMultimap.create();

    public static ValidatorsCache get() {
        return instance;
    }

    public synchronized Collection getValidators(ValidatorCacheRequest request) throws IOException {

        Log log = request.getLog();
        boolean verbose = request.isVerbose();
        Path sourceRootPath = request.getSourceRootPath();

        List result = new LinkedList<>();

        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(request.getUrlClassLoader());
            Map, Class> extraClassMapping = request.getExtraClassMapping();
            if (extraClassMapping != null) {
                validators.clear();
            }
            Collection source = loadValidators(log, verbose, extraClassMapping, sourceRootPath);
            log.info("Detects " + source.size() + " validator(s) from source directory.");
            result.addAll(source);
            if (request.getExtraSourceRootPath().isPresent()) {
                Collection generated = loadValidators(log, verbose, extraClassMapping, request.getExtraSourceRootPath().get());
                log.info("Detects " + generated.size() + " validator(s) from generated directory.");
                Collection merge = merge(source, generated, extraClassMapping);
                log.info("Merge " + merge.size() + " validator(s).");
                result.addAll(merge);
            }
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
        log.info("Found " + result.size() + " validator(s).");
        return result;
    }

    private Collection loadValidators(Log log, boolean verbose, Map, Class> extraClassMapping, Path sourceRootPath) {
        String key = sourceRootPath.toFile().getAbsolutePath();
        if (!validators.containsKey(key)) {

            log.info("Loading validators from " + sourceRootPath);

            PathSimpleFileVisitorResult result;
            try {
                result = new PathSimpleFileVisitor(log, sourceRootPath, verbose).walk();
            } catch (IOException e) {
                throw new RuntimeException("Can't walk through directory: " + sourceRootPath, e);
            }

            NuitonValidatorScope[] scopes = result.getEffectiveScopes();

            for (ValidatorDescriptor validatorDescriptor : result.descritptors) {

                String typeName = validatorDescriptor.getTypeName();
                String context = validatorDescriptor.getContext();

                Class type = Objects2.forName(typeName);
                if (extraClassMapping != null) {

                    Class type2 = extraClassMapping.get(type);
                    if (type2 == null) {
                        log.warn("Can't find type for type: " + type.getName());
                        type2 = type;
                    }
                    type = type2;
                }
                SimpleBeanValidator validator = SimpleBeanValidator.newValidator(type, context, scopes);
                for (NuitonValidatorScope scope : validator.getEffectiveScopes()) {
                    Set effectiveFields = validator.getEffectiveFields(scope);
                    ValidatorInfo validatorInfo = new ValidatorInfo(type, context, scope, new LinkedHashSet<>(effectiveFields.stream().sorted().collect(Collectors.toList())));
                    validators.put(key, validatorInfo);
                }
            }
        }
        Collection validatorInfos = validators.get(key);
        log.debug("Found " + validatorInfos.size() + " validator(s).");
        return validatorInfos;
    }

    private Collection merge(Collection validatorInfos, Collection generatedValidatorInfos, Map, Class> extraClassMapping) {
        List result = new LinkedList<>();
        if (extraClassMapping == null) {
            for (ValidatorInfo validatorInfo : validatorInfos) {
                ValidatorInfo merge = merge(validatorInfo, generatedValidatorInfos);
                result.add(merge);
            }
            return result;
        }
        ArrayListMultimap, ValidatorInfo> allInfos = ArrayListMultimap.create();
        for (ValidatorInfo validatorInfo : validatorInfos) {
            allInfos.put(validatorInfo.getType(), validatorInfo);
        }
        for (ValidatorInfo validatorInfo : generatedValidatorInfos) {
            allInfos.put(validatorInfo.getType(), validatorInfo);
        }
        ArrayListMultimap infosByKey = ArrayListMultimap.create();
        for (Map.Entry, Collection> entry : allInfos.asMap().entrySet()) {
            String prefix = entry.getKey().getName();
            entry.getValue().forEach(i -> infosByKey.put(prefix + Objects.requireNonNull(i).getScope() + i.getContext(), i));
        }
        for (Collection infos : infosByKey.asMap().values()) {
            ValidatorInfo merge = merge(infos);
            result.add(merge);
        }
        return result;
    }

    private ValidatorInfo merge(ValidatorInfo source, Collection generatedValidatorInfos) {
        String context = source.getContext();
        Class type = source.getType();
        NuitonValidatorScope scope = source.getScope();
        Set fields = new TreeSet<>(source.getFields());
        for (ValidatorInfo validatorInfo : generatedValidatorInfos.stream().filter(v -> scope.equals(v.getScope()) && v.getType().isAssignableFrom(type) && context.equals(v.getContext())).collect(Collectors.toList())) {
            fields.addAll(validatorInfo.getFields());
        }
        return new ValidatorInfo(type, context, scope, fields);
    }

    private ValidatorInfo merge(Collection infos) {
        ValidatorInfo source = infos.iterator().next();
        String context = source.getContext();
        Class type = source.getType();
        NuitonValidatorScope scope = source.getScope();
        Set fields = new TreeSet<>(source.getFields());
        for (ValidatorInfo validatorInfo : infos) {
            fields.addAll(validatorInfo.getFields());
        }
        return new ValidatorInfo(type, context, scope, fields);
    }

    public static class ValidatorInfo {

        private final Class type;
        private final String context;
        private final NuitonValidatorScope scope;
        private final Set fields;

        ValidatorInfo(Class type, String context, NuitonValidatorScope scope, Set fields) {
            this.type = type;
            this.context = context;
            this.scope = scope;
            this.fields = fields;
        }

        public String getOrderKey() {
            return type.getName() + "-" + context + "-" + scope;
        }

        public Class getType() {
            return type;
        }

        public String getContext() {
            return context;
        }

        public NuitonValidatorScope getScope() {
            return scope;
        }

        public Set getFields() {
            return fields;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ValidatorInfo that = (ValidatorInfo) o;
            return Objects.equals(type, that.type) &&
                    Objects.equals(context, that.context) &&
                    scope == that.scope;
        }

        @Override
        public int hashCode() {
            return Objects.hash(type, context, scope);
        }

        @SuppressWarnings("unchecked")
        public ValidatorDto toValidatorDto() {
            return new ValidatorDto(
                    (Class) type,
                    scope,
                    context,
                    fields
            );
        }
    }

    private static class PathSimpleFileVisitorResult {

        private final Set descritptors;

        private PathSimpleFileVisitorResult(Set descritptors) {
            this.descritptors = descritptors;
        }


        public NuitonValidatorScope[] getEffectiveScopes() {

            EnumSet result = EnumSet.noneOf(NuitonValidatorScope.class);
            result.addAll(descritptors.stream().map(ValidatorDescriptor::getScope).collect(Collectors.toList()));
            return result.toArray(new NuitonValidatorScope[0]);
        }

    }

    private static class ValidatorDescriptor {

        private final String typeName;
        private final String context;
        private final NuitonValidatorScope scope;

        private ValidatorDescriptor(String typeName, String context, NuitonValidatorScope scope) {
            this.typeName = typeName;
            this.context = context;
            this.scope = scope;
        }

        public String getTypeName() {
            return typeName;
        }

        public String getContext() {
            return context;
        }

        public NuitonValidatorScope getScope() {
            return scope;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ValidatorDescriptor that = (ValidatorDescriptor) o;
            return Objects.equals(getTypeName(), that.getTypeName()) &&
                    Objects.equals(getContext(), that.getContext()) &&
                    getScope() == that.getScope();
        }

        @Override
        public int hashCode() {
            return Objects.hash(getTypeName(), getContext(), getScope());
        }
    }

    private static class PathSimpleFileVisitor extends SimpleFileVisitor {

        private final Set descritptors = new LinkedHashSet<>();
        private final Set scopes = new LinkedHashSet<>();
        private final Log log;
        private final Path sourceRootPath;
        private final boolean verbose;

        private String packageName = "";

        PathSimpleFileVisitor(Log log, Path sourceRootPath, boolean verbose) {

            this.log = log;
            this.sourceRootPath = sourceRootPath;
            this.verbose = verbose;

            for (NuitonValidatorScope scope : NuitonValidatorScope.values()) {
                scopes.add(scope.name().toLowerCase());
            }
        }

        public PathSimpleFileVisitorResult walk() throws IOException {

            Files.walkFileTree(sourceRootPath, this);

            log.info(String.format("Found %d validator context(s).", descritptors.size()));
            return new PathSimpleFileVisitorResult(descritptors);
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
            if (!dir.equals(sourceRootPath)) {
                if (!packageName.isEmpty()) {
                    packageName += ".";
                }
                packageName += dir.toFile().getName();
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
            if (!dir.equals(sourceRootPath)) {
                String name = dir.toFile().getName();
                packageName = packageName.substring(0, packageName.length() - name.length());
                if (packageName.endsWith(".")) {
                    packageName = packageName.substring(0, packageName.length() - 1);
                }
            }
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            String name = file.toFile().getName();
            if (name.endsWith("-validation.xml")) {
                int i = name.indexOf('-');
                String typeName = packageName + "." + name.substring(0, i);
                String rest = name.substring(i);
                LinkedHashSet contexts = new LinkedHashSet<>();
                StringTokenizer tok = new StringTokenizer(rest, "-");
                NuitonValidatorScope scope = null;
                while (tok.hasMoreTokens()) {

                    String token = tok.nextToken();
                    if (scopes.contains(token)) {
                        scope = NuitonValidatorScope.valueOf(token.toUpperCase());
                        break;
                    }
                    contexts.add(token);
                }
                String context = String.join("-", contexts);
                ValidatorDescriptor descritptor = new ValidatorDescriptor(typeName, context, scope);

                boolean add = descritptors.add(descritptor);
                if (add) {
                    if (verbose) {
                        log.info("Register " + typeName);
                    }
                }
            }
            return FileVisitResult.CONTINUE;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy