Skip to content

Commit

Permalink
libcore|Record: Added a new utility method
Browse files Browse the repository at this point in the history
Duplicating a Record while retaining existing Variable instances.
  • Loading branch information
skyjake committed Dec 7, 2016
1 parent 7c11755 commit 3b87f14
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 13 deletions.
12 changes: 12 additions & 0 deletions doomsday/sdk/libcore/include/de/data/record.h
Expand Up @@ -127,6 +127,18 @@ class DENG2_PUBLIC Record
*/
void copyMembersFrom(Record const &other, Behavior behavior = AllMembers);

/**
* Duplicates the contents of @a from into this record. Existing variables with
* matching names are kept, with only their values changed. New variables are
* added, and missing variables are removed from this record.
*
* Recursively called on subrecords.
*
* @param from Source record.
* @param behavior Assignment behavior.
*/
void assignPreservingVariables(Record const &from, Behavior behavior = AllMembers);

/**
* Assignment operator.
* @return This record.
Expand Down
101 changes: 88 additions & 13 deletions doomsday/sdk/libcore/src/data/record.cpp
Expand Up @@ -112,28 +112,97 @@ DENG2_PIMPL(Record)
}
}

template <typename Predicate>
void copyMembersFrom(Record const &other, Predicate excluded)
void copyMembersFrom(Record const &other, std::function<bool (Variable const &)> excluded)
{
DENG2_GUARD(this); // and the other?
auto const *other_d = other.d.getConst();
DENG2_GUARD(other_d);

DENG2_FOR_EACH_CONST(Members, i, other.d->members)
DENG2_FOR_EACH_CONST(Members, i, other_d->members)
{
if (excluded(*i.value())) continue;
if (!excluded(*i.value()))
{
bool alreadyExists;
Variable *var;
{
DENG2_GUARD(this);
alreadyExists = members.contains(i.key());
var = new Variable(*i.value());
var->audienceForDeletion() += this;
members[i.key()] = var;
}

if (!alreadyExists)
{
// Notify about newly added members.
DENG2_FOR_PUBLIC_AUDIENCE2(Addition, i) i->recordMemberAdded(self(), *var);
}

bool const alreadyExists = members.contains(i.key());
/// @todo Should also notify if the value of an existing variable changes. -jk
}
}
}

Variable *var = new Variable(*i.value());
var->audienceForDeletion() += this;
members[i.key()] = var;
void assignPreservingVariables(Record const &other, std::function<bool (Variable const &)> excluded)
{
auto const *other_d = other.d.getConst();
DENG2_GUARD(other_d);

if (!alreadyExists)
// Add variables or update existing ones.
for (auto i = other_d->members.begin(); i != other_d->members.end(); ++i)
{
if (!excluded(*i.value()))
{
// Notify about newly added members.
DENG2_FOR_PUBLIC_AUDIENCE2(Addition, i) i->recordMemberAdded(self(), *var);
Variable *var = nullptr;

// Already have a variable with this name?
{
DENG2_GUARD(this);
auto found = members.constFind(i.key());
if (found != members.constEnd())
{
var = found.value();
}
}

// Change the existing value.
if (var)
{
if (isSubrecord(*i.value()) && isSubrecord(*var))
{
// Recurse to subrecords.
var->valueAsRecord().d->assignPreservingVariables
(i.value()->valueAsRecord(), excluded);
}
else
{
// Just make a copy.
var->set(i.value()->value());
}
}
else
{
// Add a new one.
DENG2_GUARD(this);
var = new Variable(*i.value());
var->audienceForDeletion() += this;
members[i.key()] = var;
}
}
}

/// @todo Should also notify if the value of an existing variable changes. -jk
// Remove variables not present in the other.
DENG2_GUARD(this);
QMutableHashIterator<String, Variable *> iter(members);
while (iter.hasNext())
{
iter.next();
if (!excluded(*iter.value()) && !other.hasMember(iter.key()))
{
Variable *var = iter.value();
iter.remove();
var->audienceForDeletion() -= this;
delete var;
}
}
}

Expand All @@ -146,6 +215,7 @@ DENG2_PIMPL(Record)
bool isSubrecord(Variable const &var) const
{
// Subrecords are owned by this record.
// Note: Non-owned Records are likely imports from other modules.
RecordValue const *value = var.value().maybeAs<RecordValue>();
return value && value->record() && value->hasOwnership();
}
Expand Down Expand Up @@ -354,6 +424,11 @@ void Record::copyMembersFrom(Record const &other, Behavior behavior)
d->copyMembersFrom(other, Impl::ExcludeByBehavior(behavior));
}

void Record::assignPreservingVariables(Record const &from, Behavior behavior)
{
d->assignPreservingVariables(from, Impl::ExcludeByBehavior(behavior));
}

Record &Record::operator = (Record const &other)
{
return assign(other);
Expand Down

0 comments on commit 3b87f14

Please sign in to comment.