mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
DSL Items Parser: Fix incorrect parsing of keywords encountered in the wrong context (#4928)
* DSL Items Parser: Fix incorrect parsing of keywords encountered in the wrong context When specifying tag names that match one of the valid item types, e.g. `Switch`, the parser incorrectly treated it as a new start of an item definition. Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
This commit is contained in:
+8
-1
@@ -330,7 +330,14 @@ public class ModelRepositoryImpl implements ModelRepository {
|
||||
final org.eclipse.emf.common.util.Diagnostic diagnostic = safeEmf
|
||||
.call(() -> Diagnostician.INSTANCE.validate(resource.getContents().getFirst()));
|
||||
for (org.eclipse.emf.common.util.Diagnostic d : diagnostic.getChildren()) {
|
||||
warnings.add(d.getMessage());
|
||||
if (d.getSeverity() == org.eclipse.emf.common.util.Diagnostic.ERROR) {
|
||||
errors.add(d.getMessage());
|
||||
} else {
|
||||
warnings.add(d.getMessage());
|
||||
}
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
// see https://github.com/eclipse/smarthome/issues/3335
|
||||
|
||||
@@ -22,6 +22,7 @@ Import-Package: javax.measure,\
|
||||
org.openhab.core.items,\
|
||||
org.openhab.core.items.dto,\
|
||||
org.openhab.core.items.fileconverter,\
|
||||
org.openhab.core.library,\
|
||||
org.openhab.core.library.items,\
|
||||
org.openhab.core.library.types,\
|
||||
org.openhab.core.thing.util,\
|
||||
|
||||
@@ -14,31 +14,18 @@ ItemModel:
|
||||
;
|
||||
|
||||
ModelItem:
|
||||
(ModelNormalItem | ModelGroupItem) name=ID
|
||||
type=ModelItemType ('(' args+=(ID|STRING) (',' args+=(ID|STRING))* ')')?
|
||||
name=ID
|
||||
(label=STRING)?
|
||||
('<' icon=Icon '>')?
|
||||
('(' groups+=ID (',' groups+=ID)* ')')?
|
||||
('(' groups+=ID (',' groups+=ID)* ')')?
|
||||
('[' tags+=(ID|STRING) (',' tags+=(ID|STRING))* ']')?
|
||||
('{' bindings+=ModelBinding (',' bindings+=ModelBinding)* '}')?
|
||||
;
|
||||
|
||||
ModelGroupItem:
|
||||
{ModelGroupItem} 'Group' (':' type=ModelItemType ( ':' function=ModelGroupFunction ('(' args+=(ID|STRING) (',' args+=(ID|STRING))* ')')?)?)?
|
||||
;
|
||||
|
||||
enum ModelGroupFunction:
|
||||
EQUALITY='EQUALITY' | AND='AND' | OR='OR' | NAND='NAND' | NOR='NOR' | XOR='XOR' | AVG='AVG' | MEDIAN='MEDIAN' | SUM='SUM' | MAX='MAX' | MIN='MIN' | COUNT='COUNT' | LATEST='LATEST' | EARLIEST='EARLIEST'
|
||||
;
|
||||
|
||||
ModelNormalItem:
|
||||
type=ModelItemType
|
||||
('{' bindings+=ModelBinding (',' bindings+=ModelBinding)* '}')?
|
||||
;
|
||||
|
||||
// Supports item types with up to 4 colon-separated segments, e.g., Group:Number:Dimension:MAX
|
||||
ModelItemType:
|
||||
BaseModelItemType | ('Number' (':' ID)?)
|
||||
;
|
||||
BaseModelItemType:
|
||||
'Switch' | 'Rollershutter' | 'String' | 'Dimmer' | 'Contact' | 'DateTime' | 'Color' | 'Player' | 'Location' | 'Call' | 'Image'
|
||||
ID (':' ID (':' ID (':' ID )?)?)?
|
||||
;
|
||||
|
||||
ModelBinding:
|
||||
@@ -56,7 +43,7 @@ ValueType returns ecore::EJavaObject:
|
||||
STRING | NUMBER | BOOLEAN
|
||||
;
|
||||
|
||||
BOOLEAN returns ecore::EBoolean:
|
||||
BOOLEAN returns ecore::EBoolean:
|
||||
'true' | 'false'
|
||||
;
|
||||
|
||||
|
||||
+4
-2
@@ -25,8 +25,10 @@ class ItemsFormatter extends AbstractDeclarativeFormatter {
|
||||
@Inject extension ItemsGrammarAccess
|
||||
|
||||
override protected void configureFormatting(FormattingConfig c) {
|
||||
c.setLinewrap(1, 1, 2).before(modelGroupItemRule)
|
||||
c.setLinewrap(1, 1, 2).before(modelNormalItemRule)
|
||||
c.setLinewrap(1, 1, 2).before(modelItemRule)
|
||||
|
||||
// No space between the item type and the opening parenthesis for the group function arguments
|
||||
c.setNoSpace().between(modelItemTypeRule, modelItemAccess.leftParenthesisKeyword_1_0)
|
||||
|
||||
c.setNoSpace().withinKeywordPairs("<", ">")
|
||||
c.setNoSpace().withinKeywordPairs("(", ")")
|
||||
|
||||
+89
-59
@@ -47,10 +47,7 @@ import org.openhab.core.model.item.BindingConfigParseException;
|
||||
import org.openhab.core.model.item.BindingConfigReader;
|
||||
import org.openhab.core.model.items.ItemModel;
|
||||
import org.openhab.core.model.items.ModelBinding;
|
||||
import org.openhab.core.model.items.ModelGroupFunction;
|
||||
import org.openhab.core.model.items.ModelGroupItem;
|
||||
import org.openhab.core.model.items.ModelItem;
|
||||
import org.openhab.core.model.items.ModelNormalItem;
|
||||
import org.openhab.core.types.StateDescriptionFragment;
|
||||
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||
import org.openhab.core.types.StateDescriptionFragmentProvider;
|
||||
@@ -234,62 +231,54 @@ public class GenericItemProvider extends AbstractProvider<Item>
|
||||
}
|
||||
|
||||
private @Nullable Item createItemFromModelItem(ModelItem modelItem, String modelName) {
|
||||
Item item;
|
||||
if (modelItem instanceof ModelGroupItem modelGroupItem) {
|
||||
Item baseItem;
|
||||
try {
|
||||
baseItem = createItemOfType(modelGroupItem.getType(), modelGroupItem.getName());
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Error creating base item for group item '{}', item will be ignored: {}",
|
||||
modelGroupItem.getName(), e.getMessage());
|
||||
return null;
|
||||
}
|
||||
if (baseItem != null) {
|
||||
// if the user did not specify a function the first value of the enum in xtext (EQUAL) will be used
|
||||
ModelGroupFunction function = modelGroupItem.getFunction();
|
||||
item = applyGroupFunction(baseItem, modelGroupItem, function);
|
||||
} else {
|
||||
item = new GroupItem(modelGroupItem.getName());
|
||||
}
|
||||
} else {
|
||||
ModelNormalItem normalItem = (ModelNormalItem) modelItem;
|
||||
try {
|
||||
item = createItemOfType(normalItem.getType(), normalItem.getName());
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Error creating item '{}', item will be ignored: {}", normalItem.getName(),
|
||||
e.getMessage());
|
||||
return null;
|
||||
}
|
||||
String itemType = modelItem.getType();
|
||||
if (itemType == null || itemType.isBlank()) {
|
||||
logger.warn("Item '{}' has no type defined, ignoring it.", modelItem.getName());
|
||||
return null;
|
||||
}
|
||||
if (item instanceof ActiveItem activeItem) {
|
||||
String label = modelItem.getLabel();
|
||||
String format = extractFormat(label);
|
||||
if (format != null) {
|
||||
label = label.substring(0, label.indexOf("[")).trim();
|
||||
Map<String, String> formatters = Objects
|
||||
.requireNonNull(stateFormattersMap.computeIfAbsent(modelName, k -> new HashMap<>()));
|
||||
formatters.put(modelItem.getName(), format);
|
||||
if (!isIsolatedModel(modelName)) {
|
||||
stateDescriptionFragments.put(modelItem.getName(),
|
||||
StateDescriptionFragmentBuilder.create().withPattern(format).build());
|
||||
}
|
||||
} else {
|
||||
Map<String, String> formatters = stateFormattersMap.get(modelName);
|
||||
if (formatters != null) {
|
||||
formatters.remove(modelItem.getName());
|
||||
if (formatters.isEmpty()) {
|
||||
stateFormattersMap.remove(modelName);
|
||||
|
||||
try {
|
||||
String[] itemTypeSegments = itemType.split(ItemUtil.EXTENSION_SEPARATOR);
|
||||
String mainItemType = itemTypeSegments[0];
|
||||
|
||||
Item item = switch (mainItemType) {
|
||||
case "Group" -> createGroupItem(modelItem, itemTypeSegments);
|
||||
default -> createItemOfType(itemType, modelItem.getName());
|
||||
};
|
||||
|
||||
if (item instanceof ActiveItem activeItem) {
|
||||
String label = modelItem.getLabel();
|
||||
String format = extractFormat(label);
|
||||
if (format != null) {
|
||||
label = label.substring(0, label.indexOf("[")).trim();
|
||||
Map<String, String> formatters = Objects
|
||||
.requireNonNull(stateFormattersMap.computeIfAbsent(modelName, k -> new HashMap<>()));
|
||||
formatters.put(modelItem.getName(), format);
|
||||
if (!isIsolatedModel(modelName)) {
|
||||
stateDescriptionFragments.put(modelItem.getName(),
|
||||
StateDescriptionFragmentBuilder.create().withPattern(format).build());
|
||||
}
|
||||
} else {
|
||||
Map<String, String> formatters = stateFormattersMap.get(modelName);
|
||||
if (formatters != null) {
|
||||
formatters.remove(modelItem.getName());
|
||||
if (formatters.isEmpty()) {
|
||||
stateFormattersMap.remove(modelName);
|
||||
}
|
||||
}
|
||||
if (!isIsolatedModel(modelName)) {
|
||||
stateDescriptionFragments.remove(modelItem.getName());
|
||||
}
|
||||
}
|
||||
if (!isIsolatedModel(modelName)) {
|
||||
stateDescriptionFragments.remove(modelItem.getName());
|
||||
}
|
||||
activeItem.setLabel(label);
|
||||
activeItem.setCategory(modelItem.getIcon());
|
||||
assignTags(modelItem, activeItem);
|
||||
return item;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
activeItem.setLabel(label);
|
||||
activeItem.setCategory(modelItem.getIcon());
|
||||
assignTags(modelItem, activeItem);
|
||||
return item;
|
||||
} else {
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Error creating item '{}', item will be ignored: {}", modelItem.getName(), e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -312,14 +301,14 @@ public class GenericItemProvider extends AbstractProvider<Item>
|
||||
}
|
||||
}
|
||||
|
||||
private GroupItem applyGroupFunction(Item baseItem, ModelGroupItem modelGroupItem, ModelGroupFunction function) {
|
||||
private GroupItem applyGroupFunction(Item baseItem, ModelItem modelItem, String function) {
|
||||
GroupFunctionDTO dto = new GroupFunctionDTO();
|
||||
dto.name = function.getName();
|
||||
dto.params = modelGroupItem.getArgs().toArray(new String[0]);
|
||||
dto.name = function;
|
||||
dto.params = modelItem.getArgs().toArray(new String[0]);
|
||||
|
||||
GroupFunction groupFunction = ItemDTOMapper.mapFunction(baseItem, dto);
|
||||
|
||||
return new GroupItem(modelGroupItem.getName(), baseItem, groupFunction);
|
||||
return new GroupItem(modelItem.getName(), baseItem, groupFunction);
|
||||
}
|
||||
|
||||
private void dispatchBindingsPerItemType(String[] itemTypes) {
|
||||
@@ -529,6 +518,47 @@ public class GenericItemProvider extends AbstractProvider<Item>
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GroupItem based on the given ModelItem and item type segments.
|
||||
*
|
||||
* @param modelItem The ModelItem to create the GroupItem from.
|
||||
* @param itemTypeSegments The segments of the item type.
|
||||
* @return A new GroupItem or null if the item type is invalid.
|
||||
*/
|
||||
private @Nullable GroupItem createGroupItem(ModelItem modelItem, String[] itemTypeSegments) {
|
||||
if (itemTypeSegments.length == 1) {
|
||||
// Just plain "Group" with no base type
|
||||
return new GroupItem(modelItem.getName());
|
||||
}
|
||||
|
||||
String function = GroupFunction.DEFAULT;
|
||||
|
||||
String baseItemType = switch (itemTypeSegments.length) {
|
||||
case 2 -> itemTypeSegments[1];
|
||||
case 3 -> {
|
||||
// 3 segments could either be Group:Type:Function, or Group:Number:Dimension -> Find out which one it is
|
||||
if (!modelItem.getArgs().isEmpty() || GroupFunction.VALID_FUNCTIONS.contains(itemTypeSegments[2])) {
|
||||
// It's Group:Type:Function because there are arguments or the third segment is a valid function
|
||||
function = itemTypeSegments[2];
|
||||
yield itemTypeSegments[1];
|
||||
} else {
|
||||
// Otherwise, it must be Group:Number:Dimension
|
||||
yield itemTypeSegments[1] + ItemUtil.EXTENSION_SEPARATOR + itemTypeSegments[2];
|
||||
}
|
||||
}
|
||||
case 4 -> {
|
||||
// 4 segments: "Group:Number:Dimension:Function"
|
||||
function = itemTypeSegments[3];
|
||||
yield itemTypeSegments[1] + ItemUtil.EXTENSION_SEPARATOR + itemTypeSegments[2];
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Invalid group item type: " + modelItem.getType()
|
||||
+ ". Expected formats are 'Group', 'Group:Type', 'Group:Type:Function', or 'Group:Number:Dimension:Function' with a maximum of 4 segments.");
|
||||
};
|
||||
|
||||
Item baseItem = createItemOfType(baseItemType, modelItem.getName());
|
||||
return applyGroupFunction(baseItem, modelItem, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new item of type {@code itemType} by utilizing an appropriate {@link ItemFactory}.
|
||||
*
|
||||
|
||||
+8
-11
@@ -35,6 +35,7 @@ import org.openhab.core.config.core.ConfigUtil;
|
||||
import org.openhab.core.items.GroupFunction;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemUtil;
|
||||
import org.openhab.core.items.Metadata;
|
||||
import org.openhab.core.items.fileconverter.AbstractItemFileGenerator;
|
||||
import org.openhab.core.items.fileconverter.ItemFileGenerator;
|
||||
@@ -45,8 +46,6 @@ import org.openhab.core.model.item.internal.GenericMetadataProvider;
|
||||
import org.openhab.core.model.items.ItemModel;
|
||||
import org.openhab.core.model.items.ItemsFactory;
|
||||
import org.openhab.core.model.items.ModelBinding;
|
||||
import org.openhab.core.model.items.ModelGroupFunction;
|
||||
import org.openhab.core.model.items.ModelGroupItem;
|
||||
import org.openhab.core.model.items.ModelItem;
|
||||
import org.openhab.core.model.items.ModelProperty;
|
||||
import org.openhab.core.types.State;
|
||||
@@ -115,26 +114,24 @@ public class DslItemFileConverter extends AbstractItemFileGenerator implements I
|
||||
|
||||
private ModelItem buildModelItem(Item item, List<Metadata> channelLinks, List<Metadata> metadata,
|
||||
@Nullable String stateFormatter, boolean hideDefaultParameters) {
|
||||
ModelItem model;
|
||||
ModelItem model = ItemsFactory.eINSTANCE.createModelItem();
|
||||
if (item instanceof GroupItem groupItem) {
|
||||
ModelGroupItem modelGroup = ItemsFactory.eINSTANCE.createModelGroupItem();
|
||||
model = modelGroup;
|
||||
Item baseItem = groupItem.getBaseItem();
|
||||
List<String> groupType = new ArrayList<>();
|
||||
groupType.add(groupItem.getType());
|
||||
if (baseItem != null) {
|
||||
modelGroup.setType(baseItem.getType());
|
||||
groupType.add(baseItem.getType());
|
||||
GroupFunction function = groupItem.getFunction();
|
||||
if (function != null) {
|
||||
ModelGroupFunction modelFunction = ModelGroupFunction
|
||||
.getByName(function.getClass().getSimpleName().toUpperCase());
|
||||
modelGroup.setFunction(modelFunction);
|
||||
groupType.add(function.getClass().getSimpleName().toUpperCase());
|
||||
State[] parameters = function.getParameters();
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
modelGroup.getArgs().add(parameters[i].toString());
|
||||
model.getArgs().add(parameters[i].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
model.setType(groupType.stream().collect(Collectors.joining(ItemUtil.EXTENSION_SEPARATOR)));
|
||||
} else {
|
||||
model = ItemsFactory.eINSTANCE.createModelNormalItem();
|
||||
model.setType(item.getType());
|
||||
}
|
||||
|
||||
|
||||
+94
-18
@@ -12,15 +12,18 @@
|
||||
*/
|
||||
package org.openhab.core.model.validation
|
||||
|
||||
import org.openhab.core.model.items.ModelItem
|
||||
import org.eclipse.xtext.validation.Check
|
||||
import org.openhab.core.items.GroupFunction
|
||||
import org.openhab.core.items.ItemUtil
|
||||
import org.openhab.core.library.CoreItemFactory
|
||||
import org.openhab.core.model.items.ItemsPackage
|
||||
import org.openhab.core.model.items.ModelItem
|
||||
import org.openhab.core.types.util.UnitUtils
|
||||
|
||||
/**
|
||||
* Custom validation rules.
|
||||
* Custom validation rules.
|
||||
*
|
||||
* see http://www.eclipse.org/Xtext/documentation.html#validation
|
||||
* see https://eclipse.dev/Xtext/documentation/303_runtime_concepts.html#validation
|
||||
*/
|
||||
class ItemsValidator extends AbstractItemsValidator {
|
||||
|
||||
@@ -29,23 +32,96 @@ class ItemsValidator extends AbstractItemsValidator {
|
||||
if (item === null || item.name === null) {
|
||||
return
|
||||
}
|
||||
if (item.name.contains("-")) {
|
||||
error('Item name must not contain dashes.', ItemsPackage.Literals.MODEL_ITEM__NAME)
|
||||
}
|
||||
if (!ItemUtil.isValidItemName(item.name)) {
|
||||
error('Item name "' + item.name + '" is invalid. It must begin with a letter or underscore followed by alphanumeric characters and underscores, and must not contain any other symbols.', ItemsPackage.Literals.MODEL_ITEM__NAME)
|
||||
}
|
||||
}
|
||||
|
||||
@Check
|
||||
def checkDimension(ModelItem item) {
|
||||
if (item === null || item.type === null) {
|
||||
|
||||
@Check
|
||||
def checkValidItemType(ModelItem item) {
|
||||
if (item === null || item.type === null) {
|
||||
return
|
||||
}
|
||||
if (item.type.startsWith("Number:")) {
|
||||
var dimension = item.type.substring(item.type.indexOf(":") + 1)
|
||||
try {
|
||||
UnitUtils.parseDimension(dimension)
|
||||
} catch (IllegalArgumentException e) {
|
||||
warning("'" + dimension + "' is not a valid dimension.", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
|
||||
val segments = item.type.split(":")
|
||||
val mainType = segments.get(0)
|
||||
switch mainType {
|
||||
case "Group": checkGroupType(item, segments)
|
||||
case "Number": checkNumberType(item, segments)
|
||||
default: checkBasicItemType(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def checkNumberType(ModelItem item, String[] segments) {
|
||||
switch (segments.size) {
|
||||
case 1: return // Just "Number", valid
|
||||
case 2: checkDimension(item, segments.get(1)) // "Number:<dimension>"
|
||||
default: error("Item: '" + item.name + "' has an invalid Number type, too many segments: " + item.type, ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
def checkBasicItemType(ModelItem item) {
|
||||
if (!CoreItemFactory.VALID_ITEM_TYPES.contains(item.type)) {
|
||||
error("Item '" + item.name + "' has an invalid type: '" + item.type + "'", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
def checkGroupType(ModelItem item, String[] segments) {
|
||||
switch (segments.size) {
|
||||
case 1: return // Just "Group", valid
|
||||
case 2: checkGroupBaseType(item, segments.get(1))
|
||||
case 3: checkGroupWithOneParam(item, segments.get(1), segments.get(2))
|
||||
case 4: checkGroupWithTwoParams(item, segments.get(1), segments.get(2), segments.get(3))
|
||||
// The xtext grammar will not allow more than 4 segments for Group types, so no default case is needed
|
||||
}
|
||||
}
|
||||
|
||||
def checkGroupBaseType(ModelItem item, String baseType) {
|
||||
if (!CoreItemFactory.VALID_ITEM_TYPES.contains(baseType)) {
|
||||
error("Item '" + item.name + "' has an invalid base item type: '" + baseType + "'", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
def checkGroupWithOneParam(ModelItem item, String baseType, String param) {
|
||||
checkGroupBaseType(item, baseType)
|
||||
|
||||
if (item.args.size == 0 && baseType == "Number") {
|
||||
if (!isValidGroupFunction(param)) {
|
||||
// Group:Number:Dimension
|
||||
checkDimension(item, param)
|
||||
}
|
||||
} else {
|
||||
checkGroupFunction(item, param)
|
||||
}
|
||||
}
|
||||
|
||||
def checkGroupWithTwoParams(ModelItem item, String baseType, String dimension, String function) {
|
||||
checkGroupBaseType(item, baseType)
|
||||
|
||||
if (baseType == "Number") {
|
||||
checkDimension(item, dimension)
|
||||
} else {
|
||||
error("Item '" + item.name + "' with type '" + item.type + "' cannot have a dimension. Dimensions are only valid for Number base type. The dimension '" + dimension + "' is ignored.", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
|
||||
checkGroupFunction(item, function)
|
||||
}
|
||||
|
||||
def checkDimension(ModelItem item, String dimension) {
|
||||
try {
|
||||
UnitUtils.parseDimension(dimension)
|
||||
} catch (IllegalArgumentException e) {
|
||||
error("Item '" + item.name + "' has an unknown dimension: '" + dimension + "'. The Item will not be created.", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
def checkGroupFunction(ModelItem item, String function) {
|
||||
if (!isValidGroupFunction(function)) {
|
||||
warning("Item '" + item.name + "' has an unknown group function: '" + function + "'. Using EQUALITY instead.", ItemsPackage.Literals.MODEL_ITEM__TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
def isValidGroupFunction(String value) {
|
||||
return GroupFunction.VALID_FUNCTIONS.contains(value)
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -32,25 +32,25 @@ import org.openhab.core.model.sitemap.sitemap.Chart
|
||||
|
||||
//import org.eclipse.xtext.validation.Check
|
||||
/**
|
||||
* Custom validation rules.
|
||||
*
|
||||
* Custom validation rules.
|
||||
*
|
||||
* see http://www.eclipse.org/Xtext/documentation.html#validation
|
||||
*/
|
||||
class SitemapValidator extends AbstractSitemapValidator {
|
||||
|
||||
val ALLOWED_HINTS = #["text", "number", "date", "time", "datetime"]
|
||||
val ALLOWED_INTERPOLATION = #["linear", "step"]
|
||||
|
||||
|
||||
@Check
|
||||
def void checkFramesInFrame(Frame frame) {
|
||||
for (Widget w : frame.children) {
|
||||
if (w instanceof Frame) {
|
||||
error("Frames must not contain other frames",
|
||||
warning("Frames must not contain other frames",
|
||||
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
|
||||
return;
|
||||
}
|
||||
if (w instanceof Button) {
|
||||
error("Frames should not contain Button, Button is allowed only in Buttongrid",
|
||||
warning("Frames should not contain Button, Button is allowed only in Buttongrid",
|
||||
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
|
||||
return;
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
|
||||
for (Widget w : sitemap.children) {
|
||||
if (w instanceof Button) {
|
||||
error("Sitemap should not contain Button, Button is allowed only in Buttongrid",
|
||||
warning("Sitemap should not contain Button, Button is allowed only in Buttongrid",
|
||||
SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
|
||||
return;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
containsOtherWidgets = true
|
||||
}
|
||||
if (containsFrames && containsOtherWidgets) {
|
||||
error("Sitemap should contain either only frames or none at all",
|
||||
warning("Sitemap should contain either only frames or none at all",
|
||||
SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
|
||||
return
|
||||
}
|
||||
@@ -95,7 +95,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
var containsOtherWidgets = false
|
||||
for (Widget w : widget.children) {
|
||||
if (w instanceof Button) {
|
||||
error("Linkable widget should not contain Button, Button is allowed only in Buttongrid",
|
||||
warning("Linkable widget should not contain Button, Button is allowed only in Buttongrid",
|
||||
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
containsOtherWidgets = true
|
||||
}
|
||||
if (containsFrames && containsOtherWidgets) {
|
||||
error("Linkable widget should contain either only frames or none at all",
|
||||
warning("Linkable widget should contain either only frames or none at all",
|
||||
SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
|
||||
return
|
||||
}
|
||||
@@ -116,12 +116,12 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
def void checkWidgetsInButtongrid(Buttongrid grid) {
|
||||
val nb = grid.getButtons.size()
|
||||
if (nb > 0 && grid.item === null) {
|
||||
error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required",
|
||||
warning("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required",
|
||||
SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__ITEM));
|
||||
}
|
||||
for (Widget w : grid.children) {
|
||||
if (!(w instanceof Button)) {
|
||||
error("Buttongrid must contain only Button",
|
||||
warning("Buttongrid must contain only Button",
|
||||
SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__CHILDREN));
|
||||
return;
|
||||
}
|
||||
@@ -131,17 +131,17 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
@Check
|
||||
def void checkSetpoints(Setpoint sp) {
|
||||
if (BigDecimal.ZERO == sp.step) {
|
||||
error("Setpoint on item '" + sp.item + "' has step size of 0",
|
||||
warning("Setpoint on item '" + sp.item + "' has step size of 0",
|
||||
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
|
||||
}
|
||||
|
||||
if (sp.step !== null && sp.step < BigDecimal.ZERO) {
|
||||
error("Setpoint on item '" + sp.item + "' has negative step size",
|
||||
warning("Setpoint on item '" + sp.item + "' has negative step size",
|
||||
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
|
||||
}
|
||||
|
||||
if (sp.minValue !== null && sp.maxValue !== null && sp.minValue > sp.maxValue) {
|
||||
error("Setpoint on item '" + sp.item + "' has larger minValue than maxValue",
|
||||
warning("Setpoint on item '" + sp.item + "' has larger minValue than maxValue",
|
||||
SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__MIN_VALUE));
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
@Check
|
||||
def void checkColortemperaturepicker(Colortemperaturepicker ctp) {
|
||||
if (ctp.minValue !== null && ctp.maxValue !== null && ctp.minValue > ctp.maxValue) {
|
||||
error("Colortemperaturepicker on item '" + ctp.item + "' has larger minValue than maxValue",
|
||||
warning("Colortemperaturepicker on item '" + ctp.item + "' has larger minValue than maxValue",
|
||||
SitemapPackage.Literals.COLORTEMPERATUREPICKER.getEStructuralFeature(SitemapPackage.COLORTEMPERATUREPICKER__MIN_VALUE));
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
if (i.inputHint !== null && !ALLOWED_HINTS.contains(i.inputHint)) {
|
||||
val node = NodeModelUtils.getNode(i)
|
||||
val line = node.getStartLine()
|
||||
error("Input on item '" + i.item + "' has invalid inputHint '" + i.inputHint + "' at line " + line,
|
||||
warning("Input on item '" + i.item + "' has invalid inputHint '" + i.inputHint + "' at line " + line,
|
||||
SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.INPUT__INPUT_HINT))
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ class SitemapValidator extends AbstractSitemapValidator {
|
||||
if (i.interpolation !== null && !ALLOWED_INTERPOLATION.contains(i.interpolation)) {
|
||||
val node = NodeModelUtils.getNode(i)
|
||||
val line = node.getStartLine()
|
||||
error("Input on item '" + i.item + "' has invalid interpolation '" + i.interpolation + "' at line " + line,
|
||||
warning("Input on item '" + i.item + "' has invalid interpolation '" + i.interpolation + "' at line " + line,
|
||||
SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.CHART__INTERPOLATION))
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -22,7 +22,7 @@ import org.eclipse.xtext.nodemodel.util.NodeModelUtils
|
||||
import org.openhab.core.thing.ThingUID
|
||||
|
||||
/**
|
||||
* Custom validation rules.
|
||||
* Custom validation rules.
|
||||
*
|
||||
* see http://www.eclipse.org/Xtext/documentation.html#validation
|
||||
*/
|
||||
@@ -36,24 +36,24 @@ class ThingValidator extends AbstractThingValidator {
|
||||
// We have to provide thingTypeId and a thingId
|
||||
if (!thing.eIsSet(ThingPackage.Literals.MODEL_THING__THING_TYPE_ID)) {
|
||||
if (thing.eIsSet(ThingPackage.Literals.MODEL_PROPERTY_CONTAINER__ID)) {
|
||||
error("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_PROPERTY_CONTAINER__ID)
|
||||
warning("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_PROPERTY_CONTAINER__ID)
|
||||
} else {
|
||||
if (thing.eIsSet(ThingPackage.Literals.MODEL_BRIDGE__BRIDGE)) {
|
||||
error("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_BRIDGE__BRIDGE)
|
||||
warning("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_BRIDGE__BRIDGE)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!thing.eIsSet(ThingPackage.Literals.MODEL_THING__THING_ID)) {
|
||||
error("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_THING__THING_TYPE_ID)
|
||||
warning("Provide a thing type ID and a thing ID in this format:\n <thingTypeId> <thingId>", ThingPackage.Literals.MODEL_THING__THING_TYPE_ID)
|
||||
}
|
||||
}
|
||||
} else { // thing in container
|
||||
} else { // thing in container
|
||||
if (thing.eIsSet(ThingPackage.Literals.MODEL_THING__THING_TYPE_ID) && thing.eIsSet(ThingPackage.Literals.MODEL_THING__THING_ID)) {
|
||||
val thingTypeIdFeature = NodeModelUtils.findNodesForFeature(thing, ThingPackage.Literals.MODEL_THING__THING_TYPE_ID).head
|
||||
val thingIdFeature = NodeModelUtils.findNodesForFeature(thing, ThingPackage.Literals.MODEL_THING__THING_ID).head
|
||||
val startOffset = thingTypeIdFeature.offset
|
||||
val endOffset = thingIdFeature.endOffset
|
||||
getMessageAcceptor().acceptError("Provide a thing UID in this format:\n <bindingId>:<thingTypeId>:<thingId>", thing, startOffset, endOffset - startOffset, null, null)
|
||||
getMessageAcceptor().acceptWarning("Provide a thing UID in this format:\n <bindingId>:<thingTypeId>:<thingId>", thing, startOffset, endOffset - startOffset, null, null)
|
||||
} else {
|
||||
if (thing.id !== null) {
|
||||
try {
|
||||
@@ -66,7 +66,7 @@ class ThingValidator extends AbstractThingValidator {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
def private isNested(ModelThing thing) {
|
||||
thing.eContainingFeature == ThingPackage.Literals.MODEL_BRIDGE__THINGS
|
||||
}
|
||||
|
||||
+3
-7
@@ -14,10 +14,10 @@ package org.openhab.core.model.yaml.internal.items;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.items.GroupFunction;
|
||||
import org.openhab.core.model.yaml.internal.util.YamlElementUtils;
|
||||
|
||||
/**
|
||||
@@ -28,10 +28,6 @@ import org.openhab.core.model.yaml.internal.util.YamlElementUtils;
|
||||
*/
|
||||
public class YamlGroupDTO {
|
||||
|
||||
private static final String DEFAULT_FUNCTION = "EQUALITY";
|
||||
private static final Set<String> VALID_FUNCTIONS = Set.of("AND", "OR", "NAND", "NOR", "XOR", "COUNT", "AVG",
|
||||
"MEDIAN", "SUM", "MIN", "MAX", "LATEST", "EARLIEST", DEFAULT_FUNCTION);
|
||||
|
||||
public String type;
|
||||
public String dimension;
|
||||
public String function;
|
||||
@@ -53,7 +49,7 @@ public class YamlGroupDTO {
|
||||
} else if (dimension != null) {
|
||||
warnings.add("\"dimension\" field in group ignored as type is not Number");
|
||||
}
|
||||
if (!VALID_FUNCTIONS.contains(getFunction())) {
|
||||
if (!GroupFunction.VALID_FUNCTIONS.contains(getFunction())) {
|
||||
errors.add("invalid value \"%s\" for \"function\" field".formatted(function));
|
||||
ok = false;
|
||||
}
|
||||
@@ -65,7 +61,7 @@ public class YamlGroupDTO {
|
||||
}
|
||||
|
||||
public String getFunction() {
|
||||
return function != null ? function.toUpperCase() : DEFAULT_FUNCTION;
|
||||
return function != null ? function.toUpperCase() : GroupFunction.DEFAULT;
|
||||
}
|
||||
|
||||
public @NonNull List<@NonNull String> getParameters() {
|
||||
|
||||
+1
-7
@@ -15,7 +15,6 @@ package org.openhab.core.model.yaml.internal.util;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -31,11 +30,6 @@ import org.openhab.core.util.StringUtils;
|
||||
@NonNullByDefault
|
||||
public class YamlElementUtils {
|
||||
|
||||
private static final Set<String> VALID_ITEM_TYPES = Set.of(CoreItemFactory.SWITCH, CoreItemFactory.ROLLERSHUTTER,
|
||||
CoreItemFactory.CONTACT, CoreItemFactory.STRING, CoreItemFactory.NUMBER, CoreItemFactory.DIMMER,
|
||||
CoreItemFactory.DATETIME, CoreItemFactory.COLOR, CoreItemFactory.IMAGE, CoreItemFactory.PLAYER,
|
||||
CoreItemFactory.LOCATION, CoreItemFactory.CALL);
|
||||
|
||||
public static boolean equalsConfig(@Nullable Map<String, Object> first, @Nullable Map<String, Object> second) {
|
||||
if (first != null && second != null) {
|
||||
return first.size() != second.size() ? false
|
||||
@@ -57,7 +51,7 @@ public class YamlElementUtils {
|
||||
|
||||
public static boolean isValidItemType(@Nullable String type) {
|
||||
String adjustedType = getAdjustedItemType(type);
|
||||
return adjustedType == null ? true : VALID_ITEM_TYPES.contains(adjustedType);
|
||||
return adjustedType == null ? true : CoreItemFactory.VALID_ITEM_TYPES.contains(adjustedType);
|
||||
}
|
||||
|
||||
public static boolean isNumberItemType(@Nullable String type) {
|
||||
|
||||
+19
-19
@@ -82,15 +82,15 @@ public class GroupFunctionHelper {
|
||||
Unit<?> baseItemUnit = baseItem.getUnit();
|
||||
if (baseItemUnit != null) {
|
||||
switch (functionName.toUpperCase()) {
|
||||
case "AVG":
|
||||
case GroupFunction.AVG:
|
||||
return new QuantityTypeArithmeticGroupFunction.Avg(baseItemUnit);
|
||||
case "MEDIAN":
|
||||
case GroupFunction.MEDIAN:
|
||||
return new QuantityTypeArithmeticGroupFunction.Median(baseItemUnit);
|
||||
case "SUM":
|
||||
case GroupFunction.SUM:
|
||||
return new QuantityTypeArithmeticGroupFunction.Sum(baseItemUnit);
|
||||
case "MIN":
|
||||
case GroupFunction.MIN:
|
||||
return new QuantityTypeArithmeticGroupFunction.Min(baseItemUnit);
|
||||
case "MAX":
|
||||
case GroupFunction.MAX:
|
||||
return new QuantityTypeArithmeticGroupFunction.Max(baseItemUnit);
|
||||
default:
|
||||
}
|
||||
@@ -102,7 +102,7 @@ public class GroupFunctionHelper {
|
||||
final String functionName = function.name;
|
||||
final List<State> args;
|
||||
switch (functionName.toUpperCase()) {
|
||||
case "AND":
|
||||
case GroupFunction.AND:
|
||||
args = parseStates(baseItem, function.params);
|
||||
if (args.size() == 2) {
|
||||
return new ArithmeticGroupFunction.And(args.getFirst(), args.get(1));
|
||||
@@ -110,7 +110,7 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'AND' requires two arguments. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "OR":
|
||||
case GroupFunction.OR:
|
||||
args = parseStates(baseItem, function.params);
|
||||
if (args.size() == 2) {
|
||||
return new ArithmeticGroupFunction.Or(args.getFirst(), args.get(1));
|
||||
@@ -118,7 +118,7 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'OR' requires two arguments. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "NAND":
|
||||
case GroupFunction.NAND:
|
||||
args = parseStates(baseItem, function.params);
|
||||
if (args.size() == 2) {
|
||||
return new ArithmeticGroupFunction.NAnd(args.getFirst(), args.get(1));
|
||||
@@ -126,7 +126,7 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'NOT AND' requires two arguments. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "NOR":
|
||||
case GroupFunction.NOR:
|
||||
args = parseStates(baseItem, function.params);
|
||||
if (args.size() == 2) {
|
||||
return new ArithmeticGroupFunction.NOr(args.getFirst(), args.get(1));
|
||||
@@ -134,7 +134,7 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'NOT OR' requires two arguments. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "XOR":
|
||||
case GroupFunction.XOR:
|
||||
args = parseStates(baseItem, function.params);
|
||||
if (args.size() == 2) {
|
||||
return new ArithmeticGroupFunction.Xor(args.getFirst(), args.get(1));
|
||||
@@ -142,7 +142,7 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'XOR' requires two arguments. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "COUNT":
|
||||
case GroupFunction.COUNT:
|
||||
if (function.params != null && function.params.length == 1) {
|
||||
State countParam = new StringType(function.params[0]);
|
||||
return new ArithmeticGroupFunction.Count(countParam);
|
||||
@@ -150,21 +150,21 @@ public class GroupFunctionHelper {
|
||||
logger.error("Group function 'COUNT' requires one argument. Using Equality instead.");
|
||||
}
|
||||
break;
|
||||
case "AVG":
|
||||
case GroupFunction.AVG:
|
||||
return new ArithmeticGroupFunction.Avg();
|
||||
case "MEDIAN":
|
||||
case GroupFunction.MEDIAN:
|
||||
return new ArithmeticGroupFunction.Median();
|
||||
case "SUM":
|
||||
case GroupFunction.SUM:
|
||||
return new ArithmeticGroupFunction.Sum();
|
||||
case "MIN":
|
||||
case GroupFunction.MIN:
|
||||
return new ArithmeticGroupFunction.Min();
|
||||
case "MAX":
|
||||
case GroupFunction.MAX:
|
||||
return new ArithmeticGroupFunction.Max();
|
||||
case "LATEST":
|
||||
case GroupFunction.LATEST:
|
||||
return new DateTimeGroupFunction.Latest();
|
||||
case "EARLIEST":
|
||||
case GroupFunction.EARLIEST:
|
||||
return new DateTimeGroupFunction.Earliest();
|
||||
case "EQUALITY":
|
||||
case GroupFunction.EQUALITY:
|
||||
return new GroupFunction.Equality();
|
||||
default:
|
||||
logger.error("Unknown group function '{}'. Using Equality instead.", functionName);
|
||||
|
||||
@@ -28,6 +28,26 @@ import org.openhab.core.types.UnDefType;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface GroupFunction {
|
||||
String EQUALITY = "EQUALITY";
|
||||
String AND = "AND";
|
||||
String OR = "OR";
|
||||
String NAND = "NAND";
|
||||
String NOR = "NOR";
|
||||
String XOR = "XOR";
|
||||
String COUNT = "COUNT";
|
||||
String AVG = "AVG";
|
||||
String MEDIAN = "MEDIAN";
|
||||
String SUM = "SUM";
|
||||
String MIN = "MIN";
|
||||
String MAX = "MAX";
|
||||
String LATEST = "LATEST";
|
||||
String EARLIEST = "EARLIEST";
|
||||
|
||||
String DEFAULT = EQUALITY;
|
||||
|
||||
Set<String> VALID_FUNCTIONS = Set.of( //
|
||||
EQUALITY, AND, OR, NAND, NOR, XOR, COUNT, AVG, MEDIAN, SUM, MIN, MAX, LATEST, EARLIEST //
|
||||
);
|
||||
|
||||
/**
|
||||
* Determines the current state of a group based on a list of items
|
||||
|
||||
+8
-2
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.core.library;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
@@ -57,6 +59,11 @@ public class CoreItemFactory implements ItemFactory {
|
||||
public static final String ROLLERSHUTTER = "Rollershutter";
|
||||
public static final String STRING = "String";
|
||||
public static final String SWITCH = "Switch";
|
||||
|
||||
public static final Set<String> VALID_ITEM_TYPES = Set.of( //
|
||||
CALL, COLOR, CONTACT, DATETIME, DIMMER, IMAGE, LOCATION, NUMBER, PLAYER, ROLLERSHUTTER, STRING, SWITCH //
|
||||
);
|
||||
|
||||
private final UnitProvider unitProvider;
|
||||
|
||||
@Activate
|
||||
@@ -90,7 +97,6 @@ public class CoreItemFactory implements ItemFactory {
|
||||
|
||||
@Override
|
||||
public String[] getSupportedItemTypes() {
|
||||
return new String[] { SWITCH, ROLLERSHUTTER, CONTACT, STRING, NUMBER, DIMMER, DATETIME, COLOR, IMAGE, PLAYER,
|
||||
LOCATION, CALL };
|
||||
return VALID_ITEM_TYPES.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ Fragment-Host: org.openhab.core.model.item
|
||||
org.eclipse.equinox.common;version='[3.19.0,3.19.1)',\
|
||||
junit-jupiter-api;version='[5.13.4,5.13.5)',\
|
||||
junit-jupiter-engine;version='[5.13.4,5.13.5)',\
|
||||
junit-jupiter-params;version='[5.13.4,5.13.5)',\
|
||||
junit-platform-commons;version='[1.13.4,1.13.5)',\
|
||||
junit-platform-engine;version='[1.13.4,1.13.5)',\
|
||||
junit-platform-launcher;version='[1.13.4,1.13.5)',\
|
||||
|
||||
+73
@@ -27,11 +27,15 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.items.GenericItem;
|
||||
@@ -47,10 +51,12 @@ import org.openhab.core.items.events.AbstractItemRegistryEvent;
|
||||
import org.openhab.core.items.events.ItemAddedEvent;
|
||||
import org.openhab.core.items.events.ItemRemovedEvent;
|
||||
import org.openhab.core.items.events.ItemUpdatedEvent;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.ArithmeticGroupFunction;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityTypeArithmeticGroupFunction;
|
||||
import org.openhab.core.model.core.EventType;
|
||||
import org.openhab.core.model.core.ModelRepository;
|
||||
import org.openhab.core.model.core.ModelRepositoryChangeListener;
|
||||
@@ -403,6 +409,34 @@ public class GenericItemProviderTest extends JavaOSGiTest {
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testGroupTypeSyntax() {
|
||||
return Stream.of( //
|
||||
Arguments.of("Group:Number Grp", "Number", GroupFunction.Equality.class),
|
||||
Arguments.of("Group:Number:MAX Grp", "Number", ArithmeticGroupFunction.Max.class),
|
||||
|
||||
Arguments.of("Group:Number:Power Grp", "Number:Power", GroupFunction.Equality.class),
|
||||
Arguments.of("Group:Number:Power:MAX Grp", "Number:Power",
|
||||
QuantityTypeArithmeticGroupFunction.Max.class),
|
||||
|
||||
Arguments.of("Group:Switch:OR(ON,OFF) Grp", "Switch", ArithmeticGroupFunction.Or.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void testGroupTypeSyntax(String model, String expectedBaseType,
|
||||
Class<? extends GroupFunction> expectedFunction) {
|
||||
modelRepository.addOrRefreshModel(TESTMODEL_NAME, new ByteArrayInputStream(model.getBytes()));
|
||||
assertThat(itemRegistry.getAll(), hasSize(1));
|
||||
|
||||
if (itemRegistry.getAll().iterator().next() instanceof GroupItem groupItem) {
|
||||
assertThat(groupItem.getBaseItem(), is(notNullValue()));
|
||||
assertThat(groupItem.getBaseItem().getType(), is(expectedBaseType));
|
||||
assertThat(groupItem.getFunction(), instanceOf(expectedFunction));
|
||||
} else {
|
||||
throw new AssertionError("Item is not a GroupItem");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStableOrder() {
|
||||
String model = "Group testGroup " + //
|
||||
@@ -711,4 +745,43 @@ public class GenericItemProviderTest extends JavaOSGiTest {
|
||||
assertThat(stateDescription, is(notNullValue()));
|
||||
assertThat(stateDescription.getPattern(), is("%s"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeywordsInOtherFields() {
|
||||
String itemTypeList = CoreItemFactory.VALID_ITEM_TYPES.stream().sorted().collect(joining(", "));
|
||||
String functionList = GroupFunction.VALID_FUNCTIONS.stream().sorted().collect(joining(", "));
|
||||
String model = """
|
||||
DateTime DateTime [%s]
|
||||
Switch Switch "Switch" <Switch> [%s]
|
||||
|
||||
// Multiline item with keywords at the start of line
|
||||
String
|
||||
String
|
||||
<
|
||||
String
|
||||
>
|
||||
[
|
||||
String
|
||||
]
|
||||
|
||||
Number EQUALITY
|
||||
""".formatted(itemTypeList, functionList);
|
||||
modelRepository.addOrRefreshModel(TESTMODEL_NAME, new ByteArrayInputStream(model.getBytes()));
|
||||
|
||||
assertThat(itemRegistry.getAll(), hasSize(4));
|
||||
|
||||
Item dateTimeItem = itemRegistry.get("DateTime");
|
||||
assertThat(dateTimeItem.getTags().stream().sorted().collect(joining(", ")), is(equalTo(itemTypeList)));
|
||||
|
||||
Item switchItem = itemRegistry.get("Switch");
|
||||
assertThat(switchItem.getTags().stream().sorted().collect(joining(", ")), is(equalTo(functionList)));
|
||||
assertThat(switchItem.getLabel(), is("Switch"));
|
||||
assertThat(switchItem.getCategory(), is("Switch"));
|
||||
|
||||
Item multilineItem = itemRegistry.get("String");
|
||||
assertThat(multilineItem.getTags().stream().sorted().collect(joining(", ")), is(equalTo("String")));
|
||||
assertThat(multilineItem.getCategory(), is("String"));
|
||||
|
||||
assertThat(itemRegistry.get("EQUALITY"), is(notNullValue()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user