2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.transform.bin2json.internal;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.time.Duration;
18 import java.time.LocalDateTime;
20 import org.openhab.core.util.HexUtils;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonObject;
26 import com.google.gson.JsonPrimitive;
27 import com.igormaznitsa.jbbp.JBBPParser;
28 import com.igormaznitsa.jbbp.exceptions.JBBPException;
29 import com.igormaznitsa.jbbp.model.JBBPAbstractArrayField;
30 import com.igormaznitsa.jbbp.model.JBBPAbstractField;
31 import com.igormaznitsa.jbbp.model.JBBPFieldArrayBit;
32 import com.igormaznitsa.jbbp.model.JBBPFieldArrayBoolean;
33 import com.igormaznitsa.jbbp.model.JBBPFieldArrayByte;
34 import com.igormaznitsa.jbbp.model.JBBPFieldArrayInt;
35 import com.igormaznitsa.jbbp.model.JBBPFieldArrayLong;
36 import com.igormaznitsa.jbbp.model.JBBPFieldArrayShort;
37 import com.igormaznitsa.jbbp.model.JBBPFieldArrayStruct;
38 import com.igormaznitsa.jbbp.model.JBBPFieldArrayUByte;
39 import com.igormaznitsa.jbbp.model.JBBPFieldArrayUShort;
40 import com.igormaznitsa.jbbp.model.JBBPFieldBit;
41 import com.igormaznitsa.jbbp.model.JBBPFieldBoolean;
42 import com.igormaznitsa.jbbp.model.JBBPFieldByte;
43 import com.igormaznitsa.jbbp.model.JBBPFieldInt;
44 import com.igormaznitsa.jbbp.model.JBBPFieldLong;
45 import com.igormaznitsa.jbbp.model.JBBPFieldShort;
46 import com.igormaznitsa.jbbp.model.JBBPFieldStruct;
47 import com.igormaznitsa.jbbp.model.JBBPFieldUByte;
48 import com.igormaznitsa.jbbp.model.JBBPFieldUShort;
51 * This class converts binary data to JSON format.
53 * Parser rules follows Java Binary Block Parser syntax.
57 * See details from <a href=
58 * "https://github.com/raydac/java-binary-block-parser">https://github.com/raydac/java-binary-block-parser</a>
65 * JsonObject json = new Bin2Json("byte a; byte b; ubyte c;").convert("03FAFF");
66 * json.toString() = {"a":3,"b":-6,"c":255}
69 * @author Pauli Anttila - Initial contribution
72 public class Bin2Json {
74 private final Logger logger = LoggerFactory.getLogger(Bin2Json.class);
76 private JBBPParser parser;
80 * @param parserRule Binary data parser rule
81 * @throws ConversionException
83 public Bin2Json(String parserRule) throws ConversionException {
85 parser = JBBPParser.prepare(parserRule);
86 } catch (JBBPException e) {
87 throw new ConversionException(String.format("Illegal parser rule, reason: %s", e.getMessage(), e));
92 * Convert {@link String} in hexadecimal string format to JSON object.
94 * @param hexString Data in hexadecimal string format. Example data: 03FAFF
95 * @return Gson {@link JsonObject}
96 * @throws ConversionException
98 public JsonObject convert(String hexString) throws ConversionException {
100 return convert(HexUtils.hexToBytes(hexString));
101 } catch (IllegalArgumentException e) {
102 throw new ConversionException(String.format("Illegal hexstring , reason: %s", e.getMessage(), e));
107 * Convert byte array to JSON object.
109 * @param data Data in byte array format.
110 * @return Gson {@link JsonObject}
111 * @throws ConversionException
113 public JsonObject convert(byte[] data) throws ConversionException {
115 return convert(parser.parse(data));
116 } catch (IOException e) {
117 throw new ConversionException(String.format("Unexpected error, reason: %s", e.getMessage(), e));
118 } catch (JBBPException e) {
119 throw new ConversionException(String.format("Unexpected error, reason: %s", e.getMessage(), e));
124 * Convert data from {@link InputStream} to JSON object.
127 * @return Gson {@link JsonObject}
128 * @throws ConversionException
130 public JsonObject convert(InputStream inputStream) throws ConversionException {
132 return convert(parser.parse(inputStream));
133 } catch (IOException e) {
134 throw new ConversionException(String.format("Unexpected error, reason: %s", e.getMessage(), e));
135 } catch (JBBPException e) {
136 throw new ConversionException(String.format("Unexpected error, reason: %s", e.getMessage(), e));
140 private JsonObject convert(JBBPFieldStruct data) throws ConversionException {
142 LocalDateTime start = LocalDateTime.now();
143 final JsonObject json = convertToJSon(data);
144 if (logger.isTraceEnabled()) {
145 Duration duration = Duration.between(start, LocalDateTime.now());
146 logger.trace("Conversion time={}, json={}", duration, json.toString());
149 } catch (JBBPException e) {
150 throw new ConversionException(String.format("Unexpected error, reason: %s", e.getMessage(), e));
154 private JsonObject convertToJSon(final JBBPAbstractField field) throws ConversionException {
155 return convertToJSon(null, field);
158 private JsonObject convertToJSon(final JsonObject json, final JBBPAbstractField field) throws ConversionException {
159 JsonObject jsn = json == null ? new JsonObject() : json;
161 final String fieldName = field.getFieldName() == null ? "nonamed" : field.getFieldName();
162 if (field instanceof JBBPAbstractArrayField) {
163 final JsonArray jsonArray = new JsonArray();
164 if (field instanceof JBBPFieldArrayBit) {
165 for (final byte b : ((JBBPFieldArrayBit) field).getArray()) {
166 jsonArray.add(new JsonPrimitive(b));
168 } else if (field instanceof JBBPFieldArrayBoolean) {
169 for (final boolean b : ((JBBPFieldArrayBoolean) field).getArray()) {
170 jsonArray.add(new JsonPrimitive(b));
172 } else if (field instanceof JBBPFieldArrayByte) {
173 for (final byte b : ((JBBPFieldArrayByte) field).getArray()) {
174 jsonArray.add(new JsonPrimitive(b));
176 } else if (field instanceof JBBPFieldArrayInt) {
177 for (final int b : ((JBBPFieldArrayInt) field).getArray()) {
178 jsonArray.add(new JsonPrimitive(b));
180 } else if (field instanceof JBBPFieldArrayLong) {
181 for (final long b : ((JBBPFieldArrayLong) field).getArray()) {
182 jsonArray.add(new JsonPrimitive(b));
184 } else if (field instanceof JBBPFieldArrayShort) {
185 for (final short b : ((JBBPFieldArrayShort) field).getArray()) {
186 jsonArray.add(new JsonPrimitive(b));
188 } else if (field instanceof JBBPFieldArrayStruct) {
189 final JBBPFieldArrayStruct array = (JBBPFieldArrayStruct) field;
190 for (int i = 0; i < array.size(); i++) {
191 jsonArray.add(convertToJSon(new JsonObject(), array.getElementAt(i)));
193 } else if (field instanceof JBBPFieldArrayUByte) {
194 for (final byte b : ((JBBPFieldArrayUByte) field).getArray()) {
195 jsonArray.add(new JsonPrimitive(b & 0xFF));
197 } else if (field instanceof JBBPFieldArrayUShort) {
198 for (final short b : ((JBBPFieldArrayUShort) field).getArray()) {
199 jsonArray.add(new JsonPrimitive(b & 0xFFFF));
202 throw new ConversionException(String.format("Unexpected field type '%s'", field));
204 jsn.add(fieldName, jsonArray);
206 if (field instanceof JBBPFieldBit) {
207 jsn.addProperty(fieldName, ((JBBPFieldBit) field).getAsInt());
208 } else if (field instanceof JBBPFieldBoolean) {
209 jsn.addProperty(fieldName, ((JBBPFieldBoolean) field).getAsBool());
210 } else if (field instanceof JBBPFieldByte) {
211 jsn.addProperty(fieldName, ((JBBPFieldByte) field).getAsInt());
212 } else if (field instanceof JBBPFieldInt) {
213 jsn.addProperty(fieldName, ((JBBPFieldInt) field).getAsInt());
214 } else if (field instanceof JBBPFieldLong) {
215 jsn.addProperty(fieldName, ((JBBPFieldLong) field).getAsLong());
216 } else if (field instanceof JBBPFieldShort) {
217 jsn.addProperty(fieldName, ((JBBPFieldShort) field).getAsInt());
218 } else if (field instanceof JBBPFieldStruct) {
219 final JBBPFieldStruct struct = (JBBPFieldStruct) field;
220 final JsonObject obj = new JsonObject();
221 for (final JBBPAbstractField f : struct.getArray()) {
222 convertToJSon(obj, f);
227 jsn.add(fieldName, obj);
229 } else if (field instanceof JBBPFieldUByte) {
230 jsn.addProperty(fieldName, ((JBBPFieldUByte) field).getAsInt());
231 } else if (field instanceof JBBPFieldUShort) {
232 jsn.addProperty(fieldName, ((JBBPFieldUShort) field).getAsInt());
234 throw new ConversionException(String.format("Unexpected field '%s'", field));