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

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

The newest version!
package cdc.mf.model;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;

import cdc.util.lang.Checks;

/**
 * Local part of an element location. It is a triplet:
 * 
    *
  • mandatory code, corresponding to the element class. *
  • optional element name. *
  • optional element id. *
* The string representation is {@code code@name[id]}, where {@code name} and {@code id} are optional. */ public final class MfLocationPart { /** The code pattern: only upper case letters. */ public static final Pattern CODE_PATTERN = Pattern.compile("[A-Z]+"); /** The id pattern */ public static final Pattern ID_PATTERN = Pattern.compile("[^\\[\\]]*"); public static final char SEPARATOR = '@'; public static final char OPEN = '['; public static final char CLOSE = ']'; public static final String NULL = ""; /** * Set of classes that are considered as non-readable and should be augmented with additional * readable classes (their parent). */ private static final Set> NON_READABLE = Set.of(MfDocumentation.class, MfTag.class, MfComposition.class, MfAggregation.class, MfAssociation.class, MfSpecialization.class, MfImplementation.class, MfEnumerationValue.class); /** The code associated to the element type. */ private final String code; /** The local name of the element. */ private final String name; /** The element id. */ private final String id; /** * Set of classes for which name is expected as a designator. */ private static final Set> NAME_IS_DESIGNATOR = Set.of(MfClass.class, MfEnumeration.class, MfEnumerationValue.class, MfInterface.class, MfModel.class, MfPackage.class, MfParameter.class, MfProperty.class); private MfLocationPart(String code, String name, String id) { this.code = Checks.isNotNull(code, "code"); this.name = name == null ? NULL : name; this.id = id == null ? NULL : id; Checks.isTrue(CODE_PATTERN.matcher(this.code).matches(), "Invalid code {}", this.code); Checks.isTrue(ID_PATTERN.matcher(this.id).matches(), "Invalid id {}", this.id); } /** * @param element The element. * @return A new instance of MfLocationPart corresponding to {@code element}. */ public static MfLocationPart of(MfElement element) { Checks.isNotNull(element, "element"); final String code = element.getCode(); final String name; if (element instanceof final MfNameItem x) { name = x.getName(); } else { name = null; } return new MfLocationPart(code, name, element.getId()); } /** * @param s The string representation of the part. * @return A new instance of MfLocationPart corresponding to {@code s}. * @throws IllegalArgumentException When {@code s} can not be correctly interpreted. */ public static MfLocationPart of(String s) { final int sepLoc = s.indexOf(SEPARATOR); if (sepLoc > 0) { final String code = s.substring(0, sepLoc); final String tail = s.substring(sepLoc + 1); final int openLoc = tail.lastIndexOf(OPEN); if (openLoc >= 0) { final String name = tail.substring(0, openLoc); final String id = tail.substring(openLoc + 1, tail.length() - 1); return new MfLocationPart(code, name, id); } } throw new IllegalArgumentException("Invalid text: " + s); } /** * @return The code. */ public String getCode() { return code; } /** * @return The element class, derived from code. */ public Class getElementClass() { return MfUtils.codeToClass(code); } public boolean hasValidName() { return !name.isEmpty(); } /** * @return The element name. May be empty. */ public String getName() { return name; } public boolean hasValidId() { return !id.isEmpty(); } /** * @return The element id. May be empty. */ public String getId() { return id; } /** * * @param parent The parent element. * @param childPart The location part of the child element. * @return The child of {@code parent} corresponding to {@code childPart}, * or {@code null} if {@code childPart} does not contain enough information to locate the child. * @throws NoSuchElementException When there is enough information but no child was found. * @throws ClassCastException When there is enough information, a child was found, * but its class does not match the one defined by {@code childPart}. */ public static MfElement getChild(MfElement parent, MfLocationPart childPart) { final String code = childPart.getCode(); final Class cls = MfUtils.codeToClass(code); final String name = childPart.getName(); final MfElement result; if (childPart.hasValidId()) { final String id = childPart.getId(); result = parent.getModel().getItemWithId(id).orElseThrow(); } else if (childPart.hasValidName() && NAME_IS_DESIGNATOR.contains(cls)) { result = parent.getChild(name, MfNameItem.class); } else { return null; } Checks.assertTrue(result.getParent() == parent, "parent mismatch"); return cls.cast(result); } @Override public int hashCode() { return Objects.hash(code, name, id); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof MfLocationPart)) { return false; } final MfLocationPart other = (MfLocationPart) object; return Objects.equals(code, other.code) && Objects.equals(name, other.name) && Objects.equals(id, other.id); } @Override public String toString() { return code + SEPARATOR + name + OPEN + id + CLOSE; } private static boolean isNonReadable(Class cls) { return NON_READABLE.contains(cls); } /** * Takes a list of parts and keeps all parts starting at the last having an id. * * @param parts The parts. * @param readable If true, adds additional elements to make thing more readable. * @return A compressed version of {@code parts} that contains the same amount of information as {@code parts}. */ public static List compress(List parts, boolean readable) { if (parts.size() <= 1) { return parts; } else { int from = parts.size() - 1; // 1) Find last part with an id while (from > 0 && !parts.get(from).hasValidId()) { from--; } // 2) Find first previous part that is readable while (from > 0 && isNonReadable(parts.get(from).getElementClass())) { from--; } return parts.subList(from, parts.size()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy