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

cdc.mf.html.MfHtmlGenerationArgs Maven / Gradle / Ivy

The newest version!
package cdc.mf.html;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cdc.issues.Issue;
import cdc.issues.IssueSeverity;
import cdc.issues.io.SnapshotData;
import cdc.issues.locations.Location;
import cdc.issues.rules.Profile;
import cdc.issues.rules.Rule;
import cdc.issues.rules.RuleId;
import cdc.mf.Config;
import cdc.mf.html.index.Index;
import cdc.mf.model.MfElement;
import cdc.mf.model.MfLocation;
import cdc.mf.model.MfModel;
import cdc.mf.model.MfPackage;
import cdc.util.lang.Checks;
import cdc.util.lang.Introspection;

/**
 * Arguments used to configure HTML generation, including images.
 *
 * @author Damien Carbonne
 */
public final class MfHtmlGenerationArgs {
    public static final int DEFAULT_LTOR_THRESHOLD = 25;
    public static final int DEFAULT_LAYOUT_DEPTH = 5;

    /** The base directory for HTML generation. */
    private final File baseDir;
    /** The model for which HTML is generated. */
    private final MfModel model;
    private final SnapshotData snapshot;
    /** The associated issues. */
    private final List issues;
    /** The profile associated to issues. */
    private final Profile profile;
    /** The left to right threshold. */
    private final int ltorThreshold;
    /** The maximum depth for layout. */
    private final int layoutDepth;
    /** The generation boolean hints. */
    private final Set hints;
    /** The packages that are ignored in model overview image. */
    private final Predicate modelOverviewIgnoredPackages;
    /** The pattern of name parts that must be removed. */
    private final List nameRemovedParts = new ArrayList<>();
    /** The list of description formatters. */
    private final List> descriptionFormatters = new ArrayList<>();

    private final Map> elementToIssues = new HashMap<>();
    private final Map issueToNumber = new HashMap<>();
    private final Map> ruleIdToIssues = new HashMap<>();
    /** Cache of local worst severity. */
    private final Map worstSeverity = new HashMap<>();
    /** Cache of deep worst severity. */
    private final Map worstDeepSeverity = new HashMap<>();
    private final Index index;

    private MfHtmlGenerationArgs(Builder builder) {
        this.baseDir = Checks.isNotNull(builder.baseDir, "baseDir");
        this.model = Checks.isNotNull(builder.model, "model");
        this.snapshot = builder.snapshot;
        this.issues = Checks.isNotNull(builder.issues, "issues");
        this.profile = builder.profile;
        this.ltorThreshold = builder.ltorThreshold;
        this.layoutDepth = builder.layoutDepth;
        this.hints = builder.hints;
        this.modelOverviewIgnoredPackages = pack -> {
            for (final Pattern p : builder.modelOverviewIgnoredPackages) {
                if (p.matcher(pack.getQName().toStringSlash()).matches()) {
                    return true;
                }
            }
            return false;
        };
        this.nameRemovedParts.addAll(builder.nameRemovedParts);

        this.descriptionFormatters.addAll(builder.descriptionFormatters);

        // Associate issues to elements, rules, and number them
        int number = 0;
        for (final Issue issue : issues) {
            number++;
            issueToNumber.put(issue, number);
            for (final Location location : issue.getLocations()) {
                if (location instanceof final MfLocation mflocation) {
                    final MfElement ref = mflocation.resolve(model).orElse(model);
                    final List list = elementToIssues.computeIfAbsent(ref, k -> new ArrayList<>());
                    list.add(issue);
                }
            }
            final List list = ruleIdToIssues.computeIfAbsent(issue.getRuleId(), k -> new ArrayList<>());
            list.add(issue);
        }

        this.index = new Index(this.model);
    }

    public File getBaseDir() {
        return baseDir;
    }

    public MfModel getModel() {
        return model;
    }

    public SnapshotData getSnapshot() {
        return snapshot;
    }

    public List getIssues() {
        return issues;
    }

    public Profile getProfile() {
        return profile;
    }

    public Index getIndex() {
        return index;
    }

    @SuppressWarnings("static-method")
    public String getConfigVersion() {
        return Config.VERSION;
    }

    /**
     * @param element The element.
     * @return The issues directly associated to {@code element}.
     */
    public List getIssues(MfElement element) {
        return elementToIssues.getOrDefault(element, Collections.emptyList());
    }

    private static IssueSeverity max(IssueSeverity x,
                                     IssueSeverity y) {
        if (x == null) {
            return y;
        } else if (y == null) {
            return x;
        } else {
            return IssueSeverity.max(x, y);
        }
    }

    public IssueSeverity getWorstIssueSeverity(MfElement element) {
        if (!worstSeverity.containsKey(element)) {
            IssueSeverity max = null;
            for (final Issue issue : getIssues(element)) {
                max = max(max, issue.getSeverity());
            }
            worstSeverity.put(element, max);
        }
        return worstSeverity.get(element);
    }

    /**
     * @param issue The issue.
     * @return The number associated to {@code issue}.
     */
    public int getNumber(Issue issue) {
        return issueToNumber.getOrDefault(issue, -1);
    }

    /**
     * @param element The element.
     * @return {@code true} if {@code element} or one of its children, recursively, has issues.
     */
    public boolean hasDeepIssues(MfElement element) {
        if (!getIssues(element).isEmpty()) {
            return true;
        } else {
            for (final MfElement child : element.getChildren()) {
                if (hasDeepIssues(child)) {
                    return true;
                }
            }
            return false;
        }
    }

    public IssueSeverity getWorstDeepIssueSeverity(MfElement element) {
        if (!worstDeepSeverity.containsKey(element)) {
            IssueSeverity max = getWorstIssueSeverity(element);
            for (final MfElement child : element.getChildren()) {
                max = max(max, getWorstDeepIssueSeverity(child));
            }
            worstDeepSeverity.put(element, max);
        }
        return worstDeepSeverity.get(element);
    }

    public List getIssues(Rule rule) {
        return ruleIdToIssues.getOrDefault(rule.getId(), Collections.emptyList());
    }

    public boolean hasIssues(Rule rule) {
        return ruleIdToIssues.containsKey(rule.getId());
    }

    public int getLtorThreshold() {
        return ltorThreshold;
    }

    public int getLayoutDepth() {
        return layoutDepth;
    }

    public Set getHints() {
        return hints;
    }

    public Predicate getModelOverviewIgnoredPackages() {
        return modelOverviewIgnoredPackages;
    }

    public String simplifyName(String name) {
        if (nameRemovedParts.isEmpty()) {
            return name;
        } else {
            String s = name;
            for (final Pattern pattern : nameRemovedParts) {
                final Matcher m = pattern.matcher(s);
                s = m.replaceAll("");
            }
            return s;
        }
    }

    public List> getDescriptionFormatters() {
        return descriptionFormatters;
    }

    public String formatDescription(String s) {
        if (s == null) {
            return null;
        } else if (descriptionFormatters.isEmpty()) {
            return s;
        } else {
            for (final UnaryOperator formatter : descriptionFormatters) {
                s = formatter.apply(s);
            }
            return s;
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private File baseDir;
        private MfModel model;
        private SnapshotData snapshot;
        private final List issues = new ArrayList<>();
        private Profile profile;
        private int ltorThreshold = DEFAULT_LTOR_THRESHOLD;
        private int layoutDepth = DEFAULT_LAYOUT_DEPTH;
        private final Set hints = EnumSet.noneOf(MfHtmlGenerationHint.class);
        private final List modelOverviewIgnoredPackages = new ArrayList<>();
        private final List nameRemovedParts = new ArrayList<>();
        private final List> descriptionFormatters = new ArrayList<>();

        protected Builder() {
        }

        public Builder baseDir(File baseDir) {
            this.baseDir = baseDir;
            return this;
        }

        public Builder model(MfModel model) {
            this.model = model;
            return this;
        }

        public Builder snapshot(SnapshotData snapshot) {
            this.snapshot = snapshot;
            return this;
        }

        public Builder issues(List issues) {
            this.issues.addAll(issues);
            return this;
        }

        public Builder profile(Profile profile) {
            this.profile = profile;
            return this;
        }

        public Builder ltorThreshold(int ltorThreshold) {
            this.ltorThreshold = ltorThreshold;
            return this;
        }

        public Builder layoutDepth(int layoutDepth) {
            this.layoutDepth = layoutDepth;
            return this;
        }

        public Builder hint(MfHtmlGenerationHint hint) {
            this.hints.add(hint);
            return this;
        }

        public Builder hint(MfHtmlGenerationHint hint,
                            boolean enabled) {
            if (enabled) {
                this.hints.add(hint);
            } else {
                this.hints.remove(hint);
            }
            return this;
        }

        public Builder modelOverviewIgnoredPackage(Pattern pattern) {
            this.modelOverviewIgnoredPackages.add(pattern);
            return this;
        }

        public Builder modelOverviewIgnoredPackage(String pattern) {
            return modelOverviewIgnoredPackage(Pattern.compile(pattern));
        }

        public Builder modelOverviewIgnoredPackages(List patterns) {
            this.modelOverviewIgnoredPackages.addAll(patterns);
            return this;
        }

        public Builder nameRemovedPart(Pattern pattern) {
            this.nameRemovedParts.add(pattern);
            return this;
        }

        public Builder nameRemovedPart(String pattern) {
            return nameRemovedPart(Pattern.compile(pattern));
        }

        public Builder nameRemovedParts(List patterns) {
            this.nameRemovedParts.addAll(patterns);
            return this;
        }

        public Builder descriptionFormatter(UnaryOperator formatter) {
            this.descriptionFormatters.add(formatter);
            return this;
        }

        public Builder descriptionFormatters(List> formatters) {
            this.descriptionFormatters.addAll(formatters);
            return this;
        }

        public static UnaryOperator getFormatter(String name) {
            final int pos = name.lastIndexOf('.');
            try {
                final String className = name.substring(0, pos);
                final String methodName = name.substring(pos + 1);
                final Class cls = Introspection.getClass(className);
                if (cls != null) {
                    final Method method = Introspection.getMethod(cls, methodName, String.class);
                    if (method != null
                            && method.getReturnType().equals(String.class)
                            && Modifier.isStatic(method.getModifiers())) {
                        return s -> {
                            try {
                                return String.class.cast(method.invoke(null, s));
                            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                                return s;
                            }
                        };
                    }
                }
            } catch (final RuntimeException e) {
                // Ignore
            }
            return null;
        }

        public Builder descriptionFormatter(String formatterQName) {
            final UnaryOperator formatter = getFormatter(formatterQName);
            if (formatter != null) {
                descriptionFormatters.add(formatter);
            }
            return this;
        }

        public MfHtmlGenerationArgs build() {
            return new MfHtmlGenerationArgs(this);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy