cdc.mf.model.MfElement Maven / Gradle / Ivy
package cdc.mf.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import cdc.issues.Metas;
import cdc.issues.locations.LocatedItem;
import cdc.util.lang.Introspection;
import cdc.util.strings.StringUtils;
/**
* Base interface of all model elements.
*
* They can all have an identifier and be documented.
*
* @author Damien Carbonne
*/
public interface MfElement extends MfMetasItem, LocatedItem {
public static final Comparator ID_COMPARATOR =
Comparator.comparing(x -> x.getId() == null ? "" : x.getId());
public boolean canWrap();
public boolean canWrap(Class extends MfWrapper> wrapperClass);
public W wrap(Class wrapperClass);
public default String getKind() {
return MfUtils.getKind(this);
}
public default String getCode() {
return MfUtils.getCode(this);
}
/**
* @return The identifier of this element.
* Some elements must have an identifier, others may (but not necessarily) have one.
*/
public String getId();
public default boolean hasId() {
return !StringUtils.isNullOrEmpty(getId());
}
public String getGuid();
public default boolean hasGuid() {
return !StringUtils.isNullOrEmpty(getGuid());
}
/**
* @return The parent element of this element, possibly {@code null}.
*/
public MfElement getParent();
public default boolean hasParent() {
return getParent() != null;
}
/**
* @return The first ancestor that is a package, possibly {@code null}.
*/
public default MfPackage getSurroundingPackage() {
if (getParent() instanceof final MfPackage p) {
return p;
} else if (hasParent()) {
return getParent().getSurroundingPackage();
} else {
return null;
}
}
/**
* @return The top level ancestor that is a package, possibly {@code null}.
*/
public default MfPackage getRootPackage() {
MfPackage p = getSurroundingPackage();
if (p == null) {
return null;
} else {
while (p.getParent() instanceof final MfPackage parent) {
p = parent;
}
return p;
}
}
/**
* @return The depth (1 for root element) of the element.
*/
public default int getDepth() {
int depth = 0;
MfElement index = this;
while (index != null) {
index = index.getParent();
depth++;
}
return depth;
}
/**
* @return A list of ancestors.
*/
public default List getHierarchy() {
final List list = new ArrayList<>();
MfElement iter = this;
while (iter != null) {
list.add(iter);
iter = iter.getParent();
}
Collections.reverse(list);
return list;
}
/**
* @return The index of this element among the children of its parent.
* {@code -1} if this element has no parent.
*/
public int getIndex();
public default List getIndices() {
final List list = new ArrayList<>();
MfElement iter = this;
while (iter != null && iter.getIndex() >= 0) {
list.add(iter.getIndex());
iter = iter.getParent();
}
Collections.reverse(list);
return list;
}
public default MfLocationPart getLocationPart() {
return MfLocationPart.of(this);
}
@Override
default MfLocation getLocation() {
return MfLocation.of(this);
}
/**
* @param The parent type.
* @param parentClass The parent class.
* @return The parent element (the owner) of this element,
* possibly {@code null} or converted to {@code parentClass}.
* @throws ClassCastException When the parent can not be converted to {@code parentClass}.
*/
public default
P getParent(Class
parentClass) {
return parentClass.cast(getParent());
}
/**
* @return A list of locally owned children of this element.
*/
public List extends MfElement> getChildren();
/**
* @param The child type.
* @param childClass The child class.
* @return A list of locally owned children that are instances of {@code childClass}.
*/
public default List getChildren(Class childClass) {
return getChildren().stream()
.filter(childClass::isInstance)
.map(childClass::cast)
.toList();
}
/**
* @param The child type.
* @param childClass The child class.
* @param predicate The predicate.
* @return A list of locally owned children that are instances of {@code childClass} and match {@code predicate}.
*/
public default List getChildren(Class childClass,
Predicate super C> predicate) {
return getChildren().stream()
.filter(childClass::isInstance)
.map(childClass::cast)
.filter(predicate)
.toList();
}
/**
* @param name The child name.
* @return A list of locally owned children named {@code name}.
*/
public default List getChildren(String name) {
return getChildren().stream()
.filter(MfNameItem.class::isInstance)
.map(MfNameItem.class::cast)
.filter(x -> Objects.equals(name, x.getName()))
.toList();
}
public default Optional getFirstChild(Class childClass,
Predicate super C> predicate) {
return getChildren().stream()
.filter(childClass::isInstance)
.map(childClass::cast)
.filter(predicate)
.findFirst();
}
/**
* @param The child type.
* @param childClass The child class.
* @return The (first) child of this element that is an instance of {@code childClass}.
* @throws NoSuchElementException When there are no matching children.
*/
public default C getChild(Class childClass) {
return getChildren().stream()
.filter(childClass::isInstance)
.map(childClass::cast)
.findFirst()
.orElseThrow();
}
/**
* @param The child type.
* @param childClass The child class.
* @return {@code true} if this element has {@code childClass} children.
*/
public default boolean hasChildren(Class childClass) {
return getChildren().stream()
.anyMatch(childClass::isInstance);
}
/**
* @param The child type.
* @param name The child name.
* @param childClass The child class.
* @return The (first) child of this element that is an instance of {@code childClass}
* and is named {@code name}.
* @throws NoSuchElementException When there is no matching child.
* @throws ClassCastException When the child can not be converted to {@code childClass}.
*/
public C getChild(String name,
Class childClass);
public default MfElement getChild(MfLocationPart lp) {
return MfLocationPart.getChild(this, lp);
}
/**
* @return The {@link MfModel} of this element.
*/
public MfModel getModel();
/**
* @return A list of {@link MfDocumentation documentations} locally owned by this element.
*/
public default List getDocumentations() {
return getChildren(MfDocumentation.class);
}
/**
* @return {@code true} if this element locally owns {@link MfDocumentation documentations}.
*/
public default boolean hasDocumentations() {
return hasChildren(MfDocumentation.class);
}
/**
* @return A {@link MfDocumentation documentation} {@link MfDocumentation.Builder builder}.
*/
public MfDocumentation.Builder extends MfElement> documentation();
/**
* @return A list of {@link MfLink links} locally owned by this element.
*/
public default List> getLinks() {
return getChildren(Introspection.uncheckedCast(MfLink.class));
}
/**
* @param The link type.
* @param cls The link class.
* @return A list of {@link MfLink links} locally owned by this element and are instance of {@code cls}.
*/
public default > List getLinks(Class cls) {
return getLinks().stream()
.filter(cls::isInstance)
.map(cls::cast)
.toList();
}
/**
* @return A list of {@link MfLink links} whose target is directly this element.
*/
public List> getReversedLinks();
/**
* @param The link type.
* @param cls The link class.
* @return A List of {@link MfLink MfLinks} that are instances of {@code cls}
* and where this element is target.
*/
public default > List getReversedLinks(Class cls) {
return getReversedLinks().stream()
.filter(cls::isInstance)
.map(cls::cast)
.toList();
}
/**
* Traverses all elements and invokes a consumer on elements that match a class and predicate.
*
* @param The element type.
* @param elementClass The element class.
* @param consumer The element consumer.
* @param predicate The element predicate.
*/
public default void traverse(Class elementClass,
Consumer super E> consumer,
Predicate super E> predicate) {
if (elementClass.isInstance(this)) {
final E x = elementClass.cast(this);
if (predicate.test(x)) {
consumer.accept(x);
}
}
for (final MfElement child : getChildren()) {
child.traverse(elementClass, consumer, predicate);
}
}
/**
* Traverses all elements and invokes a consumer on elements that match a class.
*
* @param The element type.
* @param elementClass The element class.
* @param consumer The element consumer.
*/
public default void traverse(Class elementClass,
Consumer super E> consumer) {
traverse(elementClass, consumer, x -> true);
}
/**
* Traverses all elements and invokes a consumer on each traversed element.
*
* @param consumer The element consumer.
*/
public default void traverse(Consumer super MfElement> consumer) {
traverse(MfElement.class, consumer);
consumer.accept(this);
}
/**
* @param The element type.
* @param elementClass The element class.
* @param predicate The element predicate.
* @return A List of all elements that are instances of {@code elementClass} and match {@code predicate}.
*/
public default List collect(Class elementClass,
Predicate super E> predicate) {
final List list = new ArrayList<>();
traverse(elementClass, list::add, predicate);
return list;
}
/**
* @param The element type.
* @param elementClass The element class.
* @return A List of all elements that are instances of {@code elementClass}.
*/
public default List collect(Class elementClass) {
return collect(elementClass, x -> true);
}
public static interface Builder, E extends MfElement> {
/**
* @return The built class.
*/
public Class getElementClass();
public B set(E element);
/**
* @return The id.
*/
public String getId();
/**
* Sets the identifier.
*
* @param id The identifier.
* @return This builder.
*/
public B id(String id);
/**
* @return The GUID.
*/
public String getGuid();
/**
* Sets the GUID.
*
* @param guid The GUID.
* @return This builder.
*/
public B guid(String guid);
public Metas getMetas();
/**
* Adds a (name, value) meta.
*
* @param name The name.
* @param value The value.
* @return This builder.
*/
public B meta(String name,
String value);
public B meta(String name,
String value,
String separator);
public B metas(Metas metas);
/**
* Adds a (name, value) meta.
*
* @param name The name.
* @param value The value.
* @return This builder.
*/
public B meta(MfEnum name,
String value);
public B meta(MfEnum name,
String value,
String separator);
/**
* @return The built element.
*/
public E build();
}
}