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

cdc.mf.model.MfUtils Maven / Gradle / Ivy

The newest version!
package cdc.mf.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;

import cdc.args.Strictness;
import cdc.issues.locations.Location;
import cdc.util.encoding.ExtensionEncoder;
import cdc.util.lang.Introspection;
import cdc.util.paths.Path;
import cdc.util.strings.StringUtils;

public final class MfUtils {
    @SuppressWarnings("rawtypes")
    private static final ExtensionEncoder KINDS =
            new ExtensionEncoder<>(Class.class, String.class);
    @SuppressWarnings("rawtypes")
    private static final ExtensionEncoder PLURALS =
            new ExtensionEncoder<>(Class.class, String.class);
    @SuppressWarnings("rawtypes")
    private static final ExtensionEncoder CODES =
            new ExtensionEncoder<>(Class.class, String.class);
    private static final Map, Class> PARENT_CLASSES = new HashMap<>();
    private static final Map, Class> BUILDER_CLASSES = new HashMap<>();
    private static final Map, MfElementFeatures> FEATURES = new HashMap<>();

    static {
        put(MfAggregation.class,
            MfAggregation.PARENT_CLASS,
            MfAggregation.BUILDER_CLASS,
            MfConnector.FEATURES,
            "aggregation");
        put(MfAnnotation.class,
            MfAnnotation.PARENT_CLASS,
            MfAnnotation.BUILDER_CLASS,
            MfAnnotation.FEATURES,
            "annotation");
        put(MfAssociation.class,
            MfAssociation.PARENT_CLASS,
            MfAssociation.BUILDER_CLASS,
            MfConnector.FEATURES,
            "association");
        put(MfClass.class,
            MfClass.PARENT_CLASS,
            MfClass.BUILDER_CLASS,
            MfType.FEATURES,
            "class",
            "classes");
        put(MfComposition.class,
            MfComposition.PARENT_CLASS,
            MfComposition.BUILDER_CLASS,
            MfConnector.FEATURES,
            "composition");
        put(MfConstraint.class,
            MfConstraint.PARENT_CLASS,
            MfConstraint.BUILDER_CLASS,
            MfConstraint.FEATURES,
            "constraint");
        put(MfDependency.class,
            MfDependency.PARENT_CLASS,
            MfDependency.BUILDER_CLASS,
            MfDependency.FEATURES,
            "dependency");
        put(MfDocumentation.class,
            MfDocumentation.PARENT_CLASS,
            MfDocumentation.BUILDER_CLASS,
            MfDocumentation.FEATURES,
            "documentation");
        put(MfEnumeration.class,
            MfEnumeration.PARENT_CLASS,
            MfEnumeration.BUILDER_CLASS,
            MfType.FEATURES,
            "enumeration");
        put(MfEnumerationValue.class,
            MfEnumerationValue.PARENT_CLASS,
            MfEnumerationValue.BUILDER_CLASS,
            MfEnumerationValue.FEATURES,
            "value");
        put(MfImplementation.class,
            MfImplementation.PARENT_CLASS,
            MfImplementation.BUILDER_CLASS,
            MfImplementation.FEATURES,
            "implementation");
        put(MfInterface.class,
            MfInterface.PARENT_CLASS,
            MfInterface.BUILDER_CLASS,
            MfType.FEATURES,
            "interface");
        put(MfModel.class,
            null,
            MfModel.BUILDER_CLASS,
            MfModel.FEATURES,
            "model");
        put(MfOperation.class,
            MfOperation.PARENT_CLASS,
            MfOperation.BUILDER_CLASS,
            MfOperation.FEATURES,
            "operation");
        put(MfPackage.class,
            MfPackage.PARENT_CLASS,
            MfPackage.BUILDER_CLASS,
            MfPackage.FEATURES,
            "package");
        put(MfParameter.class,
            MfParameter.PARENT_CLASS,
            MfParameter.BUILDER_CLASS,
            MfParameter.FEATURES,
            "parameter");
        put(MfProperty.class,
            MfProperty.PARENT_CLASS,
            MfProperty.BUILDER_CLASS,
            MfProperty.FEATURES,
            "property",
            "properties");
        put(MfSpecialization.class,
            MfSpecialization.PARENT_CLASS,
            MfSpecialization.BUILDER_CLASS,
            MfSpecialization.FEATURES,
            "specialization");
        put(MfTag.class,
            MfTag.PARENT_CLASS,
            MfTag.BUILDER_CLASS,
            MfTag.FEATURES,
            "tag");
        put(MfTip.class,
            MfTip.PARENT_CLASS,
            MfTip.BUILDER_CLASS,
            MfTip.FEATURES,
            "tip");
        KINDS.lock();
        PLURALS.lock();
        CODES.lock();
    }

    private static >
            void put(Class childClass,
                     Class parentClass,
                     Class> builderClass,
                     MfElementFeatures features,
                     String kind,
                     String plural) {
        KINDS.put(childClass, kind);
        PLURALS.put(childClass, plural);
        CODES.put(childClass, kind.toUpperCase());
        if (parentClass != null) {
            PARENT_CLASSES.put(childClass, parentClass);
        }
        BUILDER_CLASSES.put(childClass, builderClass);
        FEATURES.put(childClass, features);
    }

    private static >
            void put(Class childClass,
                     Class parentClass,
                     Class> builderClass,
                     MfElementFeatures features,
                     String kind) {
        put(childClass, parentClass, builderClass, features, kind, kind + "s");
    }

    private MfUtils() {
    }

    public static String validate(String s) {
        return s == null ? "" : s;
    }

    public static Set> getConcreteElementClasses() {
        return FEATURES.keySet();
    }

    public static 

> Class

getParentClass(Class childClass) { return Introspection.uncheckedCast(PARENT_CLASSES.get(childClass)); } public static > Class getBuilderClass(Class elementClass) { return Introspection.uncheckedCast(BUILDER_CLASSES.get(elementClass)); } public static MfElementFeatures getFeatures(Class elementClass) { return FEATURES.get(elementClass); } public static MfElementFeatures getFeatures(MfElement element) { return getFeatures(element.getClass()); } public static String getKind(Class cls) { return KINDS.encode(cls); } public static String getKindPlural(Class cls) { return PLURALS.encode(cls); } public static String getCode(Class cls) { return CODES.encode(cls); } /** * @param item The item. * @return A string representing the item kind. */ public static String getKind(MfElement item) { return KINDS.encode(item.getClass()); } /** * @param item The item. * @return A string representing the plural of item kind. */ public static String getKindPlural(MfElement item) { return PLURALS.encode(item.getClass()); } /** * @param item The item. * @return A string representing the item code. */ public static String getCode(MfElement item) { return CODES.encode(item.getClass()); } /** * @param kind The kind. * @return The class corresponding to {@code kind}. */ public static Class kindToClass(String kind) { @SuppressWarnings("unchecked") final Class tmp = KINDS.decode(kind); return tmp; } /** * @param code The code. * @return The class corresponding to {@code code}. */ public static Class codeToClass(String code) { @SuppressWarnings("unchecked") final Class tmp = CODES.decode(code); return tmp; } public static String identify(MfElement item) { return Location.toString(item.getLocation(), false); } public static String toOrdering(boolean isOrdered) { return isOrdered ? "ordered" : "not ordered"; } public static String toNavigability(boolean isNavigable) { return isNavigable ? "navigable" : "not navigable"; } public static String toString(MfElement item) { final StringBuilder builder = new StringBuilder(); if (item == null) { builder.append("null"); } else { builder.append(item.getClass().getSimpleName()); if (!StringUtils.isNullOrEmpty(item.getId())) { builder.append(" (") .append(item.getId()) .append(')'); } if (item instanceof final MfNameItem x) { builder.append(" '") .append(x.getName()) .append('\''); } } return builder.toString(); } public static Path toQName(MfQNameItem item) { if (item instanceof MfModel) { return Path.ROOT; } else { if (item.getParent() instanceof MfModel) { return Path.of(Path.SLASH + item.getName()); } else { final Path parentPath = item.getParent().getQName(); return Path.of(parentPath.toStringSlash() + Path.SLASH + item.getName()); } } } private static MfQNameItem resolve(MfQNameItem item, Path path, boolean fail) { MfQNameItem index; if (path.isAbsolute()) { index = item.getModel(); } else { index = item; } for (int i = 0; i < path.getNameCount(); i++) { final String name = path.getName(i); if (".".equals(name)) { // Ignore } else if ("..".equals(name)) { index = index.getParent(); } else { final List candidates = index.getChildren() .stream() .filter(MfQNameItem.class::isInstance) .map(MfQNameItem.class::cast) .filter(x -> Objects.equals(name, x.getName())) .toList(); if (candidates.size() == 1) { index = candidates.get(0); } else if (candidates.isEmpty()) { if (fail) { throw new NoSuchElementException(index + " has no child named '" + name + "'"); } else { return null; } } else { if (fail) { throw new NoSuchElementException(index + " has several children named '" + name + "'"); } else { return null; } } } } return index; } public static MfQNameItem resolve(MfQNameItem item, Path path) { return resolve(item, path, true); } public static boolean canResolve(MfQNameItem item, Path path) { return resolve(item, path, false) != null; } private static MfUsage.Kind getInheritanceKind(MfType type, MfType x) { if (type instanceof MfInterface) { if (x instanceof MfInterface) { return MfUsage.Kind.EXTENSION; } else { return MfUsage.Kind.IMPLEMENTATION; } } else { return MfUsage.Kind.EXTENSION; } } private static boolean isInheritanceDirect(MfType type, MfType x) { final Set xAncestors = x.getDirectAncestors(); return xAncestors.contains(type); } /** * Return the usages of an element. *

* For a type: *

    *
  • Properties that use that type or one of its descendants. *
  • Parameters that use that type or one of its descendants. *
  • Types that extend that type. *
  • Navigable connectors to that type or one of its descendants. *
* * @param element The element. * @param includeNonNavigableTips If {@code true}, include non-navigable tips. * @return The usages of {@code element}. */ public static List getUsages(MfElement element, boolean includeNonNavigableTips) { final List list = new ArrayList<>(); final MfModel model = element.getModel(); if (element instanceof final MfType type) { final Set descendants = type.getAllDescendants(Strictness.LOOSE); final List sortedDescendants = new ArrayList<>(descendants); Collections.sort(sortedDescendants, MfQNameItem.QNAME_COMPARATOR); // Properties that are typed with that type or one of its descendants model.traverse(MfProperty.class, x -> list.add(new MfUsage(x, MfUsage.Kind.PROPERTY_TYPE, x.getType() == type)), x -> x.hasValidType() && descendants.contains(x.getType())); // Parameters that are typed with that type or one of its descendants model.traverse(MfParameter.class, x -> list.add(new MfUsage(x, MfUsage.Kind.PARAMETER_TYPE, x.getType() == type)), x -> x.hasValidType() && descendants.contains(x.getType())); // Types that extend or implement that type for (final MfType descendant : sortedDescendants) { if (descendant != type) { list.add(new MfUsage(descendant, getInheritanceKind(type, descendant), isInheritanceDirect(type, descendant))); } } // Navigable Tips for (final MfType descendant : sortedDescendants) { for (final MfConnector connector : descendant.getConnectors()) { if (connector.getSourceTip().isNavigable()) { list.add(new MfUsage(connector, MfUsage.Kind.NAVIGABLE_SOURCE_TYPE, connector.getSourceTip().getType() == type)); } else if (includeNonNavigableTips) { list.add(new MfUsage(connector, MfUsage.Kind.NON_NAVIGABLE_SOURCE_TYPE, connector.getSourceTip().getType() == type)); } } for (final MfConnector connector : descendant.getReversedConnectors()) { if (connector.getTargetTip().isNavigable()) { list.add(new MfUsage(connector, MfUsage.Kind.NAVIGABLE_TARGET_TYPE, connector.getTargetTip().getType() == type)); } else if (includeNonNavigableTips) { list.add(new MfUsage(connector, MfUsage.Kind.NON_NAVIGABLE_TARGET_TYPE, connector.getTargetTip().getType() == type)); } } } } return list; } }