diff --git a/eraser-base/build.gradle b/eraser-base/build.gradle index c749833004d9487549536e9f0e72ee8f1deec54c..9ddd9ac3888fa9a9c94b2344feea798353b9aff5 100644 --- a/eraser-base/build.gradle +++ b/eraser-base/build.gradle @@ -54,7 +54,7 @@ String[] ragconnectArguments = [ '--o=src/gen/jastadd', '--logReads', '--logWrites', - '--logIncremental', +// '--logIncremental', // '--verbose', '--rootNode=Root', '--experimental-jastadd-329', diff --git a/eraser-base/libs/ragconnect.base-fatjar-1.0.1.jar b/eraser-base/libs/ragconnect.base-fatjar-1.0.1.jar index 2f36a2b7ae01b43acc5083e6243f4bf7b18c1796..7f68b3d5b5b95463c07ba0b6140488aaa6639fb9 100644 Binary files a/eraser-base/libs/ragconnect.base-fatjar-1.0.1.jar and b/eraser-base/libs/ragconnect.base-fatjar-1.0.1.jar differ diff --git a/eraser-base/src/main/jastadd/Item.jrag b/eraser-base/src/main/jastadd/Item.jrag index 5e481dc5c6072e44a1d4dd2587be05179494110e..cc63fa0bbfe4e081132e763b8825932b6c8d18bb 100644 --- a/eraser-base/src/main/jastadd/Item.jrag +++ b/eraser-base/src/main/jastadd/Item.jrag @@ -330,10 +330,6 @@ aspect ItemHandling { return getStateAsString(); } - syn KeyValuePair Item.getFoo() { - return new KeyValuePair().setKey("State").setValue(getStateAsString()); - } - //--- sendState --- protected void Item.sendState() throws Exception { for (MachineLearningModel model : getRelevantInMachineLearningModels()) { diff --git a/eraser-base/src/main/jastadd/JastAddFixes.jadd b/eraser-base/src/main/jastadd/JastAddFixes.jadd new file mode 100644 index 0000000000000000000000000000000000000000..b4fb0cb8096c448130d09af0a24a9954606f6696 --- /dev/null +++ b/eraser-base/src/main/jastadd/JastAddFixes.jadd @@ -0,0 +1,21 @@ +aspect JastAddFixes { + refine ASTNode public void Opt.init$Children() { + state().enterConstruction(); + //getChild_handler = new ASTNode$DepGraphNode[4]; + state().exitConstruction(); + if (getChild_handler == null) { + //getChild_handler = new ASTNode$DepGraphNode[4]; + setChild(null, 0); + } + } + refine ASTNode public void JastAddList.init$Children() { + state().enterConstruction(); + state().exitConstruction(); + if (getChild_handler == null) { + //addChild(null); + //removeChild(0); + children = new ASTNode[4]; + getChild_handler = new ASTNode$DepGraphNode[4]; + } + } +} diff --git a/eraser-base/src/main/jastadd/Navigation.jrag b/eraser-base/src/main/jastadd/Navigation.jrag index 8281266e2752f40aceba72cefc862956d40395a2..fc4c5e224e33f1fe63fc1f984fb2659fb696a022 100644 --- a/eraser-base/src/main/jastadd/Navigation.jrag +++ b/eraser-base/src/main/jastadd/Navigation.jrag @@ -8,7 +8,6 @@ aspect Navigation { syn java.util.List<Item> SmartHomeEntityModel.items() { java.util.List<Item> result = new java.util.ArrayList<>(); getGroupList().forEach(group -> result.addAll(group.items())); - result.addAll(unknownGroup().items()); return result; } syn java.util.List<Item> Group.items() { @@ -36,10 +35,16 @@ aspect Navigation { inh Group Item.enclosingGroup(); eq Group.getItem().enclosingGroup() = this; eq Group.getGroup().enclosingGroup() = this; - eq SmartHomeEntityModel.unknownGroup().enclosingGroup() = null; eq SmartHomeEntityModel.getGroup().enclosingGroup() = null; eq SmartHomeEntityModel.getActivityItem().enclosingGroup() = null; + inh int Item.myIndexInGroupList(); + eq Group.getItem(int index).myIndexInGroupList() = index; + eq SmartHomeEntityModel.getActivityItem().myIndexInGroupList() = -1; + + syn boolean Item.isItemPrototype() = false; + eq ItemPrototype.isItemPrototype() = true; + //--- containingSmartHomeEntityModel --- inh SmartHomeEntityModel Thing.containingSmartHomeEntityModel(); inh SmartHomeEntityModel Group.containingSmartHomeEntityModel(); diff --git a/eraser-base/src/main/jastadd/Printing.jrag b/eraser-base/src/main/jastadd/Printing.jrag index 18f5f5b106a5f73a27a2d41423abb504b350c0be..fd35ffc0f3f67495e21a1b756466c018e56cf18a 100644 --- a/eraser-base/src/main/jastadd/Printing.jrag +++ b/eraser-base/src/main/jastadd/Printing.jrag @@ -24,10 +24,9 @@ aspect Printing { sb.append(i.prettyPrint()); } for (Group g : groups()) { - sb.append(g.prettyPrint()); - } - if (unknownGroup().getNumItem() > 0 ) { - sb.append(unknownGroup().prettyPrint()); + if (!g.isUnknownGroup() || g.getNumItem() > 0) { + sb.append(g.prettyPrint()); + } } for (ThingType tt : getThingTypeList()) { sb.append(tt.prettyPrint()); @@ -153,7 +152,7 @@ aspect Printing { .addNonDefault("description", getDescription()) .addOptional("type", getType() != null, () -> getType().toString()) .addNonDefault("context", getContext()) - .addOptional("default", hasDefaultValue(), () -> getDefaultValue().getValue()) + .addOptional("default", hasDefaultValue() && getDefaultValue() != null, () -> getDefaultValue().getValue()) .addFlag("required", getRequired()) .build(); } diff --git a/eraser-base/src/main/jastadd/eraser.parser b/eraser-base/src/main/jastadd/eraser.parser index 862fad3cb0f34812b95e57ccba42819355b0d71d..3733d6411690a3c17c26d9cdac293d27eeb8be2d 100644 --- a/eraser-base/src/main/jastadd/eraser.parser +++ b/eraser-base/src/main/jastadd/eraser.parser @@ -53,18 +53,29 @@ import java.util.HashMap; root.doSafeFullTraversal(); // resolve ItemPlaceHolders + java.util.Map<Item, Item> toReplace = new HashMap<>(); for (Group g : root.getSmartHomeEntityModel().groups()) { JastAddList<Item> items = g.getItemList(); for (int i = 0; i < g.getNumItem(); i++) { Item item = items.getChild(i); Item realItem = item.realItem(); if (item != realItem) { - realItem.removeSelf(); - item.removeSelf(); - items.insertChild(realItem, i); + toReplace.put(item, realItem); } } } + for (Map.Entry<Item, Item> entry : toReplace.entrySet()) { + Item placeholder = entry.getKey(); + Item realItem = entry.getValue(); + if (realItem.isActivityItem() && placeholder.myIndexInGroupList() == -1) { + // can not move activity item + System.err.println("Can not put activity item in a group"); + } else { + realItem.removeSelf(); + placeholder.enclosingGroup().setItem(realItem, placeholder.myIndexInGroupList()); + } + } + // resolve GroupPlaceHolders for (Group g : root.getSmartHomeEntityModel().groups()) { JastAddList<Group> groups = g.getGroupList(); @@ -116,7 +127,7 @@ import java.util.HashMap; Root root = thing.t root.r {: insertZero(r.getSmartHomeEntityModel().getThingList(), t); return r; :} - | item.i root.r {: insertZero(r.getSmartHomeEntityModel().unknownGroup().getItemList(), i); return r; :} + | item.i root.r {: insertZero(r.getSmartHomeEntityModel().getUnknownGroup().getItemList(), i); return r; :} | group.g root.r {: insertZero(r.getSmartHomeEntityModel().getGroupList(), g); return r; :} | state_sync_group.ss root.r {: r.addRule(ss); return r; :} | thing_type.tt root.r {: insertZero(r.getSmartHomeEntityModel().getThingTypeList(), tt); return r; :} @@ -190,19 +201,19 @@ Thing thing_body = ; Item item = - COLOR ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ColorItem()); return ib; :} - | CONTACT ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ContactItem()); return ib; :} - | DATE_TIME ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DateTimeItem()); return ib; :} - | DIMMER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DimmerItem()); return ib; :} - | IMAGE ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ImageItem()); return ib; :} - | LOCATION ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new LocationItem()); return ib; :} - | NUMBER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new NumberItem()); return ib; :} - | PLAYER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new PlayerItem()); return ib; :} - | ROLLER_SHUTTER ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new RollerShutterItem()); return ib; :} - | STRING ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new StringItem()); return ib; :} - | SWITCH ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new SwitchItem()); return ib; :} - | ACTIVITY ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new ActivityItem()); return ib; :} - | ITEM COLON item_body.ib SEMICOLON {: ib.setItemWithCorrectType(new DefaultItem()); return ib; :} + COLOR ITEM COLON item_body.ib SEMICOLON {: return new ColorItem().fillFromPrototype(ib); :} + | CONTACT ITEM COLON item_body.ib SEMICOLON {: return new ContactItem().fillFromPrototype(ib); :} + | DATE_TIME ITEM COLON item_body.ib SEMICOLON {: return new DateTimeItem().fillFromPrototype(ib); :} + | DIMMER ITEM COLON item_body.ib SEMICOLON {: return new DimmerItem().fillFromPrototype(ib); :} + | IMAGE ITEM COLON item_body.ib SEMICOLON {: return new ImageItem().fillFromPrototype(ib); :} + | LOCATION ITEM COLON item_body.ib SEMICOLON {: return new LocationItem().fillFromPrototype(ib); :} + | NUMBER ITEM COLON item_body.ib SEMICOLON {: return new NumberItem().fillFromPrototype(ib); :} + | PLAYER ITEM COLON item_body.ib SEMICOLON {: return new PlayerItem().fillFromPrototype(ib); :} + | ROLLER_SHUTTER ITEM COLON item_body.ib SEMICOLON {: return new RollerShutterItem().fillFromPrototype(ib); :} + | STRING ITEM COLON item_body.ib SEMICOLON {: return new StringItem().fillFromPrototype(ib); :} + | SWITCH ITEM COLON item_body.ib SEMICOLON {: return new SwitchItem().fillFromPrototype(ib); :} + | ACTIVITY ITEM COLON item_body.ib SEMICOLON {: return new ActivityItem().fillFromPrototype(ib); :} + | ITEM COLON item_body.ib SEMICOLON {: return new DefaultItem().fillFromPrototype(ib); :} ; // ITEM_TYPE Item: id="" label="" state="" category="" topic="" performance="" metaData={"key":"value"} ; @@ -211,7 +222,7 @@ ItemPrototype item_body = | LABEL EQUALS TEXT.n item_body.i {: i.setLabel(n); return i; :} | STATE EQUALS TEXT.n item_body.i {: i.setStateFromString(n); return i; :} | TOPIC EQUALS TEXT.n item_body.i {: i.setTopicString(n); return i; :} - | CATEGORY EQUALS TEXT.n item_body.i {: i.setCategory(ItemCategory.createRef(n)); return i; :} + | CATEGORY EQUALS TEXT.n item_body.i {: i.setCategoryText(n); return i; :} | PERFORMANCE EQUALS TEXT.n item_body.i {: i.setFrequencySetting(FrequencySetting.createRef(n)); return i; :} | META_DATA EQUALS meta_data.md item_body.i {: i.setMetaData(md); return i; :} | {: ItemPrototype result = new ItemPrototype(); diff --git a/eraser-base/src/main/jastadd/mqtt.jrag b/eraser-base/src/main/jastadd/mqtt.jrag index 1d99dabbd76ea0f236ff8c48036556a2b514d1be..e76d6d5a6277c069e949aec6cc9f66b2827e5904 100644 --- a/eraser-base/src/main/jastadd/mqtt.jrag +++ b/eraser-base/src/main/jastadd/mqtt.jrag @@ -44,8 +44,7 @@ aspect MQTT { } success &= connectReceive.apply(prefix + mqttRoot.getIncomingPrefix() + suffix) & item.connectTriggerStateUpdated("java://localhost/" + suffix, true) & - item.connectTriggerStateUpdated("mqtt://localhost/trigger/" + suffix, true) & - item.connectFoo("mqtt://localhost/foo/" + suffix, true) + item.connectTriggerStateUpdated("mqtt://localhost/trigger/" + suffix, true) ; } return success; diff --git a/eraser-base/src/main/jastadd/shem.connect b/eraser-base/src/main/jastadd/shem.connect index 0a149718b3a676a75fd897d45df1801de95ae12d..794681a5802ab6a8f4d02831e47323dcd62147df 100644 --- a/eraser-base/src/main/jastadd/shem.connect +++ b/eraser-base/src/main/jastadd/shem.connect @@ -9,8 +9,6 @@ receive ItemWithStringState._state ; send Item.triggerStateUpdated(String); -send Item.Foo; - // Mappings StringToDouble maps String s to double {: return Double.parseDouble(s); diff --git a/eraser-base/src/main/jastadd/shem.jrag b/eraser-base/src/main/jastadd/shem.jrag index abbfd07751887f5955b32eb0b45b779d1cbb49dc..d4ff917d45f1d79f53da8d100bc77d4fe09a635b 100644 --- a/eraser-base/src/main/jastadd/shem.jrag +++ b/eraser-base/src/main/jastadd/shem.jrag @@ -4,7 +4,7 @@ aspect SmartHomeEntityModel { } public void SmartHomeEntityModel.addNewItem(Item item) { - unknownGroup().addItem(item); + getUnknownGroup().addItem(item); } public MetaData MetaData.add(String key, String value) { @@ -12,9 +12,22 @@ aspect SmartHomeEntityModel { return this; } - syn nta Group SmartHomeEntityModel.unknownGroup() { - return new Group().setID("Unknown"); +// syn nta Group SmartHomeEntityModel.unknownGroup() { +// return new Group().setID("Unknown").setAggregationFunction(null); +// } + + static final String Group.UnknownGroupName = "Unknown"; + public Group SmartHomeEntityModel.getUnknownGroup() { + for (Group group : getGroupList()) { + if (group.getID().equals(Group.UnknownGroupName)) { + return group; + } + } + Group newUnknownGroup = new Group().setID(Group.UnknownGroupName).setAggregationFunction(null); + addGroup(newUnknownGroup); + return newUnknownGroup; } + syn boolean Group.isUnknownGroup() = getID().equals(Group.UnknownGroupName); syn nta JastAddList<DefaultChannelCategory> SmartHomeEntityModel.defaultChannelCategoryList() { JastAddList<DefaultChannelCategory> result = new JastAddList<>(); @@ -22,28 +35,29 @@ aspect SmartHomeEntityModel { return result; } - rewrite ItemPrototype { - to Item { - Item result = getItemWithCorrectType(); - result.setID(this.getID()); - result.setLabel(this.getLabel()); - result.setMetaData(this.getMetaData()); - result.setTopicString(this.getTopicString()); - if (this.hasCategory()) { - result.setCategory(this.getCategory()); - } - if (!result.isActivityItem()) { - String state = this.getStateAsString(); - result.disableSendState(); - if (state.isEmpty()) { - result.setStateToDefault(); - } else { - result.setStateFromString(state); - } - result.enableSendState(); + public Item Item.fillFromPrototype(ItemPrototype prototype) { + this.setID(prototype.getID()); + this.setLabel(prototype.getLabel()); + //if (prototype.hasMetaData()) { + this.setMetaData(prototype.getMetaData()); + //} else { + // this.setMetaData(null); + //} + this.setTopicString(prototype.getTopicString()); + if (!prototype.getCategoryText().isEmpty()) { + this.setCategory(ItemCategory.createRef(prototype.getCategoryText())); + } + if (!this.isActivityItem()) { + String state = prototype.getStateAsString(); + this.disableSendState(); + if (state.isEmpty()) { + this.setStateToDefault(); + } else { + this.setStateFromString(state); } - return result; + this.enableSendState(); } + return this; } // PlaceHolders diff --git a/eraser-base/src/main/jastadd/shem.relast b/eraser-base/src/main/jastadd/shem.relast index f64d20d1e1851ed42e894d543117f67633f26787..843303f12361f597abf1369957b24a59f765f052 100644 --- a/eraser-base/src/main/jastadd/shem.relast +++ b/eraser-base/src/main/jastadd/shem.relast @@ -29,7 +29,7 @@ rel Channel.LinkedItem* <-> Item.Channel? ; Parameter : DescribableModelElement ::= <Type:ParameterValueType> [DefaultValue:ParameterDefaultValue] <Context:String> <Required:boolean> ; ParameterDefaultValue ::= <Value:String> ; -abstract Item : LabelledModelElement ::= <_fetched_data:boolean> <TopicString> [MetaData] /ItemObserver/ /LastChanged/ /Foo:KeyValuePair/; +abstract Item : LabelledModelElement ::= <_fetched_data:boolean> <TopicString> [MetaData] /ItemObserver/ /LastChanged/ ; rel Item.Category? <-> ItemCategory.Items* ; rel Item.FrequencySetting? -> FrequencySetting ; @@ -61,7 +61,7 @@ StringItem : ItemWithStringState ; SwitchItem : ItemWithBooleanState ; DefaultItem : ItemWithStringState ; ActivityItem : ItemWithDoubleState ; -ItemPrototype : ItemWithStringState ::= ItemWithCorrectType:Item ; // only used for parsing +ItemPrototype : ItemWithStringState ::= <CategoryText> ; // only used for parsing ItemPlaceHolder : ItemWithStringState ; // only used for parsing TupleHSB ::= <Hue:int> <Saturation:int> <Brightness:int> ; @@ -81,4 +81,3 @@ abstract GroupAggregationFunction ; SimpleGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:SimpleGroupAggregationFunctionName> ; ParameterizedGroupAggregationFunction : GroupAggregationFunction ::= <FunctionName:ParameterizedGroupAggregationFunctionName> <Param1:String> <Param2:String> ; - diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java index 5b365efb7b3e184a76396411a659ce624c76a779..2db2993186b9ae238162cdcca81355456c5addba 100644 --- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java +++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/ParserUtils.java @@ -174,7 +174,7 @@ public class ParserUtils { * @param danglingItems A list of items to add to the new group */ public static void addToUnknownGroup(SmartHomeEntityModel model, Collection<Item> danglingItems) { - Group unknownGroup = model.unknownGroup(); + Group unknownGroup = model.getUnknownGroup(); danglingItems.forEach(unknownGroup::addItem); logger.info("Updated unknown group {}", unknownGroup.prettyPrint().trim()); } diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/IncrementalTests.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/IncrementalTests.java new file mode 100644 index 0000000000000000000000000000000000000000..d1ecbef463935916e84866bbd03a0df5160806c3 --- /dev/null +++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/IncrementalTests.java @@ -0,0 +1,47 @@ +package de.tudresden.inf.st.eraser; + +import de.tudresden.inf.st.eraser.jastadd.model.ColorItem; +import de.tudresden.inf.st.eraser.jastadd.model.Group; +import de.tudresden.inf.st.eraser.jastadd.model.ItemPlaceHolder; +import de.tudresden.inf.st.eraser.jastadd.model.Root; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Some special corner case with enabled incremental evaluation. + * + * @author rschoene - Initial contribution + */ +public class IncrementalTests { + + @Test + public void unknownGroupPrettyPrint() { + Root root = Root.createEmptyRoot(); + String output = root.getSmartHomeEntityModel().getUnknownGroup().prettyPrint(); + assertNotNull(output); + } + + @Test + public void newGroupPrettyPrint() { + String output = new Group().setID("group2").setAggregationFunction(null).prettyPrint(); + assertNotNull(output); + } + + @Test + public void itemPlaceHolderPrettyPrint() { + ItemPlaceHolder placeholder = new ItemPlaceHolder().setID("placeholder"); + placeholder.setMetaData(null); + String output = placeholder.prettyPrint(); + assertNotNull(output); + } + + @Test + public void colorItemPrettyPrint() { + ColorItem item = new ColorItem().setID("colored"); + item.setStateFromString("100,100,100"); + item.setMetaData(null); + String output = item.prettyPrint(); + assertNotNull(output); + } +} diff --git a/eraser-base/src/test/resources/tests/mini/Test.properties b/eraser-base/src/test/resources/tests/mini/Test.properties new file mode 100644 index 0000000000000000000000000000000000000000..616a3deddd4decffd5d0e6e3db7bb8ce3873562a --- /dev/null +++ b/eraser-base/src/test/resources/tests/mini/Test.properties @@ -0,0 +1 @@ +result=OUTPUT_PASS diff --git a/eraser-base/src/test/resources/tests/mini/input.eraser b/eraser-base/src/test/resources/tests/mini/input.eraser new file mode 100644 index 0000000000000000000000000000000000000000..55fcd00b316bba3c4aa32295069216febb36dc55 --- /dev/null +++ b/eraser-base/src/test/resources/tests/mini/input.eraser @@ -0,0 +1,4 @@ +Group: id="g" items=["datetime1", "default1"] ; + +DateTime Item : id="datetime1" label="a DateTime Item" state="1543415826"; +Item : id="default1" label="a Default Item"; diff --git a/eraser-base/src/test/resources/tests/mini/output.eraser b/eraser-base/src/test/resources/tests/mini/output.eraser new file mode 100644 index 0000000000000000000000000000000000000000..c9f6fd5d34dd9ba7225b38e5dccb0098ea8e5893 --- /dev/null +++ b/eraser-base/src/test/resources/tests/mini/output.eraser @@ -0,0 +1,3 @@ +DateTime Item: id="datetime1" label="a DateTime Item" state="1970-01-18T20:43:35.826Z" ; +Item: id="default1" label="a Default Item" state="" ; +Group: id="g" items=["datetime1", "default1"] ;