NetworkTable: Add key utility functions. (#256)

- BasenameKey
- NormalizeKey
- GetHierarchy
This commit is contained in:
Peter Johnson
2017-11-19 11:52:10 -08:00
committed by GitHub
parent 551504e773
commit 0e4a1c5dae
5 changed files with 293 additions and 0 deletions

View File

@@ -7,7 +7,9 @@
package edu.wpi.first.networktables;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -28,6 +30,93 @@ public final class NetworkTable {
private final String pathWithSep;
private final NetworkTableInstance inst;
/**
* Gets the "base name" of a key. For example, "/foo/bar" becomes "bar".
* If the key has a trailing slash, returns an empty string.
* @param key key
* @return base name
*/
public static String basenameKey(String key) {
final int slash = key.lastIndexOf(PATH_SEPARATOR);
if (slash == -1) {
return key;
}
return key.substring(slash + 1);
}
/**
* Normalizes an network table key to contain no consecutive slashes and
* optionally start with a leading slash. For example:
*
* <pre><code>
* normalizeKey("/foo/bar", true) == "/foo/bar"
* normalizeKey("foo/bar", true) == "/foo/bar"
* normalizeKey("/foo/bar", false) == "foo/bar"
* normalizeKey("foo//bar", false) == "foo/bar"
* </code></pre>
*
* @param key the key to normalize
* @param withLeadingSlash whether or not the normalized key should begin
* with a leading slash
* @return normalized key
*/
public static String normalizeKey(String key, boolean withLeadingSlash) {
String normalized;
if (withLeadingSlash) {
normalized = PATH_SEPARATOR + key;
} else {
normalized = key;
}
normalized = normalized.replaceAll(PATH_SEPARATOR + "{2,}", String.valueOf(PATH_SEPARATOR));
if (!withLeadingSlash && normalized.charAt(0) == PATH_SEPARATOR) {
// remove leading slash, if present
normalized = normalized.substring(1);
}
return normalized;
}
/**
* Normalizes a network table key to start with exactly one leading slash
* ("/") and contain no consecutive slashes. For example,
* {@code "//foo/bar/"} becomes {@code "/foo/bar/"} and
* {@code "///a/b/c"} becomes {@code "/a/b/c"}.
*
* <p>This is equivalent to {@code normalizeKey(key, true)}
*
* @param key the key to normalize
* @return normalized key
*/
public static String normalizeKey(String key) {
return normalizeKey(key, true);
}
/**
* Gets a list of the names of all the super tables of a given key. For
* example, the key "/foo/bar/baz" has a hierarchy of "/", "/foo",
* "/foo/bar", and "/foo/bar/baz".
* @param key the key
* @return List of super tables
*/
public static List<String> getHierarchy(String key) {
final String normal = normalizeKey(key, true);
List<String> hierarchy = new ArrayList<>();
if (normal.length() == 1) {
hierarchy.add(normal);
return hierarchy;
}
for (int i = 1; ; i = normal.indexOf(PATH_SEPARATOR, i + 1)) {
if (i == -1) {
// add the full key
hierarchy.add(normal);
break;
} else {
hierarchy.add(normal.substring(0, i));
}
}
return hierarchy;
}
/**
* Constructor. Use NetworkTableInstance.getTable() or getSubTable() instead.
*/

View File

@@ -22,6 +22,56 @@ bool NetworkTable::s_enable_ds = true;
bool NetworkTable::s_running = false;
unsigned int NetworkTable::s_port = NT_DEFAULT_PORT;
StringRef NetworkTable::BasenameKey(StringRef key) {
size_t slash = key.rfind(PATH_SEPARATOR_CHAR);
if (slash == StringRef::npos) return key;
return key.substr(slash + 1);
}
std::string NetworkTable::NormalizeKey(StringRef key, bool withLeadingSlash) {
llvm::SmallString<128> buf;
return NormalizeKey(key, buf, withLeadingSlash);
}
StringRef NetworkTable::NormalizeKey(StringRef key,
llvm::SmallVectorImpl<char>& buf,
bool withLeadingSlash) {
buf.clear();
if (withLeadingSlash) buf.push_back(PATH_SEPARATOR_CHAR);
// for each path element, add it with a slash following
llvm::SmallVector<StringRef, 16> parts;
key.split(parts, PATH_SEPARATOR_CHAR, -1, false);
for (auto i = parts.begin(); i != parts.end(); ++i) {
buf.append(i->begin(), i->end());
buf.push_back(PATH_SEPARATOR_CHAR);
}
// remove trailing slash if the input key didn't have one
if (!key.empty() && key.back() != PATH_SEPARATOR_CHAR) buf.pop_back();
return StringRef(buf.data(), buf.size());
}
std::vector<std::string> NetworkTable::GetHierarchy(StringRef key) {
std::vector<std::string> hierarchy;
hierarchy.emplace_back(1, PATH_SEPARATOR_CHAR);
// for each path element, add it to the end of what we built previously
llvm::SmallString<128> path;
llvm::SmallVector<StringRef, 16> parts;
key.split(parts, PATH_SEPARATOR_CHAR, -1, false);
if (!parts.empty()) {
for (auto i = parts.begin(); i != parts.end(); ++i) {
path += PATH_SEPARATOR_CHAR;
path += *i;
hierarchy.emplace_back(path.str());
}
// handle trailing slash
if (key.back() == PATH_SEPARATOR_CHAR) {
path += PATH_SEPARATOR_CHAR;
hierarchy.emplace_back(path.str());
}
}
return hierarchy;
}
void NetworkTable::Initialize() {
if (s_running) Shutdown();
auto inst = NetworkTableInstance::GetDefault();

View File

@@ -11,6 +11,7 @@
#include <functional>
#include <vector>
#include "llvm/ArrayRef.h"
#include "llvm/StringMap.h"
#include "networktables/NetworkTableEntry.h"
#include "networktables/TableEntryListener.h"
@@ -54,6 +55,44 @@ class NetworkTable final : public ITable {
friend class NetworkTableInstance;
public:
/**
* Gets the "base name" of a key. For example, "/foo/bar" becomes "bar".
* If the key has a trailing slash, returns an empty string.
* @param key key
* @return base name
*/
static StringRef BasenameKey(StringRef key);
/**
* Normalizes an network table key to contain no consecutive slashes and
* optionally start with a leading slash. For example:
*
* <pre><code>
* normalizeKey("/foo/bar", true) == "/foo/bar"
* normalizeKey("foo/bar", true) == "/foo/bar"
* normalizeKey("/foo/bar", false) == "foo/bar"
* normalizeKey("foo//bar", false) == "foo/bar"
* </code></pre>
*
* @param key the key to normalize
* @param withLeadingSlash whether or not the normalized key should begin
* with a leading slash
* @return normalized key
*/
static std::string NormalizeKey(StringRef key, bool withLeadingSlash = true);
static StringRef NormalizeKey(StringRef key, llvm::SmallVectorImpl<char>& buf,
bool withLeadingSlash = true);
/**
* Gets a list of the names of all the super tables of a given key. For
* example, the key "/foo/bar/baz" has a hierarchy of "/", "/foo",
* "/foo/bar", and "/foo/bar/baz".
* @param key the key
* @return List of super tables
*/
static std::vector<std::string> GetHierarchy(StringRef key);
/**
* Constructor. Use NetworkTableInstance::GetTable() or GetSubTable()
* instead.

View File

@@ -0,0 +1,67 @@
package edu.wpi.first.networktables;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
public class NetworkTableTest extends TestCase {
public void testBasenameKey() {
assertEquals("simple", NetworkTable.basenameKey("simple"));
assertEquals("simple", NetworkTable.basenameKey("one/two/many/simple"));
assertEquals("simple",
NetworkTable.basenameKey("//////an/////awful/key////simple"));
}
public void testNormalizeKeySlash() {
assertEquals("/", NetworkTable.normalizeKey("///"));
assertEquals("/no/normal/req", NetworkTable.normalizeKey("/no/normal/req"));
assertEquals("/no/leading/slash",
NetworkTable.normalizeKey("no/leading/slash"));
assertEquals(
"/what/an/awful/key/",
NetworkTable.normalizeKey("//////what////an/awful/////key///"));
}
public void testNormalizeKeyNoSlash() {
assertEquals("a", NetworkTable.normalizeKey("a", false));
assertEquals("a", NetworkTable.normalizeKey("///a", false));
assertEquals("leading/slash",
NetworkTable.normalizeKey("/leading/slash", false));
assertEquals("no/leading/slash",
NetworkTable.normalizeKey("no/leading/slash", false));
assertEquals(
"what/an/awful/key/",
NetworkTable.normalizeKey("//////what////an/awful/////key///", false));
}
public void testGetHierarchyEmpty() {
List<String> expected = new ArrayList<>();
expected.add("/");
assertEquals(expected, NetworkTable.getHierarchy(""));
}
public void testGetHierarchyRoot() {
List<String> expected = new ArrayList<>();
expected.add("/");
assertEquals(expected, NetworkTable.getHierarchy("/"));
}
public void testGetHierarchyNormal() {
List<String> expected = new ArrayList<>();
expected.add("/");
expected.add("/foo");
expected.add("/foo/bar");
expected.add("/foo/bar/baz");
assertEquals(expected, NetworkTable.getHierarchy("/foo/bar/baz"));
}
public void testGetHierarchyTrailingSlash() {
List<String> expected = new ArrayList<>();
expected.add("/");
expected.add("/foo");
expected.add("/foo/bar");
expected.add("/foo/bar/");
assertEquals(expected, NetworkTable.getHierarchy("/foo/bar/"));
}
}

View File

@@ -13,6 +13,54 @@
class NetworkTableTest : public ::testing::Test {};
TEST_F(NetworkTableTest, BasenameKey) {
EXPECT_EQ("simple", NetworkTable::BasenameKey("simple"));
EXPECT_EQ("simple", NetworkTable::BasenameKey("one/two/many/simple"));
EXPECT_EQ("simple",
NetworkTable::BasenameKey("//////an/////awful/key////simple"));
}
TEST_F(NetworkTableTest, NormalizeKeySlash) {
EXPECT_EQ("/", NetworkTable::NormalizeKey("///"));
EXPECT_EQ("/no/normal/req", NetworkTable::NormalizeKey("/no/normal/req"));
EXPECT_EQ("/no/leading/slash",
NetworkTable::NormalizeKey("no/leading/slash"));
EXPECT_EQ("/what/an/awful/key/",
NetworkTable::NormalizeKey("//////what////an/awful/////key///"));
}
TEST_F(NetworkTableTest, NormalizeKeyNoSlash) {
EXPECT_EQ("a", NetworkTable::NormalizeKey("a", false));
EXPECT_EQ("a", NetworkTable::NormalizeKey("///a", false));
EXPECT_EQ("leading/slash",
NetworkTable::NormalizeKey("/leading/slash", false));
EXPECT_EQ("no/leading/slash",
NetworkTable::NormalizeKey("no/leading/slash", false));
EXPECT_EQ(
"what/an/awful/key/",
NetworkTable::NormalizeKey("//////what////an/awful/////key///", false));
}
TEST_F(NetworkTableTest, GetHierarchyEmpty) {
std::vector<std::string> expected{"/"};
ASSERT_EQ(expected, NetworkTable::GetHierarchy(""));
}
TEST_F(NetworkTableTest, GetHierarchyRoot) {
std::vector<std::string> expected{"/"};
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/"));
}
TEST_F(NetworkTableTest, GetHierarchyNormal) {
std::vector<std::string> expected{"/", "/foo", "/foo/bar", "/foo/bar/baz"};
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/baz"));
}
TEST_F(NetworkTableTest, GetHierarchyTrailingSlash) {
std::vector<std::string> expected{"/", "/foo", "/foo/bar", "/foo/bar/"};
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/"));
}
TEST_F(NetworkTableTest, ContainsKey) {
auto inst = nt::NetworkTableInstance::Create();
auto nt = inst.GetTable("containskey");