Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- editor field sequence determined by field sequence in model
- defining ui groups for applications
  • Loading branch information
eichelbe committed Jan 15, 2024
1 parent 51ac670 commit 73b73b8
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@ project Applications {
import Nameplate;

annotate BindingTime bindingTime = BindingTime::compile to .;
annotate Integer uiGroup = UiMandatoryGroup1 to .;

enum AppPackagingSchema {SpringFat, ZipWithClasspath};

compound Application refines VersionedElement {
Id id;
NonEmptyNameString name;
String description = "";
String artifact = "";
assign (uiGroup = UiOptionalGroup1) to {
String description = "";
String artifact = "";
}
setOf(refTo(ServiceMesh)) services; // no services is ok, then the artifact must have hand-crafted services
NameplateInfo nameplateInfo;
Boolean createContainer = false;
Boolean debug = false;
String cmdArg; // for now, comma separated
setOf(refTo(Server)) servers = {}; // additional server instances to be started
setOf(AppPackagingSchema) packaging = { AppPackagingSchema::SpringFat };
assign (uiGroup = UiOptionalGroup1) to {
Boolean createContainer = false;
Boolean debug = false;
String cmdArg; // for now, comma separated
setOf(refTo(Server)) servers = {}; // additional server instances to be started
setOf(AppPackagingSchema) packaging = { AppPackagingSchema::SpringFat };
}

Constraint idOk = matches(id, "^[^@]+$");
Constraint packagingOk = packaging.size() >= 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,8 @@ void mapVariable(IDecisionVariable var, SubmodelElementCollectionBuilder builder
varBuilder = builder.createSubmodelElementCollectionBuilder(
AasUtils.fixId(varName), false, false);
for (int member = 0; member < var.getNestedElementsCount(); member++) {
mapVariable(var.getNestedElement(member), varBuilder, null);
IDecisionVariable elt = var.getNestedElement(member);
mapVariable(elt, varBuilder, null);
}
} else if (TypeQueries.isContainer(rVarType)) {
boolean isSequence = TypeQueries.isSequence(rVarType);
Expand Down Expand Up @@ -1379,7 +1380,6 @@ private void addMetaProperties(IDecisionVariable var, IDatatype varType,
LoggerFactory.getLogger(AasIvmlMapper.class).warn(
"Cannot find type ServiceBase. No service will have a AAS URL. {}", e.getMessage());
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import net.ssehub.easy.varModel.model.datatypes.OclKeyWords;
import net.ssehub.easy.varModel.model.datatypes.Reference;
import net.ssehub.easy.varModel.model.datatypes.Sequence;
import net.ssehub.easy.varModel.model.datatypes.TypeQueries;
import net.ssehub.easy.varModel.model.values.Value;
import net.ssehub.easy.varModel.model.values.ValueDoesNotMatchTypeException;
import net.ssehub.easy.varModel.persistency.IVMLWriter;
Expand Down Expand Up @@ -514,7 +513,7 @@ public synchronized void changeValues(Map<String, String> values) throws Executi
protected void setValue(IDecisionVariable var, String expression, EvaluationVisitor eval, AssignmentState state)
throws ExecutionException {
try {
ConstraintSyntaxTree cst = createExpression(expression, var.getConfiguration().getProject());
ConstraintSyntaxTree cst = createExpression(null, expression, var.getConfiguration().getProject());
if (null == eval) {
eval = new EvaluationVisitor();
}
Expand All @@ -531,19 +530,23 @@ protected void setValue(IDecisionVariable var, String expression, EvaluationVisi
/**
* Creates an IVML expression syntax tree for {@code expression}.
*
* @param type the target type, may be <b>null</b> for none
* @param expression the expression
* @param scope the resolution scope, may be <b>null</b> for the root project
* @return the syntax tree
* @throws ExecutionException if the expression cannot be created, e.g., due to syntactic or semantic errors
*/
protected ConstraintSyntaxTree createExpression(String expression, Project scope) throws ExecutionException {
protected ConstraintSyntaxTree createExpression(IDatatype type, String expression, Project scope)
throws ExecutionException {
try {
if (null == scope) {
scope = getIvmlConfiguration().getProject();
}
return ModelUtility.INSTANCE.createExpression(expression, scope);
} catch (ConstraintSyntaxException | CSTSemanticException e) {
throw new ExecutionException(e.getMessage(), null);
return ModelUtility.INSTANCE.createExpression(type, expression, scope);
} catch (CSTSemanticException e) {
throw new ExecutionException("IVML expression semantic error: " + e.getMessage(), null);
} catch (ConstraintSyntaxException e) {
throw new ExecutionException("IVML expression syntax error: " + e.getMessage(), null);
}
}

Expand All @@ -559,7 +562,7 @@ protected ConstraintSyntaxTree createExpression(String expression, Project scope
protected Constraint createAssignment(AbstractVariable varDecl, String valueEx, Project prj)
throws ExecutionException {
try {
Constraint c = new Constraint(createExpression(varDecl.getName() + "=" + valueEx, prj), prj);
Constraint c = new Constraint(createExpression(null, varDecl.getName() + "=" + valueEx, prj), prj);
prj.add(c);
return c;
} catch (CSTSemanticException e) {
Expand All @@ -577,10 +580,8 @@ protected Constraint createAssignment(AbstractVariable varDecl, String valueEx,
*/
protected void setValue(AbstractVariable var, String expression) throws ExecutionException {
try {
if (TypeQueries.isCompound(var.getType()) && expression.trim().startsWith("{")) {
expression = IvmlDatatypeVisitor.getUnqualifiedType(var.getType()) + expression;
} // container type may require special treatment
ConstraintSyntaxTree cst = createExpression(expression, var.getProject());
IDatatype type = var.getType();
ConstraintSyntaxTree cst = createExpression(type, expression, var.getProject());
cst.inferDatatype();
var.setValue(cst);
} catch (ValueDoesNotMatchTypeException | CSTSemanticException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

package de.iip_ecosphere.platform.configuration.ivml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
Expand Down Expand Up @@ -64,6 +66,7 @@ class TypeMapper {
private Predicate<AbstractVariable> variableFilter;
private Stack<Map<String, Object>> assignments = new Stack<>();
private Function<String, String> metaShortId;
private Map<String, Integer> uiGroups = new HashMap<>();

/**
* Creates a type mapper instance.
Expand Down Expand Up @@ -163,9 +166,12 @@ private void mapCompoundType(Compound type) {
if (!isDoneType(typeId)) {
SubmodelElementCollectionBuilder typeB = builder.createSubmodelElementCollectionBuilder(
typeId, false, false);
Set<String> doneSlots = new HashSet<>();
mapCompoundSlots(type, type, typeB, doneSlots);
mapRefines(type, typeB, doneSlots);
Map<String, SubmodelElementCollectionBuilder> doneSlots = new HashMap<>();
mapCompoundSlots(type, type, type, typeB, doneSlots);
mapRefines(type, type, typeB, doneSlots);
for (SubmodelElementCollectionBuilder builder : sortMembers(type, doneSlots)) {
builder.build();
}
typeB.createPropertyBuilder(metaShortId.apply("abstract"))
.setValue(Type.BOOLEAN, type.isAbstract())
.build();
Expand All @@ -176,6 +182,101 @@ private void mapCompoundType(Compound type) {
typeB.build();
}
}

/**
* Sort members for display sequence in UI.
*
* @param type the type to sort the members for
* @param slots the SME builders per slot
* @return the sorted members
*/
private List<SubmodelElementCollectionBuilder> sortMembers(Compound type,
Map<String, SubmodelElementCollectionBuilder> slots) {
List<SubmodelElementCollectionBuilder> result = new ArrayList<>();
Set<String> done = null;
if (type instanceof Compound) {
List<String> names = new ArrayList<String>();
done = new HashSet<>();
collectMemberNames((Compound) type, names, done);
Map<Integer, List<SubmodelElementCollectionBuilder>> namesByUiGroups = new HashMap<>();
for (String name : names) {
SubmodelElementCollectionBuilder elt = slots.get(name);
if (null != elt) {
int uiGroup = getUiGroup(type.getName(), name) / 100;
List<SubmodelElementCollectionBuilder> tmp = namesByUiGroups.get(uiGroup);
if (null == tmp) {
tmp = new ArrayList<>();
namesByUiGroups.put(uiGroup, tmp);
}
tmp.add(elt);
done.remove(name);
} // EASy problems with Any
}
add(namesByUiGroups, result, 1, 1); // mandatory UI groups
add(namesByUiGroups, result, 0, 0); // invisible UI group (optional, may not be there)
add(namesByUiGroups, result, -1, -1); // optional UI groups
}
return result;
}

/**
* Adds all entries from {@code src} with matching key to {@code tgt}. Assumption: There are no holes between the
* groups.
*
* @param src source structure
* @param tgt target structure
* @param start the start ui group/key
* @param inc the increment, terminate immediately after first transfer if {@code 0}
*/
private void add(Map<Integer, List<SubmodelElementCollectionBuilder>> src,
List<SubmodelElementCollectionBuilder> tgt, int start, int inc) {
int u = start;
while (src.get(u) != null) {
tgt.addAll(src.get(u));
if (inc == 0) {
break;
}
u += inc;
}
}

/**
* Collects the member names for type.
*
* @param type the compound type
* @param result the resulting name sequence
* @param done the done names
*/
private void collectMemberNames(Compound type, List<String> result, Set<String> done) {
if (!type.getProject().getName().equals("MetaConcepts")) {
for (int r = 0; r < type.getRefinesCount(); r++) { // important: parents go first
collectMemberNames(type.getRefines(r), result, done);
}
collectMemberNamesContainer(type, result, done);
}
}

/**
* Collects the member names for the given container.
*
* @param cnt the container
* @param result the resulting name sequence
* @param done the done names
*/
private void collectMemberNamesContainer(IDecisionVariableContainer cnt, List<String> result, Set<String> done) {
for (int e = 0; e < cnt.getModelElementCount(); e++) { // important: keep sequence
ContainableModelElement element = cnt.getModelElement(e);
if (element instanceof AbstractVariable) {
String name = element.getName();
if (!done.contains(name)) {
done.add(name);
result.add(name); // uiGroup % 100 might affect position here
}
} else if (element instanceof AttributeAssignment) {
collectMemberNamesContainer((AttributeAssignment) element, result, done);
}
}
}

/**
* Maps an IVML type. May be called for duplicates but leads only to one entry.
Expand Down Expand Up @@ -281,14 +382,16 @@ private static String getRefines(Compound type) {
* Maps refines of a compound type.
*
* @param type the type
* @param topType the top-level type the mapping started with
* @param typeB the type builder
* @param doneSlots already done slot names
*/
private void mapRefines(Compound type, SubmodelElementCollectionBuilder typeB, Set<String> doneSlots) {
private void mapRefines(Compound type, Compound topType, SubmodelElementCollectionBuilder typeB,
Map<String, SubmodelElementCollectionBuilder> doneSlots) {
for (int r = 0; r < type.getRefinesCount(); r++) {
Compound refines = type.getRefines(r);
mapCompoundSlots(refines, refines, typeB, doneSlots);
mapRefines(refines, typeB, doneSlots);
mapCompoundSlots(refines, refines, topType, typeB, doneSlots);
mapRefines(refines, topType, typeB, doneSlots);
}
}

Expand All @@ -297,13 +400,14 @@ private void mapRefines(Compound type, SubmodelElementCollectionBuilder typeB, S
*
* @param cnt the container (compound or annotation assignment)
* @param type the containing compound type
* @param topType the top-level type the mapping started with
* @param typeB the type builder
* @param doneSlots already done slot names
*/
private void mapCompoundSlots(IDecisionVariableContainer cnt, Compound type, SubmodelElementCollectionBuilder typeB,
Set<String> doneSlots) {
private void mapCompoundSlots(IDecisionVariableContainer cnt, Compound type, Compound topType,
SubmodelElementCollectionBuilder typeB, Map<String, SubmodelElementCollectionBuilder> doneSlots) {
for (int i = 0; i < cnt.getElementCount(); i++) {
mapCompoundSlot(cnt.getElement(i), type, typeB, doneSlots);
mapCompoundSlot(cnt.getElement(i), type, topType, typeB, doneSlots);
}
for (int a = 0; a < cnt.getAssignmentCount(); a++) {
AttributeAssignment assng = cnt.getAssignment(a);
Expand All @@ -326,7 +430,7 @@ private void mapCompoundSlots(IDecisionVariableContainer cnt, Compound type, Sub
}
}
assignments.push(thisLevelAssignments);
mapCompoundSlots(assng, type, typeB, doneSlots);
mapCompoundSlots(assng, type, topType, typeB, doneSlots);
assignments.pop();
}
}
Expand Down Expand Up @@ -385,27 +489,28 @@ static void addMetaDefault(net.ssehub.easy.varModel.confModel.Configuration cfg,
*
* @param slot the slot to map
* @param type the containing compound type
* @param topType the top-level type the mapping started with
* @param typeB the type builder
* @param doneSlots already done slot names
*/
private void mapCompoundSlot(DecisionVariableDeclaration slot, Compound type,
SubmodelElementCollectionBuilder typeB, Set<String> doneSlots) {
private void mapCompoundSlot(DecisionVariableDeclaration slot, Compound type, Compound topType,
SubmodelElementCollectionBuilder typeB, Map<String, SubmodelElementCollectionBuilder> doneSlots) {
// if we get into trouble with property ids, we have to sub-structure that
String slotName = AasUtils.fixId(slot.getName());
if (!doneSlots.contains(slotName) && variableFilter.test(slot)) {
doneSlots.add(slotName);
if (!doneSlots.containsKey(slotName) && variableFilter.test(slot)) {
String lang = AasIvmlMapper.getLang();
IDatatype slotType = slot.getType();
SubmodelElementCollectionBuilder propB = typeB.createSubmodelElementCollectionBuilder(slotName,
true, false);
doneSlots.put(slotName, propB);
propB.createPropertyBuilder("name")
.setValue(Type.STRING, slotName)
.setDescription(new LangString(lang, ModelInfo.getCommentSafe(slot)))
.build();
propB.createPropertyBuilder("type")
.setValue(Type.STRING, IvmlDatatypeVisitor.getUnqualifiedType(slotType))
.build();
int uiGroup = 1; // default, always there, mandatory
int uiGroup = 100; // default, always there, mandatory
Object tmp = getAssignmentValue("uiGroup");
if (tmp instanceof Integer) {
uiGroup = (Integer) tmp;
Expand All @@ -417,17 +522,32 @@ private void mapCompoundSlot(DecisionVariableDeclaration slot, Compound type,
}
}
}
uiGroups.put(topType.getName() + "." + slotName, uiGroup);
if (uiGroup >= 100 || uiGroup <= 100) {
uiGroup /= 100;
}
propB.createPropertyBuilder("uiGroup")
.setValue(Type.INTEGER, uiGroup)
.build();
addMetaDefault(cfg.getConfiguration(), slot, propB, metaShortId);
propB.build();
if (slotType != type) {
mapType(slotType);
}
}
}

/**
* Returns the recorded UI group.
*
* @param type the type name
* @param slot the name of the slot within {@code type}
* @return the uiGroup value, defaults to 100
*/
public int getUiGroup(String type, String slot) {
Integer tmp = uiGroups.get(type + "." + slot);
return null == tmp ? 100 : tmp;
}

/**
* Returns the top-most collected assignment value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,11 @@ public void testCreateDeleteVariable() throws IOException, ExecutionException {
assertIvmlFileChange("AllTypes", false, "rec1");
assertIvmlFileChange("AllServices", true, "test1");

mapper.createVariable("test2", "setOf(Integer)", "{25, 27}");
assertIvmlFileChange("AllConstants", false, "test2");
mapper.deleteVariable("test2");
assertIvmlFileChange("AllConstants", true, "test2");

stopEasy(lcd);
setupIvmlFiles(); // revert changes
}
Expand Down

0 comments on commit 73b73b8

Please sign in to comment.