From 49d787ced926b44d15b1fbac47503e2604da7257 Mon Sep 17 00:00:00 2001 From: Salvatore Coppola <83589980+salvatore-coppola@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:56:33 +0100 Subject: [PATCH] feat(web2): Added dynamic validator for scale and offset fields (#5530) * Added dynamic validator for scale and offset fields * Removed debugging loggers * Added check on empty String and fixed test --- .../asset/provider/BaseChannelDescriptor.java | 6 +- .../provider/BaseAssetConfiguration.java | 7 +- .../drivers/assets/AssetConfigurationUi.java | 64 +++++-- .../client/ui/drivers/assets/AssetDataUi.java | 65 ++++--- .../client/ui/drivers/assets/AssetModel.java | 12 +- .../ui/drivers/assets/AssetModelImpl.java | 119 ++++-------- .../ui/drivers/assets/LegacyChannelModel.java | 180 ++++++++++++++++++ .../kura/web/client/util/ValidationUtil.java | 14 +- .../org/eclipse/kura/web/shared/DataType.java | 79 ++++++++ .../kura/web/shared/ScaleOffsetType.java | 58 ++++++ .../web/shared/model/GwtConfigParameter.java | 128 +++++++------ .../rest/wire/provider/test/Snippets.java | 6 +- 12 files changed, 538 insertions(+), 200 deletions(-) create mode 100644 kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/LegacyChannelModel.java create mode 100644 kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/DataType.java create mode 100644 kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/ScaleOffsetType.java diff --git a/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/asset/provider/BaseChannelDescriptor.java b/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/asset/provider/BaseChannelDescriptor.java index b476a0c1dc8..8a250d6ed75 100644 --- a/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/asset/provider/BaseChannelDescriptor.java +++ b/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/asset/provider/BaseChannelDescriptor.java @@ -158,8 +158,8 @@ protected BaseChannelDescriptor() { final Tad valueScale = new Tad(); valueScale.setName(VALUE_SCALE.value().substring(1)); valueScale.setId(VALUE_SCALE.value()); - valueScale.setDescription("Scale to be applied to the numeric value of the channel"); - valueScale.setType(Tscalar.DOUBLE); + valueScale.setDescription("Scale to be applied to the numeric value of the channel."); + valueScale.setType(Tscalar.STRING); valueScale.setRequired(false); this.defaultElements.add(valueScale); @@ -168,7 +168,7 @@ protected BaseChannelDescriptor() { valueOffset.setName(VALUE_OFFSET.value().substring(1)); valueOffset.setId(VALUE_OFFSET.value()); valueOffset.setDescription("Offset to be applied to the numeric value of the channel"); - valueOffset.setType(Tscalar.DOUBLE); + valueOffset.setType(Tscalar.STRING); valueOffset.setRequired(false); this.defaultElements.add(valueOffset); diff --git a/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/internal/asset/provider/BaseAssetConfiguration.java b/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/internal/asset/provider/BaseAssetConfiguration.java index 02b26acbcc2..02b5f3d20d1 100644 --- a/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/internal/asset/provider/BaseAssetConfiguration.java +++ b/kura/org.eclipse.kura.asset.provider/src/main/java/org/eclipse/kura/internal/asset/provider/BaseAssetConfiguration.java @@ -342,7 +342,7 @@ private static ScaleOffsetType getScaleOffsetType(final Map prop private static Number getValueScale(final Map properties) { final String valueScale = (String) properties.get(VALUE_SCALE.value()); - if (valueScale == null) { + if (valueScale == null || valueScale.isEmpty()) { return 1.0d; } return parseScaleOffsetTypedValue(getScaleOffsetType(properties), valueScale); @@ -351,7 +351,7 @@ private static Number getValueScale(final Map properties) { private static Number getValueOffset(final Map properties) { final String valueOffset = (String) properties.get(VALUE_OFFSET.value()); - if (valueOffset == null) { + if (valueOffset == null || valueOffset.isEmpty()) { return 0.0d; } return parseScaleOffsetTypedValue(getScaleOffsetType(properties), valueOffset); @@ -411,8 +411,7 @@ private static Number parseScaleOffsetTypedValue(ScaleOffsetType type, String va case DOUBLE: return Double.parseDouble(value); case LONG: - // TODO replace with Long.parseLong(value) once scale and offset are turned in String in the metatype - return (Double.valueOf(value).longValue()); + return Long.parseLong(value); default: throw new IllegalArgumentException(value + " cannot be converted into a Number of type " + type); } diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetConfigurationUi.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetConfigurationUi.java index 57b19bff7cf..555cd4a041c 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetConfigurationUi.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetConfigurationUi.java @@ -32,7 +32,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import org.eclipse.kura.web.Console; @@ -98,8 +97,6 @@ public class AssetConfigurationUi extends AbstractServicesUi implements HasConfi private static final String COLUMN_VISIBILITY_SETTINGS_KEY = "org.eclipse.kura.settings.column.asset."; - private static final Logger logger = Logger.getLogger(AssetConfigurationUi.class.getSimpleName()); - private static final List defaultHiddenColumns = // Arrays.asList(// AssetConstants.VALUE_UNIT.value(), // @@ -323,8 +320,6 @@ public void setPageStart(int index) { this.btnManageHiddenColumn.setText(MSGS.columnVisibilityModalButton(// String.valueOf(totalEnabledColumns(this.columnNameVisibilityMap)), String.valueOf(this.columnNameVisibilityMap.size()))); - - logger.info("created AssetConfigurationUi for: " + this.model.getAssetPid()); } private void fillColumnVisibilityCheckBox() { @@ -394,7 +389,6 @@ private void resetDefaultColumnVisibilityMap() { private void populateColumnVisibilityMap() { this.model.getChannelDescriptor().getParameters().forEach(param -> { - AssetConfigurationUi.logger.info("Id: " + param.getId() + ", Name: " + param.getName()); if (!param.getId().equals(AssetConstants.ENABLED.value()) && !param.getId().equals(AssetConstants.NAME.value())) { boolean visible = !defaultHiddenColumns.contains(param.getId()); @@ -530,10 +524,11 @@ private Column getInputCellColumn(final GwtConfigParameter final Column result = new ChannelColumn(cell, param); if (!isReadOnly) { - result.setFieldUpdater((index, object, value) -> { - final String paramId = object.getChannelName() + '#' + param.getId(); - object.setValue(param.getId(), value); - if (!object.isValid(param.getId())) { + result.setFieldUpdater((index, channelModel, value) -> { + final String paramId = channelModel.getChannelName() + '#' + param.getId(); + Integer paramIndex = AssetConfigurationUi.this.model.getParameterIndex(param.getId()); + channelModel.setValue(paramIndex, value); + if (!channelModel.isValid(paramIndex)) { AssetConfigurationUi.this.invalidParameters.add(paramId); } else { AssetConfigurationUi.this.invalidParameters.remove(paramId); @@ -552,35 +547,60 @@ private Column getInputCellColumn(final GwtConfigParameter } private Column getSelectionInputColumn(final GwtConfigParameter param, boolean isReadOnly) { - final String id = param.getId(); final Map labelsToValues = param.getOptions(); ArrayList> sortedLabelsToValues = new ArrayList<>(labelsToValues.entrySet()); Collections.sort(sortedLabelsToValues, DROPDOWN_LABEL_COMPARATOR); + final ArrayList labels = new ArrayList<>(); final Map valuesToLabels = new HashMap<>(); + for (Entry entry : sortedLabelsToValues) { labels.add(entry.getKey()); valuesToLabels.put(entry.getValue(), entry.getKey()); } + final SelectionCell cell = new SelectionCell(new ArrayList<>(labels)); final Column result = new Column(cell) { @Override - public String getValue(final ChannelModel object) { - String result = object.getValue(id); + public String getValue(final ChannelModel channelModel) { + Integer paramIndex = AssetConfigurationUi.this.model.getParameterIndex(param.getId()); + String result = channelModel.getValue(paramIndex); if (result == null) { final String defaultValue = param.getDefault(); result = defaultValue != null ? defaultValue : labelsToValues.get(labels.get(0)); - object.setValue(id, result); + channelModel.setValue(paramIndex, result); } return valuesToLabels.get(result); } }; if (!isReadOnly) { - result.setFieldUpdater((index, object, label) -> { + result.setFieldUpdater((index, channelModel, label) -> { + String paramId = param.getId(); AssetConfigurationUi.this.setDirty(true); - object.setValue(param.getId(), labelsToValues.get(label)); + String newValue = labelsToValues.get(label); + Integer paramIndex = AssetConfigurationUi.this.model.getParameterIndex(paramId); + String oldValue = channelModel.getValue(paramIndex); + channelModel.setValue(paramIndex, newValue); + if ((param.getId().equals(AssetConstants.VALUE_TYPE.value()) + || param.getId().equals(AssetConstants.SCALE_OFFSET_TYPE.value()) + && !oldValue.equals(newValue))) { + + for (Map.Entry paramEntry : AssetConfigurationUi.this.model.getParameterIndexes() + .entrySet()) { + String paramIdentifier = channelModel.getChannelName() + '#' + paramEntry.getKey(); + Integer parameterIndex = paramEntry.getValue(); + if (!channelModel.isValid(parameterIndex)) { + AssetConfigurationUi.this.invalidParameters.add(paramIdentifier); + } else { + AssetConfigurationUi.this.invalidParameters.remove(paramIdentifier); + } + } + + this.channelTable.redrawRow(index); + + } }); } @@ -878,7 +898,7 @@ public static ColumnVisibilityMap fromString(String text) { } } - private static class ChannelColumn extends Column { + private class ChannelColumn extends Column { private final GwtConfigParameter param; @@ -888,8 +908,9 @@ public ChannelColumn(final Cell cell, final GwtConfigParameter param) { } @Override - public String getValue(final ChannelModel object) { - String result = object.getValue(this.param.getId()); + public String getValue(final ChannelModel channelModel) { + Integer paramIndex = AssetConfigurationUi.this.model.getParameterIndex(this.param.getId()); + String result = channelModel.getValue(paramIndex); if (result != null) { return result; } @@ -897,8 +918,9 @@ public String getValue(final ChannelModel object) { } @Override - public String getCellStyleNames(Context context, ChannelModel object) { - if (!object.isValid(this.param.getId())) { + public String getCellStyleNames(Context context, ChannelModel channelModel) { + Integer paramIndex = AssetConfigurationUi.this.model.getParameterIndex(this.param.getId()); + if (!channelModel.isValid(paramIndex)) { return "config-cell-not-valid"; } else { return ""; diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetDataUi.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetDataUi.java index e7a3aa7b428..b079774c001 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetDataUi.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetDataUi.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2020 Eurotech and/or its affiliates and others + * Copyright (c) 2017, 2024 Eurotech and/or its affiliates and others * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -124,8 +124,8 @@ private void initTable() { @Override public void onBrowserEvent(Context context, Element elem, ChannelModel object, NativeEvent event) { if (getChannelStatus(object) == ChannelStatus.FAILURE) { - final GwtChannelRecord record = AssetDataUi.this.channelValues.get(object.getChannelName()); - showFailureDetails(record); + final GwtChannelRecord channelRecord = AssetDataUi.this.channelValues.get(object.getChannelName()); + showFailureDetails(channelRecord); } } @@ -154,9 +154,10 @@ public void render(final Context context, final ChannelModel object, final SafeH this.valuesCell) { @Override - public void onBrowserEvent(Context context, Element elem, ChannelModel object, NativeEvent event) { - if (!"READ".equals(object.getValue(AssetConstants.TYPE.value()))) { - super.onBrowserEvent(context, elem, object, event); + public void onBrowserEvent(Context context, Element elem, ChannelModel channelModel, NativeEvent event) { + if (!"READ".equals( + channelModel.getValue(AssetDataUi.this.model.getParameterIndex(AssetConstants.TYPE.value())))) { + super.onBrowserEvent(context, elem, channelModel, event); } } @@ -178,15 +179,16 @@ public String getValue(final AssetModel.ChannelModel object) { } @Override - public void render(Context context, ChannelModel object, SafeHtmlBuilder sb) { - if ("READ".equals(object.getValue(AssetConstants.TYPE.value()))) { - sb.appendEscaped(getValue(object)); + public void render(Context context, ChannelModel channelModel, SafeHtmlBuilder sb) { + if ("READ".equals( + channelModel.getValue(AssetDataUi.this.model.getParameterIndex(AssetConstants.TYPE.value())))) { + sb.appendEscaped(getValue(channelModel)); return; } - if (!isDirty(object.getChannelName())) { + if (!isDirty(channelModel.getChannelName())) { AssetDataUi.this.valuesCell.clearViewData(context.getKey()); } - super.render(context, object, sb); + super.render(context, channelModel, sb); } }; @@ -205,24 +207,25 @@ public void render(Context context, ChannelModel object, SafeHtmlBuilder sb) { this.assetDataTable.addColumn(valueColumn, new TextHeader(MSGS.devicePropValue())); } - private static void showFailureDetails(final GwtChannelRecord record) { - record.setUnescaped(true); - String reason = record.getExceptionMessage(); - record.setUnescaped(false); + private static void showFailureDetails(final GwtChannelRecord channelRecord) { + channelRecord.setUnescaped(true); + String reason = channelRecord.getExceptionMessage(); + channelRecord.setUnescaped(false); if (reason == null || reason.trim().isEmpty()) { reason = "unknown"; } FailureHandler.showErrorMessage("Channel failure details", "Reason: " + reason, - record.getExceptionStackTrace()); + channelRecord.getExceptionStackTrace()); } private GwtChannelRecord createWriteRecord(AssetModel.ChannelModel channel) { final GwtChannelRecord result = new GwtChannelRecord(); result.setUnescaped(true); result.setName(channel.getChannelName()); - result.setValueType(channel.getValue(AssetConstants.VALUE_TYPE.value())); + result.setValueType( + channel.getValue(AssetDataUi.this.model.getParameterIndex(AssetConstants.VALUE_TYPE.value()))); return result; } @@ -234,11 +237,11 @@ private void write() { final ArrayList writeRecords = new ArrayList<>(); for (final String channelName : this.modifiedWriteChannels) { - final GwtChannelRecord record = this.channelValues.get(channelName); - if (record == null) { + final GwtChannelRecord channelRecord = this.channelValues.get(channelName); + if (channelRecord == null) { continue; } - writeRecords.add(record); + writeRecords.add(channelRecord); } if (writeRecords.isEmpty()) { @@ -299,9 +302,9 @@ public void renderForm() { final List records = result.getRecords(); if (records != null) { - for (final GwtChannelRecord record : records) { - record.setUnescaped(true); - this.channelValues.put(record.getName(), record); + for (final GwtChannelRecord channelRecord : records) { + channelRecord.setUnescaped(true); + this.channelValues.put(channelRecord.getName(), channelRecord); } AssetDataUi.this.channelsDataProvider.getList().addAll(this.model.getChannels()); AssetDataUi.this.channelsDataProvider.refresh(); @@ -319,22 +322,22 @@ public void renderForm() { private ChannelStatus getChannelStatus(final ChannelModel model) { final String channelName = model.getChannelName(); - final GwtChannelRecord record = this.channelValues.get(model.getChannelName()); + final GwtChannelRecord channelRecord = this.channelValues.get(model.getChannelName()); - if ("false".equals(model.getValue(AssetConstants.ENABLED.value()))) { + if ("false".equals(model.getValue(AssetDataUi.this.model.getParameterIndex(AssetConstants.ENABLED.value())))) { return ChannelStatus.DISABLED; } else if (this.modifiedWriteChannels.contains(channelName)) { return ChannelStatus.DIRTY; - } else if (record == null) { + } else if (channelRecord == null) { return ChannelStatus.UNKNOWN; - } else if (record.getValue() == null) { + } else if (channelRecord.getValue() == null) { return ChannelStatus.FAILURE; } else { return ChannelStatus.SUCCESS; } } - private static final class StaticColumn extends Column { + private final class StaticColumn extends Column { private final String key; @@ -344,12 +347,12 @@ public StaticColumn(final String key) { } @Override - public String getValue(final AssetModel.ChannelModel object) { - return object.getValue(this.key); + public String getValue(final AssetModel.ChannelModel channelModel) { + return channelModel.getValue(AssetDataUi.this.model.getParameterIndex(this.key)); } } - private static final class StatusCell extends TextCell { + private final class StatusCell extends TextCell { @Override public Set getConsumedEvents() { diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModel.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModel.java index 2e1d3895af3..3bdecceb457 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModel.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModel.java @@ -13,6 +13,7 @@ package org.eclipse.kura.web.client.ui.drivers.assets; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.kura.web.shared.model.GwtConfigComponent; @@ -40,16 +41,21 @@ public interface AssetModel { public boolean isValid(); + public Integer getParameterIndex(String value); + + public Map getParameterIndexes(); + public interface ChannelModel { public GwtConfigParameter getParameter(int index); - public void setValue(String id, String value); + public void setValue(Integer index, String value); - public boolean isValid(String id); + public boolean isValid(Integer index); - public String getValue(String id); + public String getValue(Integer index); public String getChannelName(); } + } diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModelImpl.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModelImpl.java index 0c48ff64865..dc9240ce332 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModelImpl.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/AssetModelImpl.java @@ -21,7 +21,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; +import org.eclipse.kura.web.client.ui.drivers.assets.LegacyChannelModel.LegacyChannelModelBuilder; import org.eclipse.kura.web.client.util.LabelComparator; import org.eclipse.kura.web.client.util.ValidationUtil; import org.eclipse.kura.web.shared.AssetConstants; @@ -37,6 +39,7 @@ public class AssetModelImpl implements AssetModel { private Set channelNames = new HashSet<>(); private final Map paramIndexes = new HashMap<>(); + private final List channelModels = new ArrayList<>(); private final List extraParameters = new ArrayList<>(); @@ -73,7 +76,7 @@ private static GwtConfigComponent concat(final GwtConfigComponent first, final G return result; } - private String getChannelName(String propertyName) { + private static String getChannelName(String propertyName) { int separatorIndex = propertyName.indexOf(AssetConstants.CHANNEL_PROPERTY_SEPARATOR.value()); if (separatorIndex != -1) { return propertyName.substring(0, separatorIndex); @@ -81,7 +84,7 @@ private String getChannelName(String propertyName) { return null; } - private String getChannelPropertyName(String propertyName) { + private static String getChannelPropertyName(String propertyName) { int separatorIndex = propertyName.indexOf(AssetConstants.CHANNEL_PROPERTY_SEPARATOR.value()); if (separatorIndex != -1) { return propertyName.substring(separatorIndex + 1); @@ -104,16 +107,18 @@ private void probeChannels() { private void loadChannelModels() { - final HashMap channelIndexes = new HashMap<>(); + final Map channelIndexes = new HashMap<>(); int i = 0; + for (GwtConfigParameter param : this.channelDescriptor.getParameters()) { channelIndexes.put(param.getId(), i); i++; } - final HashMap models = new HashMap<>(); + final Map modelBuilders = new HashMap<>(); for (GwtConfigParameter param : this.assetConfiguration.getParameters()) { + final String channelName = getChannelName(param.getId()); final String propertyName = getChannelPropertyName(param.getId()); if (channelName == null || propertyName == null) { @@ -121,10 +126,14 @@ private void loadChannelModels() { } final int index = i; - LegacyChannelModel model = models.computeIfAbsent(channelName, name -> new LegacyChannelModel(name, index)); - model.parameters[channelIndexes.get(propertyName)] = param; + LegacyChannelModelBuilder modelBuilder = modelBuilders.computeIfAbsent(channelName, + name -> LegacyChannelModel.builder(name, index)); + modelBuilder.addParameter(channelIndexes.get(propertyName), param); } + Map models = modelBuilders.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().build())); + ArrayList> sortedModels = new ArrayList<>(models.entrySet()); Collections.sort(sortedModels, CHANNEL_LABEL_COMPARATOR); List sortedLegacyChannelModels = new ArrayList<>(); @@ -143,9 +152,7 @@ public List getChannels() { @Override public ChannelModel createNewChannel(String channelName) { - final LegacyChannelModel result = new LegacyChannelModel(channelName, - this.channelDescriptor.getParameters().size()); - int i = 0; + List params = new ArrayList<>(); for (GwtConfigParameter param : this.channelDescriptor.getParameters()) { final GwtConfigParameter cloned = new GwtConfigParameter(param); final String paramId = channelName + AssetConstants.CHANNEL_PROPERTY_SEPARATOR.value() + param.getId(); @@ -153,10 +160,14 @@ public ChannelModel createNewChannel(String channelName) { cloned.setName(paramId); cloned.setValue(cloned.getDefault()); this.assetConfiguration.getParameters().add(cloned); - result.parameters[i] = cloned; - i++; + params.add(cloned); } - result.setValue(AssetConstants.NAME.value(), channelName); + + final LegacyChannelModel result = new LegacyChannelModel(channelName, + params.toArray(new GwtConfigParameter[0])); + + result.setValue(AssetModelImpl.this.paramIndexes.get(AssetConstants.NAME.value()), channelName); + this.channelNames.add(channelName); this.channelModels.add(result); return result; @@ -180,72 +191,9 @@ public void deleteChannel(String channelName) { final ChannelModel model = iter.next(); if (model.getChannelName().equals(channelName)) { iter.remove(); - ((LegacyChannelModel) model).remove(); - return; - } - } - } - - private class LegacyChannelModel implements AssetModel.ChannelModel { - - String channelName; - GwtConfigParameter[] parameters; - - public LegacyChannelModel(String channelName, int parameterCount) { - this.channelName = channelName; - this.parameters = new GwtConfigParameter[parameterCount]; - } - - @Override - public String getChannelName() { - return this.channelName; - } - - @Override - public GwtConfigParameter getParameter(int index) { - return this.parameters[index]; - } - - @Override - public void setValue(String id, String value) { - final Integer index = AssetModelImpl.this.paramIndexes.get(id); - if (index == null) { + ((LegacyChannelModel) model).removeParameters(this.assetConfiguration.getParameters()); return; } - this.parameters[index].setValue(value); - } - - @Override - public boolean isValid(final String id) { - final Integer index = AssetModelImpl.this.paramIndexes.get(id); - if (index == null) { - return false; - } - final GwtConfigParameter param = getParameter(index); - - return ValidationUtil.validateParameter(param, param.getValue()); - } - - @Override - public String getValue(String id) { - final Integer index = AssetModelImpl.this.paramIndexes.get(id); - if (index == null) { - return null; - } - return this.parameters[index].getValue(); - } - - private void remove() { - final Iterator iterator = AssetModelImpl.this.assetConfiguration.getParameters() - .iterator(); - while (iterator.hasNext()) { - final GwtConfigParameter param = iterator.next(); - for (GwtConfigParameter parameter : this.parameters) { - if (parameter == param) { - iterator.remove(); - } - } - } } } @@ -262,8 +210,8 @@ public String getAssetPid() { @Override public boolean isValid() { for (final ChannelModel model : this.channelModels) { - for (final String param : this.paramIndexes.keySet()) { - if (!model.isValid(param)) { + for (final Map.Entry entry : this.paramIndexes.entrySet()) { + if (!model.isValid(entry.getValue())) { return false; } } @@ -285,8 +233,9 @@ public void addAllChannels(final AssetModel other) { final ChannelModel channel = this.channelModels.stream() .filter(c -> c.getChannelName().contentEquals(model.getChannelName())).findAny() .orElseGet(() -> createNewChannel(model.getChannelName())); - for (final String param : this.paramIndexes.keySet()) { - channel.setValue(param, model.getValue(param)); + for (final Map.Entry entry : this.paramIndexes.entrySet()) { + Integer index = entry.getValue(); + channel.setValue(index, model.getValue(index)); } } } @@ -299,4 +248,14 @@ public void replaceChannels(final AssetModel other) { addAllChannels(other); } + @Override + public Integer getParameterIndex(String value) { + return this.paramIndexes.get(value); + } + + @Override + public Map getParameterIndexes() { + return this.paramIndexes; + } + } diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/LegacyChannelModel.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/LegacyChannelModel.java new file mode 100644 index 00000000000..19e23e776df --- /dev/null +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/ui/drivers/assets/LegacyChannelModel.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + *******************************************************************************/ + +package org.eclipse.kura.web.client.ui.drivers.assets; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.kura.web.client.util.ValidationUtil; +import org.eclipse.kura.web.shared.AssetConstants; +import org.eclipse.kura.web.shared.DataType; +import org.eclipse.kura.web.shared.ScaleOffsetType; +import org.eclipse.kura.web.shared.model.GwtConfigParameter; +import org.eclipse.kura.web.shared.model.GwtConfigParameter.GwtConfigParameterType; + +public class LegacyChannelModel implements AssetModel.ChannelModel { + + private final String channelName; + private final GwtConfigParameter[] parameters; + + private GwtConfigParameterType subtype; + + public LegacyChannelModel(String channelName, GwtConfigParameter[] parameters) { + this.channelName = channelName; + this.parameters = parameters; + + // calculate the subtype for scale and offset parameters based on the scaleoffset.type and value type + // properties. + detectSubtype(); + + setScaleOffsetSubtype(); + } + + private void setScaleOffsetSubtype() { + for (GwtConfigParameter param : this.parameters) { + if (param.getId().equals(getId(AssetConstants.VALUE_SCALE)) + || param.getId().equals(getId(AssetConstants.VALUE_OFFSET))) { + + param.setSubtype(this.subtype); + } + } + } + + private void updateScaleOffsetSubtypeForParameters() { + for (GwtConfigParameter param : this.parameters) { + if (param.getSubtype() != null && param.getSubtype() != this.subtype) { + param.setSubtype(this.subtype); + } + } + } + + private void detectSubtype() { + + GwtConfigParameterType subType = null; + + ScaleOffsetType scaleOffsetType = null; + DataType valueType = null; + + for (GwtConfigParameter param : this.parameters) { + if (param.getId().equals(getId(AssetConstants.SCALE_OFFSET_TYPE))) { + String paramValue = param.getValue() != null ? param.getValue() : param.getDefault(); + scaleOffsetType = ScaleOffsetType.getScaleOffsetType(paramValue); + } + if (param.getId().equals(getId(AssetConstants.VALUE_TYPE))) { + String paramValue = param.getValue() != null ? param.getValue() : param.getDefault(); + valueType = DataType.getDataType(paramValue); + } + } + + if (scaleOffsetType != null && valueType != null) { + subType = scaleOffsetType == ScaleOffsetType.DEFINED_BY_VALUE_TYPE + ? GwtConfigParameterType.valueOf(valueType.name()) + : GwtConfigParameterType.valueOf(scaleOffsetType.name()); + } + + this.subtype = subType; + } + + private String getId(AssetConstants assetConstant) { + return this.channelName + AssetConstants.CHANNEL_PROPERTY_SEPARATOR.value() + assetConstant.value(); + } + + @Override + public String getChannelName() { + return this.channelName; + } + + @Override + public GwtConfigParameter getParameter(int index) { + return this.parameters[index]; + } + + @Override + public void setValue(Integer index, String value) { + if (index == null) { + return; + } + GwtConfigParameter param = this.parameters[index]; + param.setValue(value); + + if (param.getId().equals(getId(AssetConstants.SCALE_OFFSET_TYPE)) + || param.getId().equals(getId(AssetConstants.VALUE_TYPE))) { + + detectSubtype(); + updateScaleOffsetSubtypeForParameters(); + } + } + + @Override + public boolean isValid(final Integer index) { + if (index == null) { + return false; + } + final GwtConfigParameter param = getParameter(index); + + return ValidationUtil.validateParameter(param, param.getValue()); + } + + @Override + public String getValue(final Integer index) { + if (index == null) { + return null; + } + return this.parameters[index].getValue(); + } + + @Override + public String toString() { + return "LegacyChannelModel [channelName=" + this.channelName + ", parameters=" + + Arrays.toString(this.parameters) + "]"; + } + + public static LegacyChannelModelBuilder builder(String channelName, int parameterCount) { + return new LegacyChannelModelBuilder(channelName, parameterCount); + } + + public void removeParameters(List parametersToRemove) { + final Iterator iterator = parametersToRemove.iterator(); + while (iterator.hasNext()) { + final GwtConfigParameter param = iterator.next(); + for (GwtConfigParameter parameter : this.parameters) { + if (parameter == param) { + iterator.remove(); + } + } + } + } + + public static class LegacyChannelModelBuilder { + + private String channelName; + private GwtConfigParameter[] parameters; + + public LegacyChannelModelBuilder(String channelName, int parameterCount) { + this.channelName = channelName; + this.parameters = new GwtConfigParameter[parameterCount]; + } + + public LegacyChannelModelBuilder addParameter(int index, GwtConfigParameter param) { + this.parameters[index] = param; + return this; + } + + public LegacyChannelModel build() { + return new LegacyChannelModel(this.channelName, this.parameters); + } + } + +} diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/util/ValidationUtil.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/util/ValidationUtil.java index b51c4c0af54..d4db6102460 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/util/ValidationUtil.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/client/util/ValidationUtil.java @@ -19,6 +19,7 @@ import org.eclipse.kura.web.client.messages.Messages; import org.eclipse.kura.web.shared.model.GwtConfigComponent; import org.eclipse.kura.web.shared.model.GwtConfigParameter; +import org.eclipse.kura.web.shared.model.GwtConfigParameter.GwtConfigParameterType; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.TakesValue; @@ -61,8 +62,18 @@ public static void validateParameter(GwtConfigParameter param, String value, Val return; } + validateType(param.getType(), param, consumer, trimmedValue); + validateType(param.getSubtype(), param, consumer, trimmedValue); + + } + + private static void validateType(GwtConfigParameterType type, GwtConfigParameter param, + ValidationErrorConsumer consumer, String trimmedValue) { + if (type == null) { + return; + } try { - switch (param.getType()) { + switch (type) { case BOOLEAN: validateBoolean(trimmedValue, param, consumer); break; @@ -99,7 +110,6 @@ public static void validateParameter(GwtConfigParameter param, String value, Val } catch (NumberFormatException e) { consumer.addError(MessageUtils.get(INVALID_VALUE, trimmedValue)); } - } public static boolean validateParameters(GwtConfigComponent component) { diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/DataType.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/DataType.java new file mode 100644 index 00000000000..ed3dfa0b777 --- /dev/null +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/DataType.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2016, 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + * Amit Kumar Mondal + ******************************************************************************/ + +/** + * This is a copy of {@code org.eclipse.kura.type.DataType} + */ + +package org.eclipse.kura.web.shared; + +import org.eclipse.kura.type.TypedValue; + +/** + * This contains all the required data type constants required for representing + * Java data types as {@link TypedValue} + * + * @since 1.2 + */ +public enum DataType { + + BOOLEAN, + + BYTE_ARRAY, + + DOUBLE, + + INTEGER, + + LONG, + + FLOAT, + + STRING; + + /** + * Converts {@code stringDataType}, if possible, to the related {@link DataType}. + * + * @param stringDataType + * String that we want to use to get the respective {@link DataType}. + * @return a DataType that corresponds to the String passed as argument. + * @throws IllegalArgumentException + * if the passed string does not correspond to an existing {@link DataType}. + */ + public static DataType getDataType(String stringDataType) { + if (INTEGER.name().equalsIgnoreCase(stringDataType)) { + return INTEGER; + } + if (FLOAT.name().equalsIgnoreCase(stringDataType)) { + return FLOAT; + } + if (DOUBLE.name().equalsIgnoreCase(stringDataType)) { + return DOUBLE; + } + if (LONG.name().equalsIgnoreCase(stringDataType)) { + return LONG; + } + if (BYTE_ARRAY.name().equalsIgnoreCase(stringDataType)) { + return BYTE_ARRAY; + } + if (BOOLEAN.name().equalsIgnoreCase(stringDataType)) { + return BOOLEAN; + } + if (STRING.name().equalsIgnoreCase(stringDataType)) { + return STRING; + } + + throw new IllegalArgumentException("Cannot convert to DataType"); + } +} diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/ScaleOffsetType.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/ScaleOffsetType.java new file mode 100644 index 00000000000..60ee8229af5 --- /dev/null +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/ScaleOffsetType.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2024 Eurotech and/or its affiliates and others + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Eurotech + ******************************************************************************/ +package org.eclipse.kura.web.shared; + +/** + * This contains all the required type constants for representing + * Scale and Offset. + * + * This is a copy of {@code org.eclipse.kura.channel.ScaleOffsetType} + * + * @since 2.8 + */ + +public enum ScaleOffsetType { + + DEFINED_BY_VALUE_TYPE, + + DOUBLE, + + LONG; + + /** + * Converts {@code stringScaleOffsetType}, if possible, to the related {@link ScaleOffsetType}. + * + * @param stringDataType + * String that we want to use to get the respective {@link ScaleOffsetType}. + * @return a ScaleOffsetType that corresponds to the String passed as argument. + * @throws IllegalArgumentException + * if the passed string does not correspond to an existing {@link ScaleOffsetType}. + */ + public static ScaleOffsetType getScaleOffsetType(String stringScaleOffsetType) { + + if (DEFINED_BY_VALUE_TYPE.name().equalsIgnoreCase(stringScaleOffsetType)) { + return DEFINED_BY_VALUE_TYPE; + } + + if (DOUBLE.name().equalsIgnoreCase(stringScaleOffsetType)) { + return DOUBLE; + } + + if (LONG.name().equalsIgnoreCase(stringScaleOffsetType)) { + return LONG; + } + + throw new IllegalArgumentException("Cannot convert to DataType"); + } + +} diff --git a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/model/GwtConfigParameter.java b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/model/GwtConfigParameter.java index 8499bcf073d..5d120565f11 100644 --- a/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/model/GwtConfigParameter.java +++ b/kura/org.eclipse.kura.web2/src/main/java/org/eclipse/kura/web/shared/model/GwtConfigParameter.java @@ -1,17 +1,18 @@ /******************************************************************************* - * Copyright (c) 2011, 2020 Eurotech and/or its affiliates and others - * + * Copyright (c) 2011, 2024 Eurotech and/or its affiliates and others + * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 - * + * * Contributors: * Eurotech *******************************************************************************/ package org.eclipse.kura.web.shared.model; +import java.util.Arrays; import java.util.Map; import com.google.gwt.user.client.rpc.IsSerializable; @@ -19,6 +20,7 @@ public class GwtConfigParameter implements IsSerializable { public enum GwtConfigParameterType { + STRING, LONG, DOUBLE, @@ -34,130 +36,150 @@ public enum GwtConfigParameterType { } } - private String m_id; - private String m_name; - private String m_description; - private GwtConfigParameterType m_type; - private boolean m_required; - private String m_default; - private int m_cardinality; - private Map m_options; - private String m_min; - private String m_max; - private String m_value; // used for fields with single cardinality - private String[] m_values; // used for fields with multiple cardinality + private String id; + private String name; + private String description; + private GwtConfigParameterType type; + private GwtConfigParameterType subtype; + + private boolean required; + private String defaultValue; + private int cardinality; + private Map options; + private String min; + private String max; + private String value; // used for fields with single cardinality + private String[] values; // used for fields with multiple cardinality public GwtConfigParameter() { } public GwtConfigParameter(GwtConfigParameter reference) { - this.m_id = reference.getId(); - this.m_name = reference.getName(); - this.m_description = reference.getDescription(); - this.m_type = reference.getType(); - this.m_required = reference.isRequired(); - this.m_default = reference.getDefault(); - this.m_cardinality = reference.getCardinality(); - this.m_options = reference.getOptions(); - this.m_min = reference.getMin(); - this.m_max = reference.getMax(); - this.m_value = reference.getValue(); - this.m_values = reference.getValues(); + this.id = reference.getId(); + this.name = reference.getName(); + this.description = reference.getDescription(); + this.type = reference.getType(); + this.subtype = reference.getSubtype(); + this.required = reference.isRequired(); + this.defaultValue = reference.getDefault(); + this.cardinality = reference.getCardinality(); + this.options = reference.getOptions(); + this.min = reference.getMin(); + this.max = reference.getMax(); + this.value = reference.getValue(); + this.values = reference.getValues(); } public String getId() { - return this.m_id; + return this.id; } public void setId(String id) { - this.m_id = id; + this.id = id; } public String getName() { - return this.m_name; + return this.name; } public void setName(String name) { - this.m_name = name; + this.name = name; } public String getDescription() { - return this.m_description; + return this.description; } public void setDescription(String description) { - this.m_description = description; + this.description = description; } public GwtConfigParameterType getType() { - return this.m_type; + return this.type; } public void setType(GwtConfigParameterType type) { - this.m_type = type; + this.type = type; + } + + public GwtConfigParameterType getSubtype() { + return this.subtype; + } + + public void setSubtype(GwtConfigParameterType subtype) { + this.subtype = subtype; } public boolean isRequired() { - return this.m_required; + return this.required; } public void setRequired(boolean required) { - this.m_required = required; + this.required = required; } public String getDefault() { - return this.m_default; + return this.defaultValue; } - public void setDefault(String default1) { - this.m_default = default1; + public void setDefault(String defaultValue) { + this.defaultValue = defaultValue; } public int getCardinality() { - return this.m_cardinality; + return this.cardinality; } public void setCardinality(int cardinality) { - this.m_cardinality = cardinality; + this.cardinality = cardinality; } public Map getOptions() { - return this.m_options; + return this.options; } public void setOptions(Map options) { - this.m_options = options; + this.options = options; } public String getMin() { - return this.m_min; + return this.min; } public void setMin(String min) { - this.m_min = min; + this.min = min; } public String getMax() { - return this.m_max; + return this.max; } public void setMax(String max) { - this.m_max = max; + this.max = max; } public String getValue() { - return this.m_value; + return this.value; } public void setValue(String value) { - this.m_value = value; + this.value = value; } public String[] getValues() { - return this.m_values; + return this.values; } public void setValues(String[] values) { - this.m_values = values; + this.values = values; + } + + @Override + public String toString() { + return "GwtConfigParameter [id=" + this.id + ", name=" + this.name + ", description=" + this.description + + ", type=" + this.type + ", subtype=" + this.subtype + ", required=" + this.required + + ", defaultValue=" + this.defaultValue + ", cardinality=" + this.cardinality + ", options=" + + this.options + ", min=" + this.min + ", max=" + this.max + ", value=" + this.value + ", values=" + + Arrays.toString(this.values) + "]"; } } diff --git a/kura/test/org.eclipse.kura.rest.wire.provider.test/src/main/java/org/eclipse/kura/rest/wire/provider/test/Snippets.java b/kura/test/org.eclipse.kura.rest.wire.provider.test/src/main/java/org/eclipse/kura/rest/wire/provider/test/Snippets.java index 7e3ad330edf..87a883a60e3 100644 --- a/kura/test/org.eclipse.kura.rest.wire.provider.test/src/main/java/org/eclipse/kura/rest/wire/provider/test/Snippets.java +++ b/kura/test/org.eclipse.kura.rest.wire.provider.test/src/main/java/org/eclipse/kura/rest/wire/provider/test/Snippets.java @@ -120,9 +120,9 @@ public class Snippets { + " },\n" // + " {\n" // + " \"name\": \"scale\",\n" // - + " \"description\": \"Scale to be applied to the numeric value of the channel\",\n" // + + " \"description\": \"Scale to be applied to the numeric value of the channel.\",\n" // + " \"id\": \"+scale\",\n" // - + " \"type\": \"DOUBLE\",\n" // + + " \"type\": \"STRING\",\n" // + " \"cardinality\": 0,\n" // + " \"isRequired\": false\n" // + " },\n" // @@ -130,7 +130,7 @@ public class Snippets { + " \"name\": \"offset\",\n" // + " \"description\": \"Offset to be applied to the numeric value of the channel\",\n" // + " \"id\": \"+offset\",\n" // - + " \"type\": \"DOUBLE\",\n" // + + " \"type\": \"STRING\",\n" // + " \"cardinality\": 0,\n" // + " \"isRequired\": false\n" // + " },\n" //