diff --git a/bundles/org.openhab.core/src/main/java/org/eclipse/smarthome/core/util/UIDUtils.java b/bundles/org.openhab.core/src/main/java/org/eclipse/smarthome/core/util/UIDUtils.java new file mode 100644 index 00000000000..198969cbe16 --- /dev/null +++ b/bundles/org.openhab.core/src/main/java/org/eclipse/smarthome/core/util/UIDUtils.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.core.util; + +import java.nio.charset.StandardCharsets; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utilities for UIDs. + * + * @author Markus Rathgeb - Initial contribution + */ +@NonNullByDefault +public class UIDUtils { + + /** + * Encodes a given string to an UID using only allowed characters. + * + *

+ * The generated UID can be given to the {@link #decode(String)} function and it will result into the given value. + * + * @param value the string that should be encoded to a valid UID + * @return a string that if a valid UID (with respect to the allowed characters) + */ + public static String encode(final String value) { + if (value.isEmpty()) { + return value; + } + + final byte[] in = value.getBytes(StandardCharsets.UTF_8); + final byte[] out = new byte[in.length * 3]; + + int opos = 0; + for (int ipos = 0; ipos < in.length; ++ipos, ++opos) { + final byte cur = in[ipos]; + if (cur >= '0' && cur <= '9' || cur >= 'A' && cur <= 'Z' || cur >= 'a' && cur <= 'z') { + out[opos] = cur; + } else { + out[opos++] = '_'; + final byte[] hex = HexUtils.byteToHex(cur); + out[opos++] = hex[0]; + out[opos] = hex[1]; + } + } + + return new String(out, 0, opos, StandardCharsets.UTF_8); + } + + /** + * Decodes an UID that has been generated by the {@link #encode(String)} function. + * + *

+ * This function should only be used for UIDs generated by the {@link #encode(String)} function. For every other UID + * the result is rather useless or could result into an {@link IllegalArgumentException}. + * + * @param value the UID to decode + * @return the decoded UID string + * @throws IllegalArgumentException if the given UID is not a valid encoded input + */ + public static String decode(final String value) { + if (value.isEmpty()) { + return value; + } + + final byte[] in = value.getBytes(StandardCharsets.UTF_8); + final byte[] out = new byte[in.length]; + + int opos = 0; + for (int ipos = 0; ipos < in.length; ++ipos, ++opos) { + final byte cur = in[ipos]; + if (cur >= '0' && cur <= '9' || cur >= 'A' && cur <= 'Z' || cur >= 'a' && cur <= 'z') { + out[opos] = cur; + } else if (cur == '_') { + final byte curHigh = in[++ipos]; + final byte curLow = in[++ipos]; + out[opos] = HexUtils.hexToByte(curHigh, curLow); + } else { + throw new IllegalArgumentException("Invalid input"); + } + } + + return new String(out, 0, opos, StandardCharsets.UTF_8); + } + +} diff --git a/bundles/org.openhab.core/src/test/java/org/eclipse/smarthome/core/common/UIDUtilsTest.java b/bundles/org.openhab.core/src/test/java/org/eclipse/smarthome/core/common/UIDUtilsTest.java new file mode 100644 index 00000000000..16be87e1858 --- /dev/null +++ b/bundles/org.openhab.core/src/test/java/org/eclipse/smarthome/core/common/UIDUtilsTest.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.core.common; + +import java.util.function.Consumer; + +import org.eclipse.smarthome.core.util.UIDUtils; +import org.hamcrest.core.IsEqual; +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for {@link AbstractUID}. + * + * @author Markus Rathgeb - Initial contribution + */ +public class UIDUtilsTest { + + @Test + public void encodeDecode() { + Consumer test = in -> { + final String encoded = UIDUtils.encode(in); + final String decoded = UIDUtils.decode(encoded); + System.out.printf("in: %s%n encoded: %s%n decoded: %s%n equals: %b%n", in, encoded, decoded, + in.equals(decoded)); + Assert.assertThat(decoded, IsEqual.equalTo(in)); + }; + test.accept("test"); + test.accept("TEST"); + test.accept("test123TEST"); + test.accept("test_test-test%test"); + test.accept("äöø€"); + } + +}