Skip to content

Commit 84e414b

Browse files
committed
Added SWE-Common validator for Datastreams
1 parent b606b42 commit 84e414b

7 files changed

Lines changed: 470 additions & 278 deletions

File tree

FROST-Server.SQLjooq/src/main/java/de/fraunhofer/iosb/ilt/frostserver/persistence/pgjooq/utils/fieldmapper/FieldMapperResult.java

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ public <T extends StaMainTable<T>> void registerMapping(JooqPersistenceManager p
107107
pfReg.addEntry(property,
108108
true,
109109
new PropertyFieldRegistry.ConverterRecordDeflt<>(
110-
(T t, Record tuple, Entity entity, DataSize dataSize) -> readResultFromDb(entity, property, t, tuple, dataSize, idxType, idxString, idxNumber, idxBoolean, idxJson),
111-
(t, entity, insertFields) -> handleResult(entity, property, t, insertFields, idxType, idxString, idxNumber, idxBoolean, idxJson),
110+
(T t, Record tuple, Entity entity, DataSize dataSize) -> readResultFromDb(entity, property, t, tuple, dataSize),
111+
(t, entity, insertFields) -> handleResult(entity, property, t, insertFields),
112112
(t, entity, updateFields, message) -> {
113-
handleResult(entity, property, t, updateFields, idxType, idxString, idxNumber, idxBoolean, idxJson);
113+
handleResult(entity, property, t, updateFields);
114114
message.addField(property);
115115
}),
116116
new PropertyFieldRegistry.NFP<>("n", t -> t.field(idxNumber)),
@@ -120,62 +120,56 @@ public <T extends StaMainTable<T>> void registerMapping(JooqPersistenceManager p
120120
new PropertyFieldRegistry.NFP<>("t", t -> t.field(idxType)));
121121
}
122122

123-
private <T extends StaMainTable<T>> void handleResult(
124-
Entity entity, Property property,
125-
T table, Map<Field, Object> output,
126-
int idxReTy, int idxReSt, int idxReNu, int idxReBo, int idxReJs) {
123+
private <T extends StaMainTable<T>> void handleResult(Entity entity, Property property, T table, Map<Field, Object> output) {
127124
Object result = entity.getProperty(property);
128125
if (result instanceof Number number) {
129-
output.put(table.field(idxReTy), ResultType.NUMBER.sqlValue());
130-
output.put(table.field(idxReSt), result.toString());
131-
output.put(table.field(idxReNu), number.doubleValue());
132-
output.put(table.field(idxReBo), null);
133-
output.put(table.field(idxReJs), null);
126+
output.put(table.field(fieldTypeIdx), ResultType.NUMBER.sqlValue());
127+
output.put(table.field(fieldStringIdx), result.toString());
128+
output.put(table.field(fieldNumberIdx), number.doubleValue());
129+
output.put(table.field(fieldBooleanIdx), null);
130+
output.put(table.field(fieldJsonIdx), null);
134131
} else if (result instanceof Boolean) {
135-
output.put(table.field(idxReTy), ResultType.BOOLEAN.sqlValue());
136-
output.put(table.field(idxReSt), result.toString());
137-
output.put(table.field(idxReBo), result);
138-
output.put(table.field(idxReNu), null);
139-
output.put(table.field(idxReJs), null);
132+
output.put(table.field(fieldTypeIdx), ResultType.BOOLEAN.sqlValue());
133+
output.put(table.field(fieldStringIdx), result.toString());
134+
output.put(table.field(fieldBooleanIdx), result);
135+
output.put(table.field(fieldNumberIdx), null);
136+
output.put(table.field(fieldJsonIdx), null);
140137
} else if (result instanceof String) {
141-
output.put(table.field(idxReTy), ResultType.STRING.sqlValue());
142-
output.put(table.field(idxReSt), result.toString());
143-
output.put(table.field(idxReNu), null);
144-
output.put(table.field(idxReBo), null);
145-
output.put(table.field(idxReJs), null);
138+
output.put(table.field(fieldTypeIdx), ResultType.STRING.sqlValue());
139+
output.put(table.field(fieldStringIdx), result.toString());
140+
output.put(table.field(fieldNumberIdx), null);
141+
output.put(table.field(fieldBooleanIdx), null);
142+
output.put(table.field(fieldJsonIdx), null);
146143
} else {
147-
output.put(table.field(idxReTy), ResultType.OBJECT_ARRAY.sqlValue());
148-
output.put(table.field(idxReJs), EntityFactories.objectToJson(result));
149-
output.put(table.field(idxReSt), null);
150-
output.put(table.field(idxReNu), null);
151-
output.put(table.field(idxReBo), null);
144+
output.put(table.field(fieldTypeIdx), ResultType.OBJECT_ARRAY.sqlValue());
145+
output.put(table.field(fieldJsonIdx), EntityFactories.objectToJson(result));
146+
output.put(table.field(fieldStringIdx), null);
147+
output.put(table.field(fieldNumberIdx), null);
148+
output.put(table.field(fieldBooleanIdx), null);
152149
}
153150
}
154151

155-
private <T extends StaMainTable<T>> void readResultFromDb(
156-
Entity entity, Property property,
157-
T table, Record tuple, DataSize dataSize,
158-
int idxReTy, int idxReSt, int idxReNu, int idxReBo, int idxReJs) {
159-
Short resultTypeOrd = Utils.getFieldOrNull(tuple, (Field<Short>) table.field(idxReTy));
152+
private <T extends StaMainTable<T>> void readResultFromDb(Entity entity, Property property, T table, Record tuple, DataSize dataSize) {
153+
Short resultTypeOrd = Utils.getFieldOrNull(tuple, (Field<Short>) table.field(fieldTypeIdx));
160154
if (resultTypeOrd != null) {
161155
ResultType resultType = ResultType.fromSqlValue(resultTypeOrd);
162156
switch (resultType) {
163157
case BOOLEAN:
164-
entity.setProperty(property, Utils.getFieldOrNull(tuple, table.field(idxReBo)));
158+
entity.setProperty(property, Utils.getFieldOrNull(tuple, table.field(fieldBooleanIdx)));
165159
break;
166160

167161
case NUMBER:
168-
handleNumber(entity, property, table, tuple, idxReSt, idxReNu);
162+
handleNumber(entity, property, table, tuple, fieldStringIdx, fieldNumberIdx);
169163
break;
170164

171165
case OBJECT_ARRAY:
172-
JsonValue jsonData = Utils.getFieldJsonValue(tuple, (Field<JsonValue>) table.field(idxReJs));
166+
JsonValue jsonData = Utils.getFieldJsonValue(tuple, (Field<JsonValue>) table.field(fieldJsonIdx));
173167
dataSize.increase(jsonData.getStringLength());
174168
entity.setProperty(property, jsonData.getValue());
175169
break;
176170

177171
case STRING:
178-
String stringData = Utils.getFieldOrNull(tuple, (Field<String>) table.field(idxReSt));
172+
String stringData = Utils.getFieldOrNull(tuple, (Field<String>) table.field(fieldStringIdx));
179173
dataSize.increase(stringData == null ? 0 : stringData.length());
180174
entity.setProperty(property, stringData);
181175
break;

FROST-Server.Util/src/main/java/de/fraunhofer/iosb/ilt/frostserver/util/SimpleJsonMapper.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.fasterxml.jackson.annotation.JsonInclude;
2121
import tools.jackson.core.JacksonException;
22+
import tools.jackson.core.TreeNode;
2223
import tools.jackson.core.type.TypeReference;
2324
import tools.jackson.databind.DeserializationFeature;
2425
import tools.jackson.databind.JsonNode;
@@ -88,6 +89,17 @@ public static JsonNode jsonToTree(String json) {
8889
}
8990
}
9091

92+
public static <T> T treeToObject(TreeNode json, Class<T> clazz) {
93+
if (json == null) {
94+
return null;
95+
}
96+
try {
97+
return getSimpleObjectMapper().treeToValue(json, clazz);
98+
} catch (JacksonException ex) {
99+
throw new IllegalStateException(FAILED_JSON_PARSE, ex);
100+
}
101+
}
102+
91103
public static <T> T jsonToObject(String json, Class<T> clazz) {
92104
if (json == null) {
93105
return null;

Plugins/BatchProcessing/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/batchprocessing/json/JsonBatchProcessor.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ public class JsonBatchProcessor implements Iterator<JsonBatchResultItem> {
6464
private static final Logger LOGGER = LoggerFactory.getLogger(JsonBatchProcessor.class.getName());
6565
private static final String REFERENCE_URL_REGEX = "^" + Pattern.quote("$") + "([a-zA-Z0-9_.:,;-]+)";
6666
private static final Pattern REFERENCE_URL_PATTERN = Pattern.compile(REFERENCE_URL_REGEX);
67-
private static final String REFERENCE_JSON_REGEX = Pattern.quote("\"$") + "([a-zA-Z0-9_.:,;-]+)" + Pattern.quote("\"");
67+
private static final String Q_QUOTE = Pattern.quote("\"");
68+
private static final String Q_DOLLAR = Pattern.quote("$");
69+
private static final String Q_BOPEN = Pattern.quote("(");
70+
private static final String Q_BCLOSE = Pattern.quote(")");
71+
private static final String MIDDLE = Q_DOLLAR + "([a-zA-Z0-9_.:,;-]+)";
72+
73+
private static final String REFERENCE_JSON_REGEX = "(" + Q_BOPEN + MIDDLE + Q_BCLOSE + "|" + Q_QUOTE + MIDDLE + Q_QUOTE + ")";
6874
private static final Pattern REFERENCE_JSON_PATTERN = Pattern.compile(REFERENCE_JSON_REGEX);
6975

7076
private final Service service;
@@ -209,14 +215,23 @@ private String replaceIdsJson(String body) {
209215
int idx = 0;
210216
while (matcher.find()) {
211217
result.append(body.substring(idx, matcher.start(0)));
212-
String name = matcher.group(1);
218+
boolean brackets = false;
219+
String name = matcher.group(3);
220+
if (name == null) {
221+
name = matcher.group(2);
222+
brackets = true;
223+
}
213224
ContentIdPair pair = ids.get(name);
214225
if (pair == null) {
215226
LOGGER.debug("Not a match: {}", matcher.group(0));
216227
result.append(matcher.group(0));
217228
} else {
218229
String value = UrlHelper.quoteForJson(pair.value.get(0));
219-
result.append(value);
230+
if (brackets) {
231+
result.append('(').append(value).append(')');
232+
} else {
233+
result.append(value);
234+
}
220235
}
221236
idx = matcher.end(0);
222237
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (C) 2024 Fraunhofer Institut IOSB, Fraunhoferstr. 1, D 76131
3+
* Karlsruhe, Germany.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2;
19+
20+
import de.fraunhofer.iosb.ilt.configurable.annotations.ConfigurableField;
21+
import de.fraunhofer.iosb.ilt.configurable.editor.EditorString;
22+
import de.fraunhofer.iosb.ilt.frostserver.model.ModelRegistry;
23+
import de.fraunhofer.iosb.ilt.frostserver.model.core.Entity;
24+
import de.fraunhofer.iosb.ilt.frostserver.model.core.EntitySet;
25+
import de.fraunhofer.iosb.ilt.frostserver.model.core.EntitySetImpl;
26+
import de.fraunhofer.iosb.ilt.frostserver.model.core.EntityValidator;
27+
import de.fraunhofer.iosb.ilt.frostserver.path.UrlHelper;
28+
import de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2.swecommon.AbstractDataComponent;
29+
import de.fraunhofer.iosb.ilt.frostserver.property.EntityPropertyMain;
30+
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain.NavigationPropertyEntitySet;
31+
import de.fraunhofer.iosb.ilt.frostserver.util.SimpleJsonMapper;
32+
import de.fraunhofer.iosb.ilt.frostserver.util.StringHelper;
33+
import de.fraunhofer.iosb.ilt.frostserver.util.exception.IncompleteEntityException;
34+
import java.util.HashSet;
35+
import java.util.Set;
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
38+
import tools.jackson.core.TreeNode;
39+
40+
/**
41+
* A validator for Datastreams. It checks resultType and resultEncoding. It also
42+
* sets the ObservedProperties based on the definitions used in the resultType.
43+
*/
44+
public class SweCommonValidator implements EntityValidator {
45+
46+
private static final Logger LOGGER = LoggerFactory.getLogger(SweCommonValidator.class.getName());
47+
48+
@ConfigurableField(editor = EditorString.class,
49+
label = "encoding", description = "Name of the property that holds the encoding")
50+
@EditorString.EdOptsString()
51+
private String nameEncoding;
52+
53+
@ConfigurableField(editor = EditorString.class,
54+
label = "structure", description = "Name of the property that holds the SWE-Common structure")
55+
@EditorString.EdOptsString()
56+
private String nameStructure;
57+
58+
@ConfigurableField(editor = EditorString.class,
59+
label = "definitions", description = "Name of the navigation property that collects observed properties")
60+
@EditorString.EdOptsString()
61+
private String nameDefinitions;
62+
63+
private EntityPropertyMain<TreeNode> epEncoding;
64+
private EntityPropertyMain<TreeNode> epStructure;
65+
private NavigationPropertyEntitySet npDefinitions;
66+
private ModelRegistry mr;
67+
private boolean initialised;
68+
69+
private void initialise(Entity entity) {
70+
if (initialised) {
71+
return;
72+
}
73+
if (!StringHelper.isNullOrEmpty(nameEncoding)) {
74+
epEncoding = entity.getEntityType().getEntityProperty(nameEncoding);
75+
}
76+
if (!StringHelper.isNullOrEmpty(nameStructure)) {
77+
epStructure = entity.getEntityType().getEntityProperty(nameStructure);
78+
}
79+
if (!StringHelper.isNullOrEmpty(nameDefinitions)) {
80+
npDefinitions = entity.getEntityType().getNavigationPropertyEntitySet(nameDefinitions);
81+
}
82+
mr = entity.getEntityType().getModelRegistry();
83+
initialised = true;
84+
}
85+
86+
@Override
87+
public void validate(Entity entity) throws IncompleteEntityException {
88+
if (!initialised) {
89+
initialise(entity);
90+
}
91+
TreeNode structureTree = entity.getProperty(epStructure);
92+
if (structureTree == null) {
93+
return;
94+
}
95+
AbstractDataComponent resultType = SimpleJsonMapper.treeToObject(structureTree, AbstractDataComponent.class);
96+
97+
Set<String> defs = new HashSet<>();
98+
resultType.gatherDefinitions(defs);
99+
100+
EntitySet observedProperties = new EntitySetImpl(npDefinitions);
101+
for (String def : defs) {
102+
LOGGER.debug("Found definition {}", def);
103+
Entity op = UrlHelper.parseSelfLink(def, mr, false);
104+
observedProperties.add(op);
105+
}
106+
entity.setProperty(npDefinitions, observedProperties);
107+
}
108+
109+
public String getNameEncoding() {
110+
return nameEncoding;
111+
}
112+
113+
public SweCommonValidator setNameEncoding(String nameEncoding) {
114+
this.nameEncoding = nameEncoding;
115+
return this;
116+
}
117+
118+
public String getNameStructure() {
119+
return nameStructure;
120+
}
121+
122+
public SweCommonValidator setNameStructure(String nameStructure) {
123+
this.nameStructure = nameStructure;
124+
return this;
125+
}
126+
127+
public String getNameDefinitions() {
128+
return nameDefinitions;
129+
}
130+
131+
public SweCommonValidator setNameDefinitions(String nameDefinitions) {
132+
this.nameDefinitions = nameDefinitions;
133+
return this;
134+
}
135+
136+
}

Plugins/CoreModelV2/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/coremodelv2/swecommon/util/SweTypeIdResolver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2.swecommon.util;
1919

2020
import com.fasterxml.jackson.annotation.JsonTypeInfo;
21+
import de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2.swecommon.AbstractSWE;
2122
import de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2.swecommon.AbstractSWEIdentifiable;
2223
import de.fraunhofer.iosb.ilt.frostserver.plugin.coremodelv2.swecommon.constraint.AbstractConstraint;
2324
import java.util.Map;
@@ -41,7 +42,7 @@ public class SweTypeIdResolver implements TypeIdResolver {
4142
private static final Map<String, Class<?>> annnotatedClasses;
4243

4344
static {
44-
final Reflections reflections = new Reflections("de.fraunhofer.iosb.ilt.frostclient.models.swecommon");
45+
final Reflections reflections = new Reflections(AbstractSWE.class.getPackageName());
4546
annnotatedClasses = reflections
4647
.getSubTypesOf(AbstractSWEIdentifiable.class)
4748
.stream()

0 commit comments

Comments
 (0)