GEODE-8702: Add StringPrefixPartitionResolver (#692)

 - Added StringPrefixPartitionResolver as it is included on the Java API
 - UTs were added to verify the PR functionality.
 - Documentation sorted out
diff --git a/cppcache/include/geode/StringPrefixPartitionResolver.hpp b/cppcache/include/geode/StringPrefixPartitionResolver.hpp
new file mode 100644
index 0000000..d1fb0de
--- /dev/null
+++ b/cppcache/include/geode/StringPrefixPartitionResolver.hpp
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef GEODE_STRINGPREFIXPARTITIONRESOLVER_H_
+#define GEODE_STRINGPREFIXPARTITIONRESOLVER_H_
+
+#include "PartitionResolver.hpp"
+
+namespace apache {
+namespace geode {
+namespace client {
+
+class CacheableKey;
+class EntryEvent;
+
+/**
+ * This class implements a partition resolver which routing object is
+ * the prefix of a given key.
+ * Delimiter is set by default to '|', still can be changed.
+ * @note If prefix is not found in the key an IllegalArgumentException is thrown
+ *
+ * Examples:
+ *  - Given key "key-1|timestamp", with delimiter '|', the routing object would
+ *    be "key-1"
+ *  - Given "key-1#DELIM#timestamp", with delimiter '|', then an exception is
+ *    thrown.
+ */
+class APACHE_GEODE_EXPORT StringPrefixPartitionResolver
+    : public PartitionResolver {
+ public:
+  StringPrefixPartitionResolver();
+  explicit StringPrefixPartitionResolver(std::string delimiter);
+
+  StringPrefixPartitionResolver(const StringPrefixPartitionResolver&) = delete;
+
+  ~StringPrefixPartitionResolver() override = default;
+
+  void operator=(const StringPrefixPartitionResolver&) = delete;
+
+  const std::string& getName() override;
+
+  std::shared_ptr<CacheableKey> getRoutingObject(
+      const EntryEvent& opDetails) override;
+
+ protected:
+  std::string delimiter_;
+};
+}  // namespace client
+}  // namespace geode
+}  // namespace apache
+
+#endif  // GEODE_STRINGPREFIXPARTITIONRESOLVER_H_
diff --git a/cppcache/src/StringPrefixPartitionResolver.cpp b/cppcache/src/StringPrefixPartitionResolver.cpp
new file mode 100644
index 0000000..5a34883
--- /dev/null
+++ b/cppcache/src/StringPrefixPartitionResolver.cpp
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <geode/CacheableKey.hpp>
+#include <geode/EntryEvent.hpp>
+#include <geode/StringPrefixPartitionResolver.hpp>
+
+namespace {
+const char* const DEFAULT_DELIMITER = "|";
+}
+
+namespace apache {
+namespace geode {
+namespace client {
+
+StringPrefixPartitionResolver::StringPrefixPartitionResolver()
+    : StringPrefixPartitionResolver(DEFAULT_DELIMITER) {}
+
+StringPrefixPartitionResolver::StringPrefixPartitionResolver(
+    std::string delimiter)
+    : PartitionResolver(), delimiter_(std::move(delimiter)) {}
+
+const std::string& StringPrefixPartitionResolver::getName() {
+  static std::string name = "StringPrefixPartitionResolver";
+  return name;
+}
+
+std::shared_ptr<CacheableKey> StringPrefixPartitionResolver::getRoutingObject(
+    const EntryEvent& event) {
+  auto&& key = event.getKey();
+  if (key == nullptr) {
+    return {};
+  }
+
+  auto key_str = key->toString();
+  auto pos = key_str.find(delimiter_);
+
+  if (pos == std::string::npos) {
+    throw IllegalArgumentException("The key \"" + key_str +
+                                   "\" does not contains the \"" + delimiter_ +
+                                   "\" delimiter.");
+  }
+
+  return CacheableKey::create(key_str.substr(0, pos));
+}
+
+}  // namespace client
+}  // namespace geode
+}  // namespace apache
diff --git a/cppcache/test/CMakeLists.txt b/cppcache/test/CMakeLists.txt
index 2fee415..d821e32 100644
--- a/cppcache/test/CMakeLists.txt
+++ b/cppcache/test/CMakeLists.txt
@@ -48,6 +48,7 @@
   QueueConnectionRequestTest.cpp
   RegionAttributesFactoryTest.cpp
   SerializableCreateTests.cpp
+  StringPrefixPartitionResolverTest.cpp
   StructSetTest.cpp
   TcrMessageTest.cpp
   ThreadPoolTest.cpp
diff --git a/cppcache/test/StringPrefixPartitionResolverTest.cpp b/cppcache/test/StringPrefixPartitionResolverTest.cpp
new file mode 100644
index 0000000..45ca65e
--- /dev/null
+++ b/cppcache/test/StringPrefixPartitionResolverTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <geode/CacheableKey.hpp>
+#include <geode/EntryEvent.hpp>
+#include <geode/StringPrefixPartitionResolver.hpp>
+
+using apache::geode::client::CacheableKey;
+using apache::geode::client::EntryEvent;
+using apache::geode::client::IllegalArgumentException;
+using apache::geode::client::StringPrefixPartitionResolver;
+
+TEST(StringPrefixPartitionResolverTest, testGetName) {
+  EXPECT_EQ(StringPrefixPartitionResolver{}.getName(),
+            "StringPrefixPartitionResolver");
+}
+
+TEST(StringPrefixPartitionResolverTest, testWithNullKey) {
+  StringPrefixPartitionResolver pr;
+  EntryEvent event{nullptr, nullptr, nullptr, nullptr, nullptr, false};
+
+  auto key = pr.getRoutingObject(event);
+  EXPECT_FALSE(key);
+}
+
+TEST(StringPrefixPartitionResolverTest, testWithDefaultDelimiter) {
+  StringPrefixPartitionResolver pr;
+  auto key = CacheableKey::create("prefix|suffix");
+  EntryEvent event{nullptr, key, nullptr, nullptr, nullptr, false};
+
+  key = pr.getRoutingObject(event);
+  EXPECT_TRUE(key);
+  EXPECT_EQ(key->toString(), "prefix");
+}
+
+TEST(StringPrefixPartitionResolverTest, testWithCustomDelimiter) {
+  StringPrefixPartitionResolver pr{"$#"};
+  auto key = CacheableKey::create("prefix$#suffix");
+  EntryEvent event{nullptr, key, nullptr, nullptr, nullptr, false};
+
+  key = pr.getRoutingObject(event);
+  EXPECT_TRUE(key);
+  EXPECT_EQ(key->toString(), "prefix");
+}
+
+TEST(StringPrefixPartitionResolverTest, testNoDelimiterFound) {
+  StringPrefixPartitionResolver pr;
+  auto key = CacheableKey::create("prefix-suffix");
+  EntryEvent event{nullptr, key, nullptr, nullptr, nullptr, false};
+
+  EXPECT_THROW(pr.getRoutingObject(event), IllegalArgumentException);
+}