Skip to content

Commit

Permalink
#5622: Entity key/value comparison
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed May 23, 2021
1 parent dd06f73 commit 314c891
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 2 deletions.
99 changes: 98 additions & 1 deletion radiantcore/map/algorithm/SceneGraphComparer.cpp
Expand Up @@ -2,6 +2,7 @@

#include <algorithm>
#include "icomparablenode.h"
#include "string/string.h"
#include "command/ExecutionNotPossible.h"

namespace map
Expand Down Expand Up @@ -88,6 +89,7 @@ void SceneGraphComparer::processDifferingEntities(const EntityMismatchByName& so
return left.first < right.first;
};

// Calculate intersection and two-way exclusives
std::set_intersection(sourceMismatches.begin(), sourceMismatches.end(), targetMismatches.begin(), targetMismatches.end(),
std::back_inserter(matchingByName), compareEntityNames);

Expand All @@ -101,13 +103,19 @@ void SceneGraphComparer::processDifferingEntities(const EntityMismatchByName& so
{
rMessage() << " - EntityPresentButDifferent: " << match.first << std::endl;

_result->differingEntities.emplace_back(ComparisonResult::EntityDifference
auto& entityDiff = _result->differingEntities.emplace_back(ComparisonResult::EntityDifference
{
match.second.fingerPrint,
match.second.node,
match.second.entityName,
ComparisonResult::EntityDifference::Type::EntityPresentButDifferent
});

auto sourceNode = sourceMismatches.find(match.second.entityName)->second.node;
auto targetNode = targetMismatches.find(match.second.entityName)->second.node;

// Analyse the key values
entityDiff.differingKeyValues = compareKeyValues(sourceNode, targetNode);
}

for (const auto& mismatch : missingInSource)
Expand Down Expand Up @@ -137,6 +145,95 @@ void SceneGraphComparer::processDifferingEntities(const EntityMismatchByName& so
}
}

namespace
{
using KeyValueMap = std::map<std::string, std::string, string::ILess>;

inline KeyValueMap loadKeyValues(const scene::INodePtr& entityNode)
{
KeyValueMap result;

auto entity = Node_getEntity(entityNode);

entity->forEachKeyValue([&](const std::string& key, const std::string& value)
{
result.emplace(key, value);
}, false);

return result;
}
}

std::list<ComparisonResult::KeyValueDifference> SceneGraphComparer::compareKeyValues(
const scene::INodePtr& sourceNode, const scene::INodePtr& targetNode)
{
std::list<ComparisonResult::KeyValueDifference> result;

auto sourceKeyValues = loadKeyValues(sourceNode);
auto targetKeyValues = loadKeyValues(targetNode);

string::ILess icmp;
auto compareKeysNoCase = [&](const KeyValueMap::value_type& left, const KeyValueMap::value_type& right)
{
return icmp(left.first, right.first);
};

std::vector<KeyValueMap::value_type> missingInSource;
std::vector<KeyValueMap::value_type> missingInTarget;
std::vector<KeyValueMap::value_type> presentInBoth;

std::set_intersection(sourceKeyValues.begin(), sourceKeyValues.end(),
targetKeyValues.begin(), targetKeyValues.end(), std::back_inserter(presentInBoth), compareKeysNoCase);
std::set_difference(sourceKeyValues.begin(), sourceKeyValues.end(),
targetKeyValues.begin(), targetKeyValues.end(), std::back_inserter(missingInTarget), compareKeysNoCase);
std::set_difference(targetKeyValues.begin(), targetKeyValues.end(),
sourceKeyValues.begin(), sourceKeyValues.end(), std::back_inserter(missingInSource), compareKeysNoCase);

for (const auto& pair : missingInTarget)
{
rMessage() << " - Key " << pair.first << " not present in target entity" << std::endl;

result.emplace_back(ComparisonResult::KeyValueDifference
{
pair.first,
pair.second,
ComparisonResult::KeyValueDifference::Type::KeyValueAdded
});
}

for (const auto& pair : missingInSource)
{
rMessage() << " - Key " << pair.first << " not present in source entity" << std::endl;

result.emplace_back(ComparisonResult::KeyValueDifference
{
pair.first,
pair.second,
ComparisonResult::KeyValueDifference::Type::KeyValueRemoved
});
}

// Compare each value which is present on both entities
for (const auto& pair : presentInBoth)
{
if (sourceKeyValues[pair.first] == targetKeyValues[pair.first])
{
continue;
}

rMessage() << " - Key " << pair.first << " changed in source to value " << pair.second << std::endl;

result.emplace_back(ComparisonResult::KeyValueDifference
{
pair.first,
pair.second,
ComparisonResult::KeyValueDifference::Type::KeyValueChanged
});
}

return result;
}

SceneGraphComparer::Fingerprints SceneGraphComparer::collectEntityFingerprints(const scene::INodePtr& root)
{
Fingerprints result;
Expand Down
21 changes: 20 additions & 1 deletion radiantcore/map/algorithm/SceneGraphComparer.h
Expand Up @@ -25,6 +25,21 @@ struct ComparisonResult
scene::INodePtr targetNode;
};

struct KeyValueDifference
{
std::string key;
std::string value;

enum class Type
{
KeyValueAdded, // key is present on the source entity, but not on the target
KeyValueRemoved, // key is present on the target entity, but not on the source
KeyValueChanged, // key present on both, but value is different
};

Type type;
};

struct EntityDifference
{
std::string fingerPrint;
Expand All @@ -39,6 +54,8 @@ struct ComparisonResult
};

Type type;

std::list<KeyValueDifference> differingKeyValues;
};

// The collection of entities with the same fingerprint value
Expand All @@ -57,7 +74,6 @@ class SceneGraphComparer
ComparisonResult::Ptr _result;

using Fingerprints = std::map<std::string, scene::INodePtr>;
using FingerprintsByType = std::map<scene::INode::Type, Fingerprints>;

public:
struct EntityMismatch
Expand Down Expand Up @@ -87,6 +103,9 @@ class SceneGraphComparer
void processDifferingEntities(const EntityMismatchByName& sourceMismatches, const EntityMismatchByName& targetMismatches);

Fingerprints collectEntityFingerprints(const scene::INodePtr& root);

std::list<ComparisonResult::KeyValueDifference> compareKeyValues(
const scene::INodePtr& sourceNode, const scene::INodePtr& targetNode);
};

}
Expand Down

0 comments on commit 314c891

Please sign in to comment.