cdc.mf.html.MfParams Maven / Gradle / Ivy
package cdc.mf.html;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.text.StringEscapeUtils;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
import cdc.graphs.impl.BasicGraphEdge;
import cdc.issues.Issue;
import cdc.issues.Meta;
import cdc.issues.rules.Rule;
import cdc.issues.rules.RuleId;
import cdc.mf.html.index.IndexSection;
import cdc.mf.model.MfClass;
import cdc.mf.model.MfConnector;
import cdc.mf.model.MfConstraint;
import cdc.mf.model.MfElement;
import cdc.mf.model.MfElementRef;
import cdc.mf.model.MfEnumeration;
import cdc.mf.model.MfInterface;
import cdc.mf.model.MfLocation;
import cdc.mf.model.MfLocationPart;
import cdc.mf.model.MfModel;
import cdc.mf.model.MfNameItem;
import cdc.mf.model.MfPackage;
import cdc.mf.model.MfQNameItem;
import cdc.mf.model.MfType;
import cdc.mf.model.MfUtils;
import cdc.util.strings.StringUtils;
public final class MfParams {
private MfParams() {
}
private static final Pattern CMAPX_FIX1 = Pattern.compile(" id=\"[^\"]+\"");
private static final Pattern CMAPX_FIX2 = Pattern.compile("\"/>");
private static final Map, String> PUML_KIND = new HashMap<>();
static {
PUML_KIND.put(MfClass.class, "class");
PUML_KIND.put(MfInterface.class, "interface");
PUML_KIND.put(MfEnumeration.class, "enum");
PUML_KIND.put(MfPackage.class, "package");
}
public static final String DEFAULT_VALID_STRING = "?";
public static List getMetas(MfElement element) {
return element.getMetas().getSortedMetas();
}
public static String validate(String s) {
return StringUtils.isNullOrEmpty(s) ? DEFAULT_VALID_STRING : s;
}
public static List sort(Collection extends X> collection,
Comparator super X> comparator) {
return collection.stream()
.sorted(comparator)
.collect(Collectors.toList());
}
public static String getHtmlId(MfElement element) {
final StringBuilder builder = new StringBuilder();
builder.append(getKind(element));
for (final int index : element.getIndices()) {
builder.append('.');
builder.append(index);
}
return builder.toString();
}
public static String getHtmlId(Issue issue,
MfHtmlGenerationArgs args) {
return "issue." + args.getNumber(issue);
}
public static String getHtmlId(Rule rule) {
return getHtmlId(rule.getId());
}
public static String getHtmlId(RuleId ruleId) {
return "rule." + toHashString(ruleId.toString());
}
public static String getId(Issue issue) {
return "I@" + toHashString(issue.getId().toString());
}
public static String getId(Rule rule) {
return "R@" + toHashString(rule.getId().toString());
}
public static String getHtmlId(IndexSection section) {
return "index." + section.getNumber();
}
public static String getDisplayName(MfElement element) {
if (element instanceof final MfType x) {
return x.getTypePath().toString().replace('/', '$');
} else if (element instanceof final MfNameItem x) {
return validate(x.getName());
} else {
return DEFAULT_VALID_STRING;
}
}
public static String getKind(MfElement element) {
return MfUtils.getKind(element);
}
public static String getPumlKind(MfElement element) {
return PUML_KIND.get(element.getClass());
}
public static String toHashString(Object object) {
return Integer.toHexString(object.hashCode());
}
private static String toHtmlSpan(String cl,
String text) {
return "" + text + "";
}
private static String toHtmlLocationPath(MfLocation location) {
final StringBuilder builder = new StringBuilder();
final List parts = location.getParts();
for (int i = 0; i < parts.size(); i++) {
final MfLocationPart part = parts.get(i);
if (i > 0) {
builder.append("/");
}
if (i == parts.size() - 1) {
builder.append(toHtmlSpan("location-part, location-part-last", part.toString()));
} else {
builder.append(toHtmlSpan("location-part", part.toString()));
}
}
return builder.toString();
}
private static String toHtmlLocationText(MfLocation location) {
final StringBuilder builder = new StringBuilder();
builder.append(toHtmlSpan("location-path", toHtmlLocationPath(location)));
if (location.hasAnchor()) {
builder.append(toHtmlSpan("location-separator", "::"));
builder.append(toHtmlSpan("location-anchor", location.getAnchor()));
}
return builder.toString();
}
private static String toHtmlA(String href,
String target,
String text) {
final StringBuilder builder = new StringBuilder();
builder.append("')
.append(text)
.append("");
return builder.toString();
}
private static String getTarget(MfHtmlGenerationArgs args) {
if (args.getHints().contains(MfHtmlGenerationHint.SINGLE_PAGE)) {
return null;
} else {
return "rightFrame";
}
}
private static String toHtmlRef(MfLocation location,
String upPath,
MfHtmlGenerationArgs args) {
final MfElement element = location.resolve(args.getModel()).orElse(null);
if (element == null) {
return toHtmlLocationText(location);
} else {
return toHtmlA(toItemHRef(element, upPath, args),
getTarget(args),
toHtmlLocationText(location));
}
}
private static String replaceLocations(String s,
String upPath,
MfHtmlGenerationArgs args) {
final Matcher m = MfLocation.PATH_PATTERN.matcher(s);
return m.replaceAll(r -> toHtmlRef(MfLocation.of(r.group(), null), upPath, args));
}
private static String escapeSpecial(String s) {
if (s == null) {
return s;
} else {
return s.replace("*", "\\*");
}
}
public static String toHtml(String text,
MfHtmlGenerationArgs args) {
if (text == null) {
return null;
} else {
final MutableDataSet options = new MutableDataSet();
options.set(HtmlRenderer.SOFT_BREAK, "
\n");
final Parser parser = Parser.builder(options).build();
final HtmlRenderer renderer = HtmlRenderer.builder(options).build();
text = args.formatDescription(text);
text = StringEscapeUtils.escapeHtml4(text);
text = escapeSpecial(text);
final Node document = parser.parse(text);
return renderer.render(document);
}
}
public static String toHtml(String text,
String upPath,
MfHtmlGenerationArgs args) {
final String html = toHtml(text, args);
return replaceLocations(html, upPath, args);
}
public static String toString(File file) {
return file == null ? "???" : file.getPath().replace('\\', '/');
}
public static String toUrl(File file) {
// try {
// // TODO encode each name individually
// return URLEncoder.encode(toString(file), "UTF-8");
// } catch (final UnsupportedEncodingException e) {
// return toString(file);
// }
return toString(file).replace(" ", "%20");
}
public static String toItemRef(MfElement element,
MfHtmlGenerationArgs args) {
// The down path
if (args.getHints().contains(MfHtmlGenerationHint.SINGLE_PAGE)) {
return "#" + getHtmlId(element);
} else {
return toString(Files.getHtmlItemFile(element, args)) + "#" + getHtmlId(element);
}
}
public static String toItemHRef(MfElement element,
String upPath,
MfHtmlGenerationArgs args) {
if (args.getHints().contains(MfHtmlGenerationHint.SINGLE_PAGE)) {
return toItemRef(element, args);
} else {
return upPath + "/" + toItemRef(element, args);
}
}
public static String toItemUrl(MfElement element,
MfHtmlGenerationArgs args) {
// The down path
if (args.getHints().contains(MfHtmlGenerationHint.SINGLE_PAGE)) {
return "#" + getHtmlId(element);
} else {
return toUrl(Files.getHtmlItemFile(element, args)) + "#" + getHtmlId(element);
}
}
public static String getUpPath(MfElement element,
MfHtmlGenerationArgs args) {
// The up path
if (args.getHints().contains(MfHtmlGenerationHint.SINGLE_PAGE)) {
return ".";
} else {
return MfParams.toString(MfParams.Dirs.getUpPath(element, args));
}
}
public static String getPumlId(MfElement element) {
final String id = element.getId();
final String result;
if (id == null) {
if (element instanceof final MfQNameItem x) {
result = toHashString(x.getQName().toStringSlash());
} else {
final String parentPumlId = getPumlId(element.getParent());
return parentPumlId + "_" + element.getIndex();
}
} else {
result = toHashString(id);
}
return result;
}
private static boolean isDisplayed(MfElementRef ref) {
return ref.isValid()
&& ref.getElement() instanceof MfType;
}
public static List> getDisplayedConstrainedElementRefs(MfConstraint constraint) {
return constraint.getConstrainedElementRefs()
.stream()
.filter(MfParams::isDisplayed)
.toList();
}
public static List> getLayoutLinks(MfElement element) {
final List> list = new ArrayList<>();
if (element instanceof final MfPackage p) {
final List sub = p.getPackages();
final int s = (int) Math.ceil(Math.sqrt(sub.size()));
for (int to = 1; to < sub.size(); to++) {
if (to % s != 0) {
list.add(new BasicGraphEdge<>(sub.get(to - 1), sub.get(to)));
}
}
}
return list;
}
/**
* @param pack The package.
* @param args The generations arguments.
* @return The number of items to be displayed in the overview image of package {@code pack}.
*/
public static int getItemsCount(MfPackage pack,
MfHtmlGenerationArgs args) {
return pack.getTypes().size() + getExternalTypes(pack, args).size();
}
/**
* @param type The type.
* @param args The generations arguments.
* @return The number of items to be displayed in the overview image of {@code type}.
*/
public static int getItemsCount(MfType type,
MfHtmlGenerationArgs args) {
return 1 + getRelatedTypes(type, args).size();
}
public static List getExternalTypes(MfPackage pack,
MfHtmlGenerationArgs args) {
final Set set = new HashSet<>();
for (final MfType type : pack.getTypes()) {
for (final MfType ancestor : type.getDirectGenitors()) {
if (ancestor.getRootType().getOwningPackage() != pack) {
set.add(ancestor);
}
}
if (args.getHints().contains(MfHtmlGenerationHint.IMG_PACKAGE_OVERVIEW_SHOW_EXTERNAL_IMPLEMENTATIONS)) {
for (final MfType ancestor : type.getDirectProGenitors()) {
if (ancestor.getRootType().getOwningPackage() != pack) {
set.add(ancestor);
}
}
}
for (final MfConnector connector : type.getConnectors()) {
if (connector.getTargetTip().getTypeRef().get().getRootType().getOwningPackage() != pack) {
set.add(connector.getTargetTip().getTypeRef().get());
}
}
}
return sort(set, MfQNameItem.QNAME_COMPARATOR);
}
public static List getRelatedTypes(MfType type,
MfHtmlGenerationArgs args) {
final Set set = new HashSet<>();
for (final MfType ancestor : type.getDirectGenitors()) {
set.add(ancestor);
}
for (final MfType ancestor : type.getDirectProGenitors()) {
set.add(ancestor);
}
for (final MfType descendant : type.getDirectSpecializations()) {
set.add(descendant);
}
for (final MfConnector connector : type.getConnectors()) {
set.add(connector.getTargetTip().getTypeRef().get());
}
for (final MfConnector connector : type.getReversedConnectors()) {
set.add(connector.getSourceTip().getTypeRef().get());
}
set.remove(type);
return sort(set, MfQNameItem.QNAME_COMPARATOR);
}
public static File getCmapxFile(File basedir,
String prefix,
MfElement element) {
return new File(basedir, Dirs.IMAGES.getPath() + "/" + prefix + getPumlId(element) + ".cmapx");
}
public static boolean hasCmapx(File basedir,
String prefix,
MfElement element) {
final File file = getCmapxFile(basedir, prefix, element);
return file.exists();
}
public static String loadCmapx(File basedir,
String prefix,
MfElement element) {
final File file = getCmapxFile(basedir, prefix, element);
try {
String s = java.nio.file.Files.readString(file.toPath());
s = CMAPX_FIX1.matcher(s).replaceAll("");
s = CMAPX_FIX2.matcher(s).replaceAll("\">");
return s;
} catch (final IOException e) {
return null;
}
}
/**
* Files of images, packages, items, ...
*/
public static final class Files {
private Files() {
}
private static final File HTML_ALL_PACKAGES = new File("all-packages.html");
private static final File HTML_ALL_ITEMS = new File("all-items.html");
private static final File HTML_FRAMES = new File("index.html");
private static final File HTML_ISSUES = new File("issues.html");
private static final File HTML_OVERVIEW = new File("overview.html");
private static final File HTML_PROFILE = new File("profile.html");
private static final File HTML_SINGLE = new File("single.html");
public static File getHtmlSinglePageFile() {
return HTML_SINGLE;
}
/**
* @return The file used for the HTML frame listing all packages.
*/
public static File getHtmlAllPackagesFile() {
return HTML_ALL_PACKAGES;
}
/**
* @return The file used for the HTML frame listing all items.
*/
public static File getHtmlAllItemsFile() {
return HTML_ALL_ITEMS;
}
/**
* @return The file used for the HTML index.
*/
public static File getHtmlFramesFile() {
return HTML_FRAMES;
}
/**
* @return The file used for the model issues HTML frame.
*/
public static File getHtmlIssuesFile() {
return HTML_ISSUES;
}
/**
* @return The file used for the model profile HTML frame.
*/
public static File getHtmlProfileFile() {
return HTML_PROFILE;
}
/**
* @return The file used for the model overview HTML frame.
*/
public static File getHtmlOverviewFile() {
return HTML_OVERVIEW;
}
/**
* @param item The model.
* @param type The image type.
* @return The file used for the model overview image.
*/
public static File getImgOverviewFile(MfModel item,
ImageType type) {
return new File(Dirs.IMAGES.getPath(),
"model-overview-"
+ getPumlId(item)
+ "."
+ type.getExt());
}
/**
* @param pack The package.
* @param args The generation arguments.
* @return The file used for the frame listing items of the {@code pack} package.
*/
public static File getHtmlPackageItemsFile(MfPackage pack,
MfHtmlGenerationArgs args) {
return new File(Dirs.getPackageDir(pack, args),
"package-items.html");
}
/**
* @param pack The package.
* @param args The generation arguments.
* @return The file used for the {@code pack} package overview frame.
*/
public static File getHtmlPackageOverviewFile(MfPackage pack,
MfHtmlGenerationArgs args) {
return new File(Dirs.getPackageDir(pack, args),
"package-overview.html");
}
/**
* @param item The package.
* @param type The image type.
* @return The file used for the {@code pack} package overview image.
*/
public static File getImgPackageOverviewFile(MfPackage item,
ImageType type) {
return new File(Dirs.IMAGES.getPath(),
"package-overview-"
+ getPumlId(item)
+ "."
+ type.getExt());
}
public static File getImgTypeOverviewFile(MfType item,
ImageType type) {
return new File(Dirs.IMAGES.getPath(),
"type-overview-"
+ getPumlId(item)
+ "."
+ type.getExt());
}
/**
* @param element The element.
* @param args The generation arguments.
* @return The file used for the {@code element} overview frame.
*/
public static File getHtmlItemFile(MfElement element,
MfHtmlGenerationArgs args) {
if (element instanceof final MfPackage e) {
return getHtmlPackageOverviewFile(e, args);
} else if (element instanceof MfModel) {
return HTML_OVERVIEW;
} else if (element instanceof final MfType e) {
return new File(Dirs.getPackageDir(e.getRootType().getOwningPackage(), args),
getDisplayName(element) + ".html");
} else {
// Recurse on parent
return getHtmlItemFile(element.getParent(), args);
}
}
public static File getHtmlIndexFile(char key,
MfHtmlGenerationArgs args) {
final IndexSection section = args.getIndex().getSection(key);
return new File(Dirs.INDEX, "index-" + section.getNumber() + ".html");
}
}
/**
* Names of parameters passed to ST4 templates.
*/
public static final class Names {
private Names() {
}
public static final String ARGS = "args";
public static final String CLASSES = "classes";
public static final String ENUMERATIONS = "enumerations";
public static final String HINTS = "hints";
public static final String INTERFACES = "interfaces";
public static final String ISSUES = "issues";
public static final String LTOR = "ltor";
public static final String MODEL = "model";
public static final String PACKAGE = "package";
public static final String PROFILE = "profile";
public static final String SECTION = "section";
public static final String SNAPSHOT = "snapshot";
public static final String TYPE = "type";
}
/**
* Directories containing images, packages and items.
*/
public static final class Dirs {
private Dirs() {
}
/** Relative directory containing images. */
public static final File IMAGES = new File("images");
/** Relative root directory where data related to packages are placed. */
public static final File PACKAGES = new File("packages");
/** Relative directory where index files are placed. */
public static final File INDEX = new File("index-files");
private static final File IMAGES_UP_PATH = new File("..");
/**
*
* @param pack The package.
* @param args The generation arguments.
* @return The relative directory used for {@code pack}.
*/
public static File getPackageDir(MfPackage pack,
MfHtmlGenerationArgs args) {
if (args.getHints().contains(MfHtmlGenerationHint.FLAT_DIRS)) {
return new File(PACKAGES.getPath(),
toHashString(pack.getQName().toRelative().toStringSlash()));
} else {
return new File(PACKAGES.getPath(),
pack.getQName().toRelative().toString());
}
}
/**
* @param element The element.
* @param args The generation arguments.
* @return The up path corresponding to the HTML file of {@code element}.
*/
public static File getUpPath(MfElement element,
MfHtmlGenerationArgs args) {
final File file = Files.getHtmlItemFile(element, args);
if (file == null) {
return null;
} else {
final StringBuilder builder = new StringBuilder();
File index = file.getParentFile();
while (index != null) {
if (builder.length() > 0) {
builder.append('/');
}
builder.append("..");
index = index.getParentFile();
}
if (builder.length() == 0) {
builder.append('.');
}
return new File(builder.toString());
}
}
public static File getImgUpPath(MfElement element,
MfHtmlGenerationArgs args) {
return IMAGES_UP_PATH;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy