|
27 | 27 | import java.util.Objects;
|
28 | 28 | import java.util.Optional;
|
29 | 29 | import java.util.Set;
|
| 30 | +import java.util.concurrent.ConcurrentHashMap; |
30 | 31 | import java.util.function.Predicate;
|
31 | 32 | import java.util.stream.Collectors;
|
32 | 33 | import java.util.stream.Stream;
|
|
89 | 90 | import org.openhab.core.library.types.UpDownType;
|
90 | 91 | import org.openhab.core.semantics.SemanticTagRegistry;
|
91 | 92 | import org.openhab.core.semantics.SemanticsPredicates;
|
| 93 | +import org.openhab.core.thing.syntaxgenerator.ItemSyntaxGenerator; |
92 | 94 | import org.openhab.core.types.Command;
|
93 | 95 | import org.openhab.core.types.State;
|
94 | 96 | import org.openhab.core.types.TypeParser;
|
95 | 97 | import org.osgi.service.component.annotations.Activate;
|
96 | 98 | import org.osgi.service.component.annotations.Component;
|
97 | 99 | import org.osgi.service.component.annotations.Deactivate;
|
98 | 100 | import org.osgi.service.component.annotations.Reference;
|
| 101 | +import org.osgi.service.component.annotations.ReferenceCardinality; |
| 102 | +import org.osgi.service.component.annotations.ReferencePolicy; |
99 | 103 | import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
|
100 | 104 | import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
|
101 | 105 | import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
|
@@ -183,6 +187,7 @@ private static void respectForwarded(final UriBuilder uriBuilder, final @Context
|
183 | 187 | private final MetadataSelectorMatcher metadataSelectorMatcher;
|
184 | 188 | private final SemanticTagRegistry semanticTagRegistry;
|
185 | 189 | private final TimeZoneProvider timeZoneProvider;
|
| 190 | + private final Map<String, ItemSyntaxGenerator> itemSyntaxGenerators = new ConcurrentHashMap<>(); |
186 | 191 |
|
187 | 192 | private final RegistryChangedRunnableListener<Item> resetLastModifiedItemChangeListener = new RegistryChangedRunnableListener<>(
|
188 | 193 | () -> lastModified = null);
|
@@ -224,6 +229,15 @@ void deactivate() {
|
224 | 229 | this.metadataRegistry.removeRegistryChangeListener(resetLastModifiedMetadataChangeListener);
|
225 | 230 | }
|
226 | 231 |
|
| 232 | + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE) |
| 233 | + protected void addItemSyntaxGenerator(ItemSyntaxGenerator itemSyntaxGenerator) { |
| 234 | + itemSyntaxGenerators.put(itemSyntaxGenerator.getFormat(), itemSyntaxGenerator); |
| 235 | + } |
| 236 | + |
| 237 | + protected void removeItemSyntaxGenerator(ItemSyntaxGenerator itemSyntaxGenerator) { |
| 238 | + itemSyntaxGenerators.remove(itemSyntaxGenerator.getFormat()); |
| 239 | + } |
| 240 | + |
227 | 241 | private UriBuilder uriBuilder(final UriInfo uriInfo, final HttpHeaders httpHeaders) {
|
228 | 242 | final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder().path(PATH_ITEMS).path("{itemName}");
|
229 | 243 | respectForwarded(uriBuilder, httpHeaders);
|
@@ -901,6 +915,53 @@ public Response getSemanticItem(final @Context UriInfo uriInfo, final @Context H
|
901 | 915 | return JSONResponse.createResponse(Status.OK, dto, null);
|
902 | 916 | }
|
903 | 917 |
|
| 918 | + @GET |
| 919 | + @RolesAllowed({ Role.ADMIN }) |
| 920 | + @Path("/filesyntax") |
| 921 | + @Produces(MediaType.TEXT_PLAIN) |
| 922 | + @Operation(operationId = "generateSyntaxForAllItems", summary = "Generate file syntax for all items.", security = { |
| 923 | + @SecurityRequirement(name = "oauth2", scopes = { "admin" }) }, responses = { |
| 924 | + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))), |
| 925 | + @ApiResponse(responseCode = "400", description = "Unsupported syntax format.") }) |
| 926 | + public Response generateSyntaxForAllItems( |
| 927 | + @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language, |
| 928 | + @DefaultValue("DSL") @QueryParam("format") @Parameter(description = "syntax format") String format) { |
| 929 | + ItemSyntaxGenerator generator = itemSyntaxGenerators.get(format); |
| 930 | + if (generator == null) { |
| 931 | + String message = "No syntax available for format " + format + "!"; |
| 932 | + return Response.status(Response.Status.BAD_REQUEST).entity(message).build(); |
| 933 | + } |
| 934 | + return Response.ok(generator.generateSyntax(sortItems(itemRegistry.getAll()))).build(); |
| 935 | + } |
| 936 | + |
| 937 | + @GET |
| 938 | + @RolesAllowed({ Role.ADMIN }) |
| 939 | + @Path("/{itemname: [a-zA-Z_0-9]+}/filesyntax") |
| 940 | + @Produces(MediaType.TEXT_PLAIN) |
| 941 | + @Operation(operationId = "generateSyntaxForItem", summary = "Generate file syntax for an item.", security = { |
| 942 | + @SecurityRequirement(name = "oauth2", scopes = { "admin" }) }, responses = { |
| 943 | + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = String.class))), |
| 944 | + @ApiResponse(responseCode = "400", description = "Unsupported syntax format."), |
| 945 | + @ApiResponse(responseCode = "404", description = "Item not found.") }) |
| 946 | + public Response generateSyntaxForItem( |
| 947 | + @HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language, |
| 948 | + @PathParam("itemname") @Parameter(description = "item name") String itemname, |
| 949 | + @DefaultValue("DSL") @QueryParam("format") @Parameter(description = "syntax format") String format) { |
| 950 | + ItemSyntaxGenerator generator = itemSyntaxGenerators.get(format); |
| 951 | + if (generator == null) { |
| 952 | + String message = "No syntax available for format " + format + "!"; |
| 953 | + return Response.status(Response.Status.BAD_REQUEST).entity(message).build(); |
| 954 | + } |
| 955 | + |
| 956 | + Item item = getItem(itemname); |
| 957 | + if (item == null) { |
| 958 | + String message = "Item " + itemname + " does not exist!"; |
| 959 | + return Response.status(Response.Status.NOT_FOUND).entity(message).build(); |
| 960 | + } |
| 961 | + |
| 962 | + return Response.ok(generator.generateSyntax(List.of(item))).build(); |
| 963 | + } |
| 964 | + |
904 | 965 | private JsonObject buildStatusObject(String itemName, String status, @Nullable String message) {
|
905 | 966 | JsonObject jo = new JsonObject();
|
906 | 967 | jo.addProperty("name", itemName);
|
@@ -1006,4 +1067,48 @@ private void addMetadata(EnrichedItemDTO dto, Set<String> namespaces, @Nullable
|
1006 | 1067 | private boolean isEditable(String itemName) {
|
1007 | 1068 | return managedItemProvider.get(itemName) != null;
|
1008 | 1069 | }
|
| 1070 | + |
| 1071 | + private List<Item> sortItems(Collection<Item> items) { |
| 1072 | + return items.stream().sorted((item1, item2) -> { |
| 1073 | + if (item1.getName().equals(item2.getName())) { |
| 1074 | + return 0; |
| 1075 | + } else if (isAncestorGroupOf(item1, item2)) { |
| 1076 | + return -1; |
| 1077 | + } else if (isAncestorGroupOf(item2, item1)) { |
| 1078 | + return 1; |
| 1079 | + } else if (item1 instanceof GroupItem && !(item2 instanceof GroupItem)) { |
| 1080 | + return -1; |
| 1081 | + } else if (item2 instanceof GroupItem && !(item1 instanceof GroupItem)) { |
| 1082 | + return 1; |
| 1083 | + } else { |
| 1084 | + return item1.getName().compareTo(item2.getName()); |
| 1085 | + } |
| 1086 | + }).collect(Collectors.toList()); |
| 1087 | + } |
| 1088 | + |
| 1089 | + private boolean isAncestorGroupOf(Item item1, Item item2) { |
| 1090 | + if (!item1.getName().equals(item2.getName()) && item1 instanceof GroupItem group) { |
| 1091 | + if (item2 instanceof GroupItem) { |
| 1092 | + List<Item> items = new ArrayList<>(); |
| 1093 | + fillGroupTree(items, group); |
| 1094 | + return items.contains(item2); |
| 1095 | + } else { |
| 1096 | + return group.getAllMembers().contains(item2); |
| 1097 | + } |
| 1098 | + } |
| 1099 | + return false; |
| 1100 | + } |
| 1101 | + |
| 1102 | + private void fillGroupTree(List<Item> items, Item item) { |
| 1103 | + if (!items.contains(item)) { |
| 1104 | + items.add(item); |
| 1105 | + if (item instanceof GroupItem group) { |
| 1106 | + for (Item member : group.getMembers()) { |
| 1107 | + if (member instanceof GroupItem groupMember) { |
| 1108 | + fillGroupTree(items, groupMember); |
| 1109 | + } |
| 1110 | + } |
| 1111 | + } |
| 1112 | + } |
| 1113 | + } |
1009 | 1114 | }
|
0 commit comments