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 extends Issue> 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