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

cdc.mf.html.adaptors.MfElementAdaptor Maven / Gradle / Ivy

There is a newer version: 0.41.0
Show newest version
package cdc.mf.html.adaptors;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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.stringtemplate.v4.Interpreter;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.misc.ObjectModelAdaptor;
import org.stringtemplate.v4.misc.STNoSuchPropertyException;

import cdc.args.Strictness;
import cdc.graphs.EdgeDirection;
import cdc.graphs.impl.BasicGraphEdge;
import cdc.graphs.impl.BasicSuperLightGraph;
import cdc.mf.Config;
import cdc.mf.html.MfHtmlGenerationArgs;
import cdc.mf.html.MfHtmlGenerationHint;
import cdc.mf.html.MfParams;
import cdc.mf.model.MfAbstractItem;
import cdc.mf.model.MfAggregation;
import cdc.mf.model.MfAnnotation;
import cdc.mf.model.MfAssociation;
import cdc.mf.model.MfCardinalityItem;
import cdc.mf.model.MfClass;
import cdc.mf.model.MfComposition;
import cdc.mf.model.MfConnector;
import cdc.mf.model.MfConnectorOwner;
import cdc.mf.model.MfConstraint;
import cdc.mf.model.MfConstraintOwner;
import cdc.mf.model.MfDecoratedElement;
import cdc.mf.model.MfDependency;
import cdc.mf.model.MfDerivedItem;
import cdc.mf.model.MfDocumentation;
import cdc.mf.model.MfElement;
import cdc.mf.model.MfEnumeration;
import cdc.mf.model.MfEnumerationValue;
import cdc.mf.model.MfImplementation;
import cdc.mf.model.MfImplementationOwner;
import cdc.mf.model.MfInheritance;
import cdc.mf.model.MfInterface;
import cdc.mf.model.MfLink;
import cdc.mf.model.MfMember;
import cdc.mf.model.MfMemberOwner;
import cdc.mf.model.MfModel;
import cdc.mf.model.MfModifierItem;
import cdc.mf.model.MfNameItem;
import cdc.mf.model.MfOperation;
import cdc.mf.model.MfOrderedItem;
import cdc.mf.model.MfPackage;
import cdc.mf.model.MfPackageOwner;
import cdc.mf.model.MfPackagedItem;
import cdc.mf.model.MfParameter;
import cdc.mf.model.MfProperty;
import cdc.mf.model.MfQNameItem;
import cdc.mf.model.MfReadOnlyItem;
import cdc.mf.model.MfSpecialization;
import cdc.mf.model.MfSpecializationOwner;
import cdc.mf.model.MfStereotypesItem;
import cdc.mf.model.MfTag;
import cdc.mf.model.MfTagOwner;
import cdc.mf.model.MfTip;
import cdc.mf.model.MfType;
import cdc.mf.model.MfTypeOwner;
import cdc.mf.model.MfTypeRefItem;
import cdc.mf.model.MfUtils;
import cdc.mf.model.MfVisibilityItem;
import cdc.mf.model.deps.MfDependencyGraph;
import cdc.util.strings.StringUtils;

public class MfElementAdaptor extends ObjectModelAdaptor {
    private final MfHtmlGenerationArgs args;
    private final MfDependencyGraph deps;
    private BasicSuperLightGraph packagesDeps = null;

    private static final Pattern HTML = Pattern.compile("^html\\((.*)\\)$");
    private static final String HAS_CMAPX = "has-cmapx";
    private static final String CMAPX = "cmapx";

    private static final Map> IS_INSTANCE = new HashMap<>();

    private static void add(Class cls) {
        final String name = cls.getSimpleName();
        // Do this even if first char is always M (at the moment)
        final String camel = Character.toLowerCase(name.charAt(0)) + name.substring(1);
        IS_INSTANCE.put(camel, cls);
    }

    static {
        add(MfAbstractItem.class);
        add(MfAggregation.class);
        add(MfAnnotation.class);
        add(MfAssociation.class);
        add(MfCardinalityItem.class);
        add(MfClass.class);
        add(MfComposition.class);
        add(MfConnector.class);
        add(MfConnectorOwner.class);
        add(MfConstraint.class);
        add(MfConstraintOwner.class);
        add(MfDependency.class);
        add(MfDerivedItem.class);
        add(MfDocumentation.class);
        add(MfElement.class);
        add(MfEnumeration.class);
        add(MfEnumerationValue.class);
        add(MfImplementation.class);
        add(MfImplementationOwner.class);
        add(MfInheritance.class);
        add(MfInterface.class);
        add(MfLink.class);
        add(MfMember.class);
        add(MfMemberOwner.class);
        add(MfModel.class);
        add(MfModifierItem.class);
        add(MfNameItem.class);
        add(MfOperation.class);
        add(MfOrderedItem.class);
        add(MfPackage.class);
        add(MfPackagedItem.class);
        add(MfPackageOwner.class);
        add(MfParameter.class);
        add(MfPackageOwner.class);
        add(MfProperty.class);
        add(MfQNameItem.class);
        add(MfReadOnlyItem.class);
        add(MfSpecialization.class);
        add(MfSpecializationOwner.class);
        add(MfTag.class);
        add(MfTagOwner.class);
        add(MfTip.class);
        add(MfType.class);
        add(MfTypeOwner.class);
        add(MfTypeRefItem.class);
        add(MfVisibilityItem.class);
    }

    private static Boolean isInstance(MfElement element,
                                      String propertyName) {
        final Class cls = IS_INSTANCE.get(propertyName);
        return cls == null
                ? null
                : cls.isInstance(element);
    }

    public MfElementAdaptor(MfHtmlGenerationArgs args) {
        this.args = args;
        this.deps = new MfDependencyGraph(args.getModel());
    }

    private void buildPackagesDeps() {
        if (packagesDeps == null) {
            packagesDeps = deps.getPackagesDependencies(args.getModelOverviewIgnoredPackages());
        }
    }

    private  T fail(T value,
                       MfElement element,
                       String propertyName) {
        if (args.getHints().contains(MfHtmlGenerationHint.FAIL)) {
            throw new IllegalArgumentException("Failed to retrieve " + propertyName + " on " + element);
        } else {
            return value;
        }
    }

    @Override
    public synchronized Object getProperty(Interpreter interp,
                                           ST self,
                                           MfElement element,
                                           Object property,
                                           String propertyName) throws STNoSuchPropertyException {
        switch (propertyName) {
        case "issues":
            // The issues associated to element
            return args.getIssues(element);
        case "hasDeepIssues":
            return args.hasDeepIssues(element);
        case "worstIssueSeverity":
            return args.getWorstIssueSeverity(element);
        case "worstDeepIssueSeverity":
            return args.getWorstDeepIssueSeverity(element);
        case "metas":
            return MfParams.getMetas(element);
        case "configVersion":
            // Version of the code
            return Config.VERSION;
        case "abstract":
            // Is the element abstract?
            if (element instanceof final MfClass c) {
                return c.isAbstract();
            } else {
                return element instanceof MfInterface;
            }

        case "simplifiedName":
            if (element instanceof final MfNameItem x) {
                return args.simplifyName(x.getName());
            } else {
                return fail(null, element, propertyName);
            }

        case "allImplements":
            return getAllImplements(element);
        case "allImplemented":
            return getAllImplemented(element);

        case "decoratedExtends":
            return getDecoratedExtends(element);
        case "decoratedExtended":
            return getDecoratedExtended(element);
        case "decoratedImplements":
            return getDecoratedImplements(element);
        case "decoratedImplemented":
            return getDecoratedImplemented(element);

        case "decoratedConnectors":
            return getDecoratedConnectors(element);
        case "decoratedReversedConnectors":
            return getDecoratedReversedConnectors(element);

        case "decoratedMembers":
            return getDecoratedMembers(element);
        case "decoratedProperties":
            return getDecoratedProperties(element);
        case "decoratedOperations":
            return getDecoratedOperations(element);

        case "directExtends":
            return getDirectExtends(element);
        case "directExtended":
            return getDirectExtended(element);
        case "directImplements":
            return getDirectImplements(element, false);
        case "directImplemented":
            return getDirectImplemented(element);

        case "wrappedDirectExtends":
            return wrap(getDirectExtends(element), '-');
        case "wrappedDirectExtended":
            return wrap(getDirectExtended(element), '-');
        case "wrappedDirectImplements":
            return wrap(getDirectImplements(element, false), '.');
        case "wrappedDirectImplemented":
            return wrap(getDirectImplemented(element), '.');

        case "wrappedDirectInternalImplements":
            return wrap(getDirectImplements(element, true), '.');

        case "wrappedConnectors":
            if (element instanceof final MfConnectorOwner co1) {
                return wrap(co1.getConnectors(), '-');
            } else {
                return fail(null, element, propertyName);
            }
        case "wrappedConnectors0":
            if (element instanceof final MfConnectorOwner co2) {
                return wrap(co2.getConnectors(), '-', 0);
            } else {
                return fail(null, element, propertyName);
            }
        case "wrappedReversedConnectors":
            if (element instanceof final MfType t2) {
                return wrap(t2.getReversedConnectors(), '-');
            } else {
                return fail(null, element, propertyName);
            }

        case "ownedClasses":
            // List of classes owned by the element
            if (element instanceof MfPackage) {
                return sort(element.getChildren(MfClass.class), MfNameItem.NAME_COMPARATOR);
            } else {
                return fail(null, element, propertyName);
            }
        case "ownedInterfaces":
            // List of interfaces owned by the element
            if (element instanceof MfPackage) {
                return sort(element.getChildren(MfInterface.class), MfNameItem.NAME_COMPARATOR);
            } else {
                return fail(null, element, propertyName);
            }
        case "ownedEnumerations":
            // List of enumerations owned by the element
            if (element instanceof MfPackage) {
                return sort(element.getChildren(MfEnumeration.class), MfNameItem.NAME_COMPARATOR);
            } else {
                return fail(null, element, propertyName);
            }
        case "allPackages":
            // List of all packages of the model
            if (element instanceof MfModel) {
                return sort(element.collect(MfPackage.class), MfQNameItem.QNAME_COMPARATOR);
            } else {
                return fail(null, element, propertyName);
            }
        case "htmlId":
            return MfParams.getHtmlId(element);
        case "displayName":
            // The display name
            return MfParams.getDisplayName(element);
        case "itemRef":
            return MfParams.toItemRef(element, args);
        case "itemUrl":
            return MfParams.toItemUrl(element, args);
        case "upPath":
            return MfParams.getUpPath(element, args);
        case "imgUpPath":
            return MfParams.toString(MfParams.Dirs.getImgUpPath(element, args));
        case "packageItemsRef":
            if (element instanceof final MfPackage p4) {
                return MfParams.toUrl(MfParams.Files.getHtmlPackageItemsFile(p4, args));
            } else {
                return fail(null, element, propertyName);
            }
        case "packageOverviewRef":
            if (element instanceof final MfPackage p3) {
                return MfParams.toUrl(MfParams.Files.getHtmlPackageOverviewFile(p3, args));
            } else {
                return fail(null, element, propertyName);
            }
        case "kind":
            // The element kind
            return MfParams.getKind(element);
        case "pumlKind":
            // The element kind
            return MfParams.getPumlKind(element);
        case "pumlId":
            // The element safe id for PlantUML
            return MfParams.getPumlId(element);

        case "modelOverviewAllPackages":
            // Return all packages of Model Overview graph
            buildPackagesDeps();
            return sort(packagesDeps.getNodes(), MfQNameItem.QNAME_COMPARATOR);

        case "modelOverviewPackages":
            // Return children packages of a PackageOwner belonging to the Model Overview graph
            if (element instanceof MfModel || element instanceof MfPackage) {
                buildPackagesDeps();
                return ((MfPackageOwner) element).getPackages()
                                                 .stream()
                                                 .filter(packagesDeps::containsNode)
                                                 .toList();
            } else {
                return fail(null, element, propertyName);
            }

        case "modelOverviewDependencies":
            // Return all dependencies of a package in Model Overview graph
            if (element instanceof final MfPackage p1) {
                buildPackagesDeps();
                return packagesDeps.getEdgesStream(p1, EdgeDirection.OUTGOING)
                                   .map(BasicGraphEdge::getTarget)
                                   .sorted(MfQNameItem.QNAME_COMPARATOR)
                                   .toList();
            } else {
                return fail(null, element, propertyName);
            }

        case "externalTypes":
            if (element instanceof final MfPackage p2) {
                return MfParams.getExternalTypes(p2, args);
            } else {
                return fail(null, element, propertyName);
            }

        case "relatedTypes":
            if (element instanceof final MfType t) {
                return MfParams.getRelatedTypes(t, args);
            } else {
                return fail(null, element, propertyName);
            }

        case "displayedConstrainedElementRefs":
            if (element instanceof final MfConstraint constraint) {
                return MfParams.getDisplayedConstrainedElementRefs(constraint);
            } else {
                return fail(null, element, propertyName);
            }

        case "tags":
            if (element instanceof final MfTagOwner to) {
                if (args.getHints().contains(MfHtmlGenerationHint.SORT_TAGS)) {
                    return to.getTags()
                             .stream()
                             .sorted(MfTag.NAME_VALUE_COMPARATOR)
                             .toList();
                } else {
                    return ((MfTagOwner) element).getTags();
                }
            } else {
                return fail(null, element, propertyName);
            }
        case "usages":
            return MfUtils.getUsages(element, args.getHints().contains(MfHtmlGenerationHint.SHOW_NON_NAVIGABLE_TIPS));
        case "layoutLinks":
            return MfParams.getLayoutLinks(element);
        case "stereotypes":
            if (element instanceof final MfStereotypesItem s) {
                return s.getStereotypes();
            } else {
                return null;
            }
        default:
            break;
        }
        final Boolean isInstance = isInstance(element, propertyName);
        if (isInstance != null) {
            return isInstance;
        }
        if (propertyName.endsWith(HAS_CMAPX)) {
            final String prefix = propertyName.substring(0, propertyName.length() - HAS_CMAPX.length());
            return MfParams.hasCmapx(args.getBaseDir(), prefix, element);
        } else if (propertyName.endsWith(CMAPX)) {
            final String prefix = propertyName.substring(0, propertyName.length() - CMAPX.length());
            return MfParams.loadCmapx(args.getBaseDir(), prefix, element);
        }

        final Matcher m = HTML.matcher(propertyName);
        if (m.matches()) {
            final String upPath = m.group(1);
            // The html content of an MfDocumentation
            if (element instanceof final MfDocumentation d) {
                return MfParams.toHtml(d.getText(), upPath, args);
            } else {
                return "";
            }
        }
        return super.getProperty(interp, self, element, property, propertyName);
    }

    private static List> getDecoratedMembers(MfElement element) {
        if (element instanceof final MfMemberOwner owner) {
            return sort(owner.getDecoratedMembers(),
                        MfDecoratedElement.ID_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedProperties(MfElement element) {
        if (element instanceof final MfMemberOwner owner) {
            return sort(owner.getDecoratedProperties(),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedOperations(MfElement element) {
        if (element instanceof final MfMemberOwner owner) {
            return sort(owner.getDecoratedOperations(),
                        MfDecoratedElement.ID_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedConnectors(MfElement element) {
        if (element instanceof final MfConnectorOwner owner) {
            return sort(owner.getDecoratedConnectors(),
                        MfDecoratedElement.ID_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedReversedConnectors(MfElement element) {
        if (element instanceof final MfType type) {
            return sort(type.getDecoratedReversedConnectors(),
                        MfDecoratedElement.ID_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedExtends(MfElement element) {
        if (element instanceof final MfClass e) {
            return sort(e.getDecoratedAncestors(MfClass.class),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else if (element instanceof final MfInterface e) {
            return sort(e.getDecoratedAncestors(MfInterface.class),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedExtended(MfElement element) {
        if (element instanceof final MfClass e) {
            return sort(e.getDecoratedDescendants(MfClass.class),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else if (element instanceof final MfInterface e) {
            return sort(e.getDecoratedDescendants(MfInterface.class),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedImplements(MfElement element) {
        if (element instanceof MfClass || element instanceof MfEnumeration) {
            return sort(((MfType) element).getDecoratedAncestors(MfInterface.class),
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List> getDecoratedImplemented(MfElement element) {
        if (element instanceof final MfInterface e) {
            final Set> set = new HashSet<>();
            set.addAll(e.getDecoratedDescendants(MfClass.class));
            set.addAll(e.getDecoratedDescendants(MfEnumeration.class));
            return sort(set,
                        MfDecoratedElement.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List getDirectExtends(MfElement element) {
        // List of directly extended types
        if (element instanceof final MfClass e) {
            return sort(e.getDirectAncestors(MfClass.class),
                        MfQNameItem.QNAME_COMPARATOR);
        } else if (element instanceof final MfInterface e) {
            return sort(e.getDirectAncestors(MfInterface.class),
                        MfQNameItem.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List getDirectExtended(MfElement element) {
        // Types that directly extend the element
        if (element instanceof final MfClass e) {
            return sort(e.getDirectDescendants(MfClass.class),
                        MfQNameItem.QNAME_COMPARATOR);
        } else if (element instanceof final MfInterface e) {
            return sort(e.getDirectDescendants(MfInterface.class),
                        MfQNameItem.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List getDirectImplements(MfElement element,
                                                         boolean internal) {
        // Interfaces that the element directly implements
        if (element instanceof MfClass || element instanceof MfEnumeration) {
            if (internal) {
                return sort(((MfType) element).getDirectAncestors(MfInterface.class)
                                              .stream()
                                              .filter(x -> x.getRootType()
                                                            .getOwningPackage() == ((MfType) element).getRootType()
                                                                                                     .getOwningPackage())
                                              .toList(),
                            MfQNameItem.QNAME_COMPARATOR);
            } else {
                return sort(((MfType) element).getDirectAncestors(MfInterface.class),
                            MfQNameItem.QNAME_COMPARATOR);
            }
        } else {
            return Collections.emptyList();
        }
    }

    private static List getAllImplements(MfElement element) {
        // List of all implemented interfaces
        if (element instanceof MfClass || element instanceof MfEnumeration) {
            return sort(((MfType) element).getAllAncestors(Strictness.STRICT, MfInterface.class),
                        MfQNameItem.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List getDirectImplemented(MfElement element) {
        // Types that directly implement the interface
        if (element instanceof final MfInterface e) {
            return sort(e.getAllImplementors(),
                        MfQNameItem.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static List getAllImplemented(MfElement element) {
        // List of types that implement this interface
        if (element instanceof final MfInterface e) {
            return sort(e.getAllImplementors(),
                        MfQNameItem.QNAME_COMPARATOR);
        } else {
            return Collections.emptyList();
        }
    }

    private static  List sort(Collection collection,
                                    Comparator comparator) {
        return collection.stream()
                         .sorted(comparator)
                         .collect(Collectors.toList());
    }

    static class LengthWrapper {
        public final X data;
        public final int length;
        public final String filler;

        public LengthWrapper(X data,
                             int length,
                             char fill) {
            this.data = data;
            this.length = length;
            this.filler = StringUtils.toString(fill, length);
        }
    }

    private  List> wrap(List list,
                                            char fill) {
        return wrap(list, fill, Integer.MAX_VALUE);

    }

    private  List> wrap(List list,
                                            char fill,
                                            int max) {
        if (list == null) {
            return null;
        } else {
            final List> out = new ArrayList<>();
            final int length = list.size();
            final int m = Math.max(1, Math.min(length / 2, args.getLayoutDepth()));
            for (int index = 0; index < list.size(); index++) {
                // final int mod = index % (2 * m);
                final int mod = index % m;
                out.add(new LengthWrapper<>(list.get(index),
                                            // Math.min(max, Math.min(mod, 2 * m - 1 - mod)),
                                            Math.min(max, mod),
                                            fill));
            }
            return out;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy