Skip to content

Commit

Permalink
libdeng2: Record allows dot notation for names when adding members
Browse files Browse the repository at this point in the history
The necessary subrecords are added if they are not present. This
behavior only affects adding -- the [] operator will not create
anything, but instead throw an exception if a member is not found.
  • Loading branch information
skyjake committed Apr 11, 2013
1 parent a2af307 commit 36a735a
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 43 deletions.
2 changes: 1 addition & 1 deletion doomsday/libdeng2/include/de/data/string.h
Expand Up @@ -170,7 +170,7 @@ class DENG2_PUBLIC String : public QString
String upper() const;

/// Extracts the base name from the string (includes extension).
String fileName() const;
String fileName(QChar dirChar = '/') const;

/// Extracts the base name from the string (does not include extension).
String fileNameWithoutExtension() const;
Expand Down
109 changes: 69 additions & 40 deletions doomsday/libdeng2/src/data/record.cpp
Expand Up @@ -95,6 +95,43 @@ DENG2_PIMPL(Record)
return 0;
}

/**
* Returns the record inside which the variable identified by path @a name
* resides. The necessary subrecords are created if they don't exist.
*
* @param name Variable name or path.
*
* @return Parent record for the variable.
*/
Record &parentRecordByPath(String const &pathOrName)
{
int pos = pathOrName.indexOf('.');
if(pos >= 0)
{
String subName = pathOrName.substr(0, pos);
String remaining = pathOrName.substr(pos + 1);
Record *rec = 0;

if(!self.hasSubrecord(subName))
{
// Create it now.
rec = &self.addRecord(subName);
}
else
{
rec = &self.subrecord(subName);
}

return rec->d->parentRecordByPath(remaining);
}
return self;
}

static String memberNameFromPath(String const &path)
{
return path.fileName('.');
}

/**
* Reconnect record values that used to reference known records. After a
* record has been deserialized, it may contain variables whose values
Expand Down Expand Up @@ -208,7 +245,7 @@ Variable &Record::add(Variable *variable)
if(variable->name().empty())
{
/// @throw UnnamedError All variables in a record must have a name.
throw UnnamedError("Record::add", "All variables in a record must have a name");
throw UnnamedError("Record::add", "All members of a record must have a name");
}
if(hasMember(variable->name()))
{
Expand All @@ -229,83 +266,75 @@ Variable *Record::remove(Variable &variable)

Variable &Record::addNumber(String const &name, Value::Number const &number)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new NumberValue(number), Variable::AllowNumber));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new NumberValue(number), Variable::AllowNumber));
}

Variable &Record::addBoolean(String const &name, bool booleanValue)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new NumberValue(booleanValue, NumberValue::Boolean),
Variable::AllowNumber));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new NumberValue(booleanValue, NumberValue::Boolean),
Variable::AllowNumber));
}

Variable &Record::addText(String const &name, Value::Text const &text)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new TextValue(text), Variable::AllowText));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new TextValue(text), Variable::AllowText));
}

Variable &Record::addTime(String const &name, Time const &time)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new TimeValue(time), Variable::AllowTime));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new TimeValue(time), Variable::AllowTime));
}

Variable &Record::addArray(String const &name, ArrayValue *array)
{
// Ownership of the array was given to us.
std::auto_ptr<ArrayValue> val(array);
if(!array) val.reset(new ArrayValue);

/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);

return add(new Variable(name, val.release(), Variable::AllowArray));
// Automatically create an empty array if one is not provided.
if(!array) array = new ArrayValue;
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
array, Variable::AllowArray));
}

Variable &Record::addDictionary(String const &name)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new DictionaryValue(), Variable::AllowDictionary));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new DictionaryValue, Variable::AllowDictionary));
}

Variable &Record::addBlock(String const &name)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new BlockValue(), Variable::AllowBlock));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new BlockValue, Variable::AllowBlock));
}

Variable &Record::addFunction(const String &name, Function *func)
{
/// @throw Variable::NameError @a name is not a valid variable name.
Variable::verifyName(name);
return add(new Variable(name, new FunctionValue(func), Variable::AllowFunction));
return d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new FunctionValue(func), Variable::AllowFunction));
}

Record &Record::add(String const &name, Record *subrecord)
{
std::auto_ptr<Record> sub(subrecord);
/// @throw Variable::NameError Subrecord names must be valid variable names.
Variable::verifyName(name);
if(name.empty())
{
/// @throw UnnamedError All subrecords in a record must have a name.
throw UnnamedError("Record::add", "All subrecords in a record must have a name");
}
add(new Variable(name, new RecordValue(sub.release(), RecordValue::OwnsRecord)));
d->parentRecordByPath(name)
.add(new Variable(Instance::memberNameFromPath(name),
new RecordValue(sub.release(), RecordValue::OwnsRecord)));
return *subrecord;
}

Record &Record::addRecord(String const &name)
{
return add(name, new Record());
return add(name, new Record);
}

Record *Record::remove(String const &name)
Expand Down
4 changes: 2 additions & 2 deletions doomsday/libdeng2/src/data/string.cpp
Expand Up @@ -337,9 +337,9 @@ String String::wideToString(std::wstring const &str)
}
*/

String String::fileName() const
String String::fileName(QChar dirChar) const
{
size_type pos = lastIndexOf('/');
size_type pos = lastIndexOf(dirChar);
if(pos >= 0)
{
return mid(pos + 1);
Expand Down

0 comments on commit 36a735a

Please sign in to comment.