cdc.mf.model.MfElementRef Maven / Gradle / Ivy
package cdc.mf.model;
import java.util.ArrayList;
import java.util.List;
import cdc.util.lang.Checks;
import cdc.util.paths.Path;
import cdc.util.refs.ResolutionException;
/**
* Element reference.
*
* It has several forms:
*
* - Eager when the referenced element exists in memory at time of creation of the reference.
*
- Lazy when the referenced element does not yet exist time of creation of the reference.
*
- Invalid when there is not enough information to reference the element.
*
*
* @author Damien Carbonne
*
* @param The referenced element type.
*/
public interface MfElementRef {
public static final MfElementRef NO_TYPE = of(MfType.class);
public static final MfElementRef NO_INTERFACE = of(MfInterface.class);
public static final String REF_CLASS = "refClass";
/**
* @return The Class of the referenced element.
*/
public Class getRefClass();
/**
* @return The identifier of the referenced element.
* May be {@code null} if this is not known when the reference is built.
* In that case, {@link #getRefQName()} should return a valid value.
*/
public String getRefId();
/**
* Returns the resolved identifier of the referenced element.
*
* If this reference was not created with an identifier, then it is resolved
* and the identifier of the referenced element is returned.
*
* @return The resolved identifier of the referenced element.
*/
public default String resolveRefId() {
if (getRefId() != null) {
return getRefId();
} else if (isValid()) {
final X ref = get();
return ref.getId();
} else {
return null;
}
}
/**
* @return The qualified name of the referenced element.
* May be {@code null} if this is not known when the reference is built.
* In that case, {@link #getRefId()} should return a valid value.
*/
public Path getRefQName();
/**
* Returns the resolved qualified name of the referenced element.
*
* If this reference was not created with a qualified name, then it is resolved
* and the qualified name of the referenced element is returned.
* This can work if the referenced element implements {@link MfQNameItem}.
*
* @return The resolved qualified name of the referenced element.
*/
public default Path resolveRefQName() {
if (getRefQName() != null) {
return getRefQName();
} else if (isValid()) {
final X ref = get();
if (ref instanceof final MfQNameItem x) {
return x.getQName();
} else {
return null;
}
} else {
return null;
}
}
/**
* @return {@code true} if this reference has neither id nor qualified name.
*/
public default boolean isEmpty() {
return getRefId() == null && getRefQName() == null;
}
/**
* @return {@code true} when the reference is valid: calling get() should return a result.
*/
public boolean isValid();
/**
* @return The referenced element.
*/
public X get();
public default X getElement() {
return get();
}
/**
* Creates an eager element reference.
*
* @param The element type.
* @param ref The element.
* @return An eager element reference instance from {@code ref}.
* @throws IllegalArgumentException When {@code ref} is {@code null}.
*/
public static MfElementRef of(X ref) {
return new Eager<>(ref);
}
/**
* Creates an element reference.
*
* - Tries to create an eager instance from {@code model} and {@code refId}.
*
- Tries to create an eager instance from {@code model} and {@code refQName}.
*
- Tries to create a lazy instance from {@code model}, {@code refClass}, {@code refId} and {@code refQName},
* if one of {@code refId} or {@code refQName} is not {@code null}
*
- Creates a nothing instance from {@code refClass}, if {@code refId} and {@code refQName} are both {@code null}.
*
*
* @param The element type.
* @param model The model.
* @param refClass The element class.
* @param refId The referenced element identifier. May be {@code null}.
* @param refQName The referenced element QName. May be {@code null}.
* @return An element reference instance.
* @throws IllegalArgumentException When {@code model} or {@code refClass} is {@code null}.
*/
public static MfElementRef of(MfModel model,
Class refClass,
String refId,
Path refQName) {
// Checks.isNotNull(model, "model");
Checks.isNotNull(refClass, REF_CLASS);
if (refId != null && model.hasItemWithId(refId)) {
final X ref = model.getItemWithId(refId, refClass).orElseThrow();
return new Eager<>(ref);
} else if (refQName != null && model.hasItemWithQName(refQName)) {
final X ref = model.getItemWithQName(refQName, refClass);
return new Eager<>(ref);
} else if (refId == null && refQName == null) {
return new None<>(refClass);
} else {
return new Lazy<>(model, refClass, refId, refQName);
}
}
/**
* Creates a nothing element reference.
*
* @param The element type.
* @param refClass The element class.
* @return A nothing element reference.
* @throws IllegalArgumentException When {@code refClass} is {@code null}.
*/
public static MfElementRef of(Class refClass) {
return of(null, refClass, null, null);
}
public static MfElementRef of(MfModel model,
Class refClass) {
return of(model, refClass, null, null);
}
public static MfElementRef of(MfModel model,
Class refClass,
String refId) {
return of(model, refClass, refId, null);
}
public static MfElementRef of(MfModel model,
Class refClass,
Path refQName) {
return of(model, refClass, null, refQName);
}
public static MfElementRef toLazy(MfElementRef srcRef,
MfModel tgtModel) {
return new Lazy<>(tgtModel, srcRef.getRefClass(), srcRef.getRefId(), srcRef.getRefQName());
}
public static List> toLazy(List> srcRefs,
MfModel tgtModel) {
final List> list = new ArrayList<>();
for (final MfElementRef srcRef : srcRefs) {
list.add(toLazy(srcRef, tgtModel));
}
return list;
}
}
/**
* Eager implementation of {@link MfElementRef}.
*
* @param The referenced element type.
*/
class Eager implements MfElementRef {
private final X ref;
public Eager(X ref) {
this.ref = Checks.isNotNull(ref, "ref");
Checks.assertTrue(isValid(), "Invalid ref");
}
@Override
public Class getRefClass() {
@SuppressWarnings("unchecked")
final Class tmp = (Class) ref.getClass();
return tmp;
}
@Override
public String getRefId() {
return ref.getId();
}
@Override
public Path getRefQName() {
if (ref instanceof final MfQNameItem x) {
return x.getQName();
} else {
return null;
}
}
@Override
public boolean isValid() {
return getRefId() != null || getRefQName() != null;
}
@Override
public X get() {
return ref;
}
}
/**
* Lazy implementation of {@link MfElementRef}.
*
* @param The referenced element type.
*/
class Lazy implements MfElementRef {
private final MfModel model;
private final Class refClass;
private final String refId;
private final Path refQName;
private X ref;
public Lazy(MfModel model,
Class refClass,
String refId,
Path refQName) {
this.model = Checks.isNotNull(model, "model");
this.refClass = Checks.isNotNull(refClass, REF_CLASS);
this.refId = refId;
this.refQName = refQName;
}
private void resolve() {
if (ref == null) {
if (refId != null) {
ref = model.getItemWithId(refId, refClass).orElseThrow();
} else if (refQName != null) {
ref = model.getItemWithQName(refQName, refClass);
}
// We can not do anything
}
}
@Override
public Class getRefClass() {
return refClass;
}
@Override
public String getRefId() {
return refId;
}
@Override
public Path getRefQName() {
return refQName;
}
@Override
public boolean isValid() {
try {
resolve();
} catch (final RuntimeException e) {
// ignore
}
return ref != null;
}
@Override
public X get() {
resolve();
if (ref == null) {
throw new ResolutionException("Can not resolve " + this);
}
return ref;
}
@Override
public String toString() {
return "[" + getRefClass().getCanonicalName()
+ " " + getRefId()
+ " " + getRefQName()
+ "]";
}
}
/**
* Implementation of {@link MfElementRef} that references nothing.
*
* @param The referenced element type.
*/
class None implements MfElementRef {
private final Class refClass;
public None(Class refClass) {
this.refClass = Checks.isNotNull(refClass, REF_CLASS);
}
@Override
public Class getRefClass() {
return refClass;
}
@Override
public String getRefId() {
return null;
}
@Override
public Path getRefQName() {
return null;
}
@Override
public boolean isValid() {
return false;
}
@Override
public X get() {
throw new ResolutionException("Can not resolve " + this);
}
@Override
public String toString() {
return "[" + getRefClass().getCanonicalName() + "]";
}
}