Skip to content

Commit 7e10182

Browse files
author
Ravi Nadahar
committed
Implement support for both "new" and "old" YAML syntax for the add-on marketplace
Signed-off-by: Ravi Nadahar <nadahar@rediffmail.com>
1 parent a950a6a commit 7e10182

3 files changed

Lines changed: 129 additions & 32 deletions

File tree

bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/automation/MarketplaceRuleTemplateProvider.java

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import java.io.IOException;
1717
import java.io.InputStreamReader;
1818
import java.nio.charset.StandardCharsets;
19+
import java.util.ArrayList;
1920
import java.util.Collection;
2021
import java.util.HashSet;
22+
import java.util.List;
2123
import java.util.Locale;
2224
import java.util.Map;
2325
import java.util.Set;
@@ -26,6 +28,7 @@
2628
import org.eclipse.jdt.annotation.NonNullByDefault;
2729
import org.eclipse.jdt.annotation.Nullable;
2830
import org.openhab.core.automation.Module;
31+
import org.openhab.core.automation.converter.RuleTemplateParser;
2932
import org.openhab.core.automation.dto.RuleTemplateDTO;
3033
import org.openhab.core.automation.dto.RuleTemplateDTOMapper;
3134
import org.openhab.core.automation.parser.Parser;
@@ -60,6 +63,7 @@ public class MarketplaceRuleTemplateProvider extends AbstractManagedProvider<Rul
6063
implements RuleTemplateProvider {
6164

6265
private final Map<String, Parser<RuleTemplate>> parsers = new ConcurrentHashMap<>();
66+
private final Map<String, RuleTemplateParser> fileConversionParsers = new ConcurrentHashMap<>();
6367
ObjectMapper yamlMapper;
6468

6569
@Activate
@@ -94,6 +98,15 @@ public void removeParser(Parser<RuleTemplate> parser, Map<String, String> proper
9498
parsers.remove(parserType);
9599
}
96100

101+
@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
102+
protected void addRuleTemplateParser(RuleTemplateParser templateParser) {
103+
fileConversionParsers.put(templateParser.getParserFormat(), templateParser);
104+
}
105+
106+
protected void removeRuleTemplateParser(RuleTemplateParser templateParser) {
107+
fileConversionParsers.remove(templateParser.getParserFormat());
108+
}
109+
97110
@Override
98111
public @Nullable RuleTemplate getTemplate(String uid, @Nullable Locale locale) {
99112
return get(uid);
@@ -158,30 +171,62 @@ public void addTemplateAsYAML(String uid, String yaml) throws ParsingException,
158171
* @throws ValidationException If the validation fails.
159172
*/
160173
protected void addTemplate(String uid, String content, String format) throws ParsingException, ValidationException {
161-
Parser<RuleTemplate> parser = parsers.get(format);
174+
Set<RuleTemplate> templates;
175+
if (Parser.FORMAT_YAML.equals(format) && content.trim().startsWith("version:")) {
176+
// Use the "new YAML" parser
177+
RuleTemplateParser parser = fileConversionParsers.get("YAML");
178+
179+
// The parser might not have been registered yet
180+
if (parser == null) {
181+
throw new ParsingException(new ParsingNestedException(ParsingNestedException.TEMPLATE,
182+
"No " + format.toUpperCase(Locale.ROOT) + " parser available", null));
183+
}
162184

163-
// The parser might not have been registered yet
164-
if (parser == null) {
165-
throw new ParsingException(new ParsingNestedException(ParsingNestedException.TEMPLATE,
166-
"No " + format.toUpperCase(Locale.ROOT) + " parser available", null));
167-
}
185+
List<String> errors = new ArrayList<>();
186+
List<String> warnings = new ArrayList<>();
187+
String modelName = parser.startParsingFormat(content, errors, warnings);
188+
if (modelName == null || !errors.isEmpty()) {
189+
if (modelName != null) {
190+
parser.finishParsingFormat(modelName);
191+
}
192+
throw new ParsingException(new ParsingNestedException(ParsingNestedException.TEMPLATE,
193+
"Parsing of " + format.toUpperCase(Locale.ROOT) + " failed: " + String.join(", ", errors),
194+
null));
195+
}
196+
if (!warnings.isEmpty()) {
197+
logger.warn("Parsing of markedplace rule template add-on {} has warnings: {}", uid,
198+
String.join(", ", warnings));
199+
}
200+
templates = Set.copyOf(parser.getParsedObjects(modelName));
201+
parser.finishParsingFormat(modelName);
202+
} else {
203+
// Use the "old YAML" parser
204+
Parser<RuleTemplate> parser = parsers.get(format);
205+
206+
// The parser might not have been registered yet
207+
if (parser == null) {
208+
throw new ParsingException(new ParsingNestedException(ParsingNestedException.TEMPLATE,
209+
"No " + format.toUpperCase(Locale.ROOT) + " parser available", null));
210+
}
168211

169-
try (InputStreamReader isr = new InputStreamReader(
170-
new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))) {
171-
Set<RuleTemplate> templates = parser.parse(isr);
172-
173-
// Add a tag with the marketplace add-on ID to be able to identify the template in the registry
174-
Set<String> tags;
175-
for (RuleTemplate template : templates) {
176-
validateTemplate(template);
177-
tags = new HashSet<String>(template.getTags());
178-
tags.add(uid);
179-
add(new RuleTemplate(template.getUID(), template.getLabel(), template.getDescription(), tags,
180-
template.getTriggers(), template.getConditions(), template.getActions(),
181-
template.getConfigurationDescriptions(), template.getVisibility()));
212+
try (InputStreamReader isr = new InputStreamReader(
213+
new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)))) {
214+
templates = parser.parse(isr);
215+
} catch (IOException e) {
216+
// Impossible for ByteArrayInputStream
217+
templates = Set.of();
182218
}
183-
} catch (IOException e) {
184-
// Impossible for ByteArrayInputStream
219+
}
220+
221+
// Add a tag with the marketplace add-on ID to be able to identify the template in the registry
222+
Set<String> tags;
223+
for (RuleTemplate template : templates) {
224+
validateTemplate(template);
225+
tags = new HashSet<String>(template.getTags());
226+
tags.add(uid);
227+
add(new RuleTemplate(template.getUID(), template.getLabel(), template.getDescription(), tags,
228+
template.getTriggers(), template.getConditions(), template.getActions(),
229+
template.getConfigurationDescriptions(), template.getVisibility()));
185230
}
186231
}
187232

bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/community/CommunityUIWidgetAddonHandler.java

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@
2222
import java.net.URL;
2323
import java.nio.charset.StandardCharsets;
2424
import java.text.SimpleDateFormat;
25+
import java.util.ArrayList;
26+
import java.util.Collection;
27+
import java.util.List;
2528
import java.util.Locale;
29+
import java.util.Map;
30+
import java.util.concurrent.ConcurrentHashMap;
2631

2732
import org.eclipse.jdt.annotation.NonNullByDefault;
2833
import org.openhab.core.addon.Addon;
@@ -31,9 +36,12 @@
3136
import org.openhab.core.ui.components.RootUIComponent;
3237
import org.openhab.core.ui.components.UIComponentRegistry;
3338
import org.openhab.core.ui.components.UIComponentRegistryFactory;
39+
import org.openhab.core.ui.components.converter.RootUIComponentParser;
3440
import org.osgi.service.component.annotations.Activate;
3541
import org.osgi.service.component.annotations.Component;
3642
import org.osgi.service.component.annotations.Reference;
43+
import org.osgi.service.component.annotations.ReferenceCardinality;
44+
import org.osgi.service.component.annotations.ReferencePolicy;
3745
import org.slf4j.Logger;
3846
import org.slf4j.LoggerFactory;
3947

@@ -55,6 +63,7 @@ public class CommunityUIWidgetAddonHandler implements MarketplaceAddonHandler {
5563

5664
private final ObjectMapper yamlMapper;
5765
private final UIComponentRegistry widgetRegistry;
66+
private final Map<String, RootUIComponentParser> parsers = new ConcurrentHashMap<>();
5867

5968
@Activate
6069
public CommunityUIWidgetAddonHandler(final @Reference UIComponentRegistryFactory uiComponentRegistryFactory) {
@@ -66,6 +75,15 @@ public CommunityUIWidgetAddonHandler(final @Reference UIComponentRegistryFactory
6675
yamlMapper.getSerializationConfig().getAnnotationIntrospector()));
6776
}
6877

78+
@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
79+
protected void addParser(RootUIComponentParser parser) {
80+
parsers.put(parser.getParserFormat(), parser);
81+
}
82+
83+
protected void removeParser(RootUIComponentParser parser) {
84+
parsers.remove(parser.getParserFormat());
85+
}
86+
6987
@Override
7088
public boolean supports(String type, String contentType) {
7189
return "ui".equals(type) && UIWIDGETS_CONTENT_TYPE.equals(contentType);
@@ -118,15 +136,48 @@ private String getWidgetFromURL(String urlString) throws IOException {
118136
}
119137
}
120138

121-
private void addWidgetAsYAML(String id, String yaml) {
122-
try {
123-
RootUIComponent widget = yamlMapper.readValue(yaml, RootUIComponent.class);
124-
// add a tag with the add-on ID to be able to identify the widget in the registry
125-
widget.addTag(id);
126-
widgetRegistry.add(widget);
127-
} catch (IOException e) {
128-
logger.error("Unable to parse YAML: {}", e.getMessage());
129-
throw new IllegalArgumentException("Unable to parse YAML");
139+
private void addWidgetAsYAML(String id, String yaml) throws MarketplaceHandlerException {
140+
if (yaml.trim().startsWith("version:")) {
141+
// Use the "new YAML" parser
142+
RootUIComponentParser parser = parsers.get("YAML");
143+
144+
// The parser might not have been registered yet
145+
if (parser == null) {
146+
throw new MarketplaceHandlerException("No Widget YAML parser available", null);
147+
}
148+
149+
List<String> errors = new ArrayList<>();
150+
List<String> warnings = new ArrayList<>();
151+
String modelName = parser.startParsingFormat(yaml, errors, warnings);
152+
if (modelName == null || !errors.isEmpty()) {
153+
if (modelName != null) {
154+
parser.finishParsingFormat(modelName);
155+
}
156+
throw new MarketplaceHandlerException("Parsing of YAML failed: " + String.join(", ", errors), null);
157+
}
158+
if (!warnings.isEmpty()) {
159+
logger.warn("Parsing of markedplace widgeet add-on {} has warnings: {}", id,
160+
String.join(", ", warnings));
161+
}
162+
Collection<? extends RootUIComponent> widgets = parser.getParsedObjects(modelName);
163+
parser.finishParsingFormat(modelName);
164+
165+
for (RootUIComponent widget : widgets) {
166+
// add a tag with the add-on ID to be able to identify the widget in the registry
167+
widget.addTag(id);
168+
widgetRegistry.add(widget);
169+
}
170+
} else {
171+
// Use the "old YAML" parser
172+
try {
173+
RootUIComponent widget = yamlMapper.readValue(yaml, RootUIComponent.class);
174+
// add a tag with the add-on ID to be able to identify the widget in the registry
175+
widget.addTag(id);
176+
widgetRegistry.add(widget);
177+
} catch (IOException e) {
178+
logger.error("Unable to parse YAML: {}", e.getMessage());
179+
throw new IllegalArgumentException("Unable to parse YAML");
180+
}
130181
}
131182
}
132183
}

bundles/org.openhab.core.model.yaml/src/main/java/org/openhab/core/model/yaml/internal/config/YamlConfigDescriptionDTO.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ public YamlConfigDescriptionDTO() {
5959
* Creates a new instance based on the specified {@link ConfiConfigDescriptiongDescriptionParameterGroup}.
6060
*
6161
* @param configDescription the {@link ConfigDescription}.
62+
* @param includeDefault whether boolean values with the default value should be included.
6263
*/
63-
public YamlConfigDescriptionDTO(@NonNull ConfigDescription configDescription) {
64+
public YamlConfigDescriptionDTO(@NonNull ConfigDescription configDescription, boolean includeDefault) {
6465
this.uri = toDecodedString(configDescription.getUID());
6566
List<@NonNull ConfigDescriptionParameter> fromParams = configDescription.getParameters();
6667
if (!fromParams.isEmpty()) {
6768
Map<String, YamlConfigDescriptionParameterDTO> params = new LinkedHashMap<>();
6869
for (ConfigDescriptionParameter param : fromParams) {
69-
params.put(param.getName(), new YamlConfigDescriptionParameterDTO(param));
70+
params.put(param.getName(), new YamlConfigDescriptionParameterDTO(param, includeDefault));
7071
}
7172
this.params = params;
7273
}

0 commit comments

Comments
 (0)