#include "keyvaluetreetransform.h"
-#include <exception>
+#include <functional>
+#include <map>
+#include <memory>
#include <vector>
#include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/stringcompare.h"
#include "gromacs/utility/stringutil.h"
namespace gmx
{
}
-/********************************************************************
- * KeyValueTreeTransformRule
- */
-
namespace internal
{
-class KeyValueTreeTransformRule
-{
- public:
-
- private:
- std::function<KeyValueTreeValue(const KeyValueTreeValue &)> transform_;
-};
-
/********************************************************************
- * KeyValueTreeTransformer::Impl
+ * KeyValueTreeTransformerImpl
*/
class KeyValueTreeTransformerImpl : public IKeyValueTreeTransformRules
public:
typedef std::function<void(KeyValueTreeValueBuilder *, const KeyValueTreeValue &)>
TransformFunction;
+ typedef std::map<std::string, Rule, StringCompare> ChildRuleMap;
+
+ explicit Rule(StringCompareType keyMatchType)
+ : childRules_(keyMatchType)
+ {
+ }
+
void doTransform(KeyValueTreeBuilder *builder,
const KeyValueTreeValue &value) const;
void doChildTransforms(KeyValueTreeBuilder *builder,
auto iter = childRules_.find(key);
if (iter == childRules_.end())
{
- iter = childRules_.insert(std::make_pair(key, Rule())).first;
+ return createChildRule(key, StringCompareType::Exact);
}
return &iter->second;
}
+ Rule *createChildRule(const std::string &key,
+ StringCompareType keyMatchType)
+ {
+ auto result = childRules_.insert(std::make_pair(key, Rule(keyMatchType)));
+ GMX_RELEASE_ASSERT(result.second,
+ "Cannot specify key match type after child rules");
+ return &result.first->second;
+ }
std::vector<std::string> targetPath_;
std::string targetKey_;
TransformFunction transform_;
- std::map<std::string, Rule> childRules_;
+ ChildRuleMap childRules_;
};
virtual KeyValueTreeTransformRuleBuilder addRule()
return KeyValueTreeTransformRuleBuilder(this);
}
- Rule rootRule_;
+ Rule *getOrCreateRootRule()
+ {
+ if (rootRule_ == nullptr)
+ {
+ createRootRule(StringCompareType::Exact);
+ }
+ return rootRule_.get();
+ }
+ void createRootRule(StringCompareType keyMatchType)
+ {
+ GMX_RELEASE_ASSERT(rootRule_ == nullptr,
+ "Cannot specify key match type after child rules");
+ rootRule_.reset(new Rule(keyMatchType));
+ }
+
+ std::unique_ptr<Rule> rootRule_;
};
+/********************************************************************
+ * KeyValueTreeTransformerImpl::Rule
+ */
+
void KeyValueTreeTransformerImpl::Rule::doTransform(
KeyValueTreeBuilder *builder, const KeyValueTreeValue &value) const
{
KeyValueTreeObject KeyValueTreeTransformer::transform(const KeyValueTreeObject &tree) const
{
gmx::KeyValueTreeBuilder builder;
- impl_->rootRule_.doChildTransforms(&builder, tree);
+ impl_->rootRule_->doChildTransforms(&builder, tree);
return builder.build();
}
class KeyValueTreeTransformRuleBuilder::Data
{
public:
- typedef internal::KeyValueTreeTransformerImpl::Rule::TransformFunction
- TransformFunction;
+ typedef internal::KeyValueTreeTransformerImpl::Rule Rule;
+
+ Data() : keyMatchType_(StringCompareType::Exact) {}
void createRule(internal::KeyValueTreeTransformerImpl *impl)
{
- std::vector<std::string> from = splitDelimitedString(fromPath_.substr(1), '/');
- std::vector<std::string> to = splitDelimitedString(toPath_.substr(1), '/');
- internal::KeyValueTreeTransformerImpl::Rule *rule = &impl->rootRule_;
+ if (toPath_.empty())
+ {
+ createRuleWithKeyMatchType(impl);
+ return;
+ }
+ std::vector<std::string> from = splitDelimitedString(fromPath_.substr(1), '/');
+ std::vector<std::string> to = splitDelimitedString(toPath_.substr(1), '/');
+ Rule *rule = impl->getOrCreateRootRule();
for (const std::string &key : from)
{
rule = rule->getOrCreateChildRule(key);
rule->transform_ = transform_;
}
- std::string fromPath_;
- std::string toPath_;
- TransformFunction transform_;
+ void createRuleWithKeyMatchType(internal::KeyValueTreeTransformerImpl *impl)
+ {
+ std::vector<std::string> from = splitDelimitedString(fromPath_.substr(1), '/');
+ if (from.empty())
+ {
+ impl->createRootRule(keyMatchType_);
+ }
+ else
+ {
+ std::string lastKey = from.back();
+ from.pop_back();
+ Rule *rule = impl->getOrCreateRootRule();
+ for (const std::string &key : from)
+ {
+ rule = rule->getOrCreateChildRule(key);
+ }
+ rule->createChildRule(lastKey, keyMatchType_);
+ }
+ }
+
+ std::string fromPath_;
+ std::string toPath_;
+ Rule::TransformFunction transform_;
+ StringCompareType keyMatchType_;
};
/********************************************************************
data_->toPath_ = path;
}
+void KeyValueTreeTransformRuleBuilder::setKeyMatchType(StringCompareType keyMatchType)
+{
+ data_->keyMatchType_ = keyMatchType;
+}
+
void KeyValueTreeTransformRuleBuilder::addTransformToVariant(
std::function<Variant(const Variant &)> transform)
{
class KeyValueTreeObject;
class KeyValueTreeObjectBuilder;
+enum class StringCompareType;
+
class KeyValueTreeTransformRuleBuilder;
namespace internal
setFromPath(path);
return AfterFrom<FromType>(this);
}
+ void keyMatchType(const std::string &path, StringCompareType keyMatchType)
+ {
+ setFromPath(path);
+ setKeyMatchType(keyMatchType);
+ }
private:
void setFromPath(const std::string &path);
void setToPath(const std::string &path);
+ void setKeyMatchType(StringCompareType keyMatchType);
void addTransformToVariant(std::function<Variant(const Variant &)> transform);
void addTransformToObject(std::function<void(KeyValueTreeObjectBuilder *, const Variant &)> transform);
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares utility functionst for string comparison.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGCOMPARE_H
+#define GMX_UTILITY_STRINGCOMPARE_H
+
+#include <string>
+
+#include "gromacs/utility/cstringutil.h"
+
+namespace gmx
+{
+
+//! \cond libapi
+/*! \brief
+ * Specifies how strings should be compared in various contexts.
+ *
+ * \ingroup module_utility
+ */
+enum class StringCompareType
+{
+ //! Only exact matches are accepted.
+ Exact,
+ //! Case-insensitive comparison.
+ CaseInsensitive,
+ //! Case-insensitive comparison that also ignores '-' and '_'.
+ CaseAndDashInsensitive
+};
+//! \endcond
+
+/*! \libinternal \brief
+ * Compare object for std::string STL containers and algorithms that supports
+ * run-time decision on how to compare.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class StringCompare
+{
+ public:
+ /*! \brief
+ * Creates a comparer with the given type
+ *
+ * This is not explicit, which allows passing \ref StringCompareType
+ * directly to, e.g., `std::map` constructors.
+ */
+ StringCompare(StringCompareType type = StringCompareType::Exact)
+ : type_(type) {}
+
+ //! The comparison operation.
+ bool operator()(const std::string &a, const std::string &b) const
+ {
+ switch (type_)
+ {
+ case StringCompareType::Exact:
+ return a < b;
+ case StringCompareType::CaseInsensitive:
+ return gmx_strcasecmp(a.c_str(), b.c_str()) < 0;
+ case StringCompareType::CaseAndDashInsensitive:
+ return gmx_strcasecmp_min(a.c_str(), b.c_str()) < 0;
+ }
+ return a < b;
+ }
+
+ private:
+ StringCompareType type_;
+};
+
+} // namespace gmx
+
+#endif
#include "gromacs/utility/keyvaluetree.h"
#include "gromacs/utility/keyvaluetreebuilder.h"
#include "gromacs/utility/strconvert.h"
+#include "gromacs/utility/stringcompare.h"
#include "gromacs/utility/stringutil.h"
#include "testutils/refdata.h"
testTransform(input, transform);
}
+TEST_F(TreeValueTransformTest, SimpleTransformsCaseAndDashInsensitive)
+{
+ gmx::KeyValueTreeBuilder builder;
+ builder.rootObject().addValue<std::string>("a-x", "1");
+ builder.rootObject().addValue<std::string>("by", "2");
+ gmx::KeyValueTreeObject input = builder.build();
+
+ gmx::KeyValueTreeTransformer transform;
+ transform.rules()->addRule()
+ .keyMatchType("/", gmx::StringCompareType::CaseAndDashInsensitive);
+ transform.rules()->addRule()
+ .from<std::string>("/Ax").to<int>("/i").transformWith(&gmx::fromStdString<int>);
+ transform.rules()->addRule()
+ .from<std::string>("/B-Y").to<int>("/j").transformWith(&gmx::fromStdString<int>);
+
+ testTransform(input, transform);
+}
+
TEST_F(TreeValueTransformTest, SimpleTransformsToObject)
{
gmx::KeyValueTreeBuilder builder;
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Object Name="Input">
+ <String Name="a-x">1</String>
+ <String Name="by">2</String>
+ </Object>
+ <Object Name="Tree">
+ <Int Name="i">1</Int>
+ <Int Name="j">2</Int>
+ </Object>
+</ReferenceData>