cdc.mf.transform.MfTransformer Maven / Gradle / Ivy
package cdc.mf.transform;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import cdc.mf.model.MfAbstractChildElement;
import cdc.mf.model.MfAggregation;
import cdc.mf.model.MfAssociation;
import cdc.mf.model.MfClass;
import cdc.mf.model.MfComposition;
import cdc.mf.model.MfConstraint;
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.MfInterface;
import cdc.mf.model.MfModel;
import cdc.mf.model.MfOperation;
import cdc.mf.model.MfPackage;
import cdc.mf.model.MfParameter;
import cdc.mf.model.MfProperty;
import cdc.mf.model.MfSpecialization;
import cdc.mf.model.MfTag;
import cdc.mf.model.MfTip;
import cdc.util.lang.Checks;
/**
* Class used to recursively transform a source model into a target one.
*
*
*
* @author Damien Carbonne
*/
public final class MfTransformer {
/** The model transformer. */
private final MfModelTransformer modelTransformer;
/** The transformer to use for each child element class. */
private final Map, MfChildTransformer, ?>> best = new HashMap<>();
private MfTransformer(Builder builder) {
this.modelTransformer = Checks.isNotNull(builder.modelTransformer, "modelTransformer");
final List> sorted = new ArrayList<>(builder.childTransformers);
// Last matching transformer must be selected, so we reverse the order
Collections.reverse(sorted);
// Select the best transformer for each child element class
addBest(MfAggregation.class, sorted);
addBest(MfAssociation.class, sorted);
addBest(MfClass.class, sorted);
addBest(MfComposition.class, sorted);
addBest(MfConstraint.class, sorted);
addBest(MfDocumentation.class, sorted);
addBest(MfEnumeration.class, sorted);
addBest(MfEnumerationValue.class, sorted);
addBest(MfImplementation.class, sorted);
addBest(MfInterface.class, sorted);
addBest(MfOperation.class, sorted);
addBest(MfPackage.class, sorted);
addBest(MfParameter.class, sorted);
addBest(MfProperty.class, sorted);
addBest(MfSpecialization.class, sorted);
addBest(MfTag.class, sorted);
addBest(MfTip.class, sorted);
}
/**
* @param The child element type.
* @param The parent type.
* @param cls The child element class.
* @param sorted The sorted list of available transformers where the selection must be done.
* @return The first matching transformer in {@code sorted}, or a cloner if no matching transformer is found.
*/
private static , P extends MfElement> MfChildTransformer, ?>
getFirstOrDef(Class cls,
List> sorted) {
for (final MfChildTransformer, ?> t : sorted) {
if (t.getChildClass().isAssignableFrom(cls)) {
return t;
}
}
return MfChildTransformer.cloner(cls);
}
/**
* Selects and adds the best transformer that should be used for a class.
*
* @param The child element type.
* @param The parent type.
* @param cls The child element class.
* @param sorted The sorted list of available transformers where the selection must be done.
*/
private , P extends MfElement> void addBest(Class cls,
List> sorted) {
best.put(cls, getFirstOrDef(cls, sorted));
}
/**
* @param src The source child element.
* @return The child element transformer to use for {@code src}.
*/
private MfChildTransformer, ?> getBest(MfElement src) {
return best.get(src.getClass());
}
/**
* Transforms a source model into a target model.
*
* @param srcModel The source model.
* @return The transformation of {@code srcModel} into a target model.
*/
public MfModel transform(MfModel srcModel) {
// Locally transform model
final MfModel tgtModel = modelTransformer.transform(srcModel);
// Recursively transform children
recurse(srcModel, tgtModel);
return tgtModel;
}
/**
* Recursively transform all children of a source parent into children of a target parent.
*
* @param srcParent The source parent element.
* @param tgtParent The target parent element.
*/
private void recurse(MfElement srcParent,
MfElement tgtParent) {
// Iterate on all source children
for (final MfElement srcChild : srcParent.getChildren()) {
// Retrieve the appropriate transformer
final MfChildTransformer, ?> t = getBest(srcChild);
// Transform the child
final MfElement tgtChild = t.transformRaw(srcChild, tgtParent);
if (tgtChild != null) {
recurse(srcChild, tgtChild);
}
}
}
public static Builder builder() {
return new Builder();
}
/**
* Builder of {@link MfTransformer}.
*
* @author Damien Carbonne
*/
public static final class Builder {
private MfModelTransformer modelTransformer = MfModelTransformer.cloner();
private final List> childTransformers = new ArrayList<>();
Builder() {
}
/**
* Sets the model local transformer.
*
* @param transformer The transformer.
* @return This builder.
*/
public Builder modelTransformer(MfModelTransformer transformer) {
this.modelTransformer = transformer;
return this;
}
/**
* Adds a child element local transformer.
*
* WARNING: the order of additions matters.
* For each element, the last matching transformer is selected.
*
* @param transformer The transformer.
* @return This builder.
*/
public Builder childTransformer(MfChildTransformer, ?> transformer) {
this.childTransformers.add(transformer);
return this;
}
public MfTransformer build() {
return new MfTransformer(this);
}
}
}