def add_attribute(self, attrkey, attrvalue, append=False, oldvalue=None):
<EOL>if attrkey == '<STR_LIT>':<EOL><INDENT>if self.children is not None:<EOL><INDENT>oldid = self.get_attribute('<STR_LIT>')<EOL>for child in self.children:<EOL><INDENT>child.add_attribute('<STR_LIT>', attrvalue,<EOL>oldvalue=oldid)<EOL><DEDENT><DEDENT>self._attrs[attrkey] = attrvalue<EOL>if self.is_multi:<EOL><INDENT>self.multi_rep._attrs[attrkey] = attrvalue<EOL>for sibling in self.multi_rep.siblings:<EOL><INDENT>sibling._attrs[attrkey] = attrvalue<EOL><DEDENT><DEDENT>return<EOL><DEDENT>if oldvalue is not None:<EOL><INDENT>if attrkey in self._attrs:<EOL><INDENT>assert oldvalue in self._attrs[attrkey]<EOL>del self._attrs[attrkey][oldvalue]<EOL><DEDENT><DEDENT>if attrkey not in self._attrs or append is False:<EOL><INDENT>self._attrs[attrkey] = dict()<EOL><DEDENT>self._attrs[attrkey][attrvalue] = True<EOL>
Add an attribute to this feature.

Feature attributes are stored as nested dictionaries.

Each feature can only have one ID, so ID attribute mapping is 'string'
to 'string'. All other attributes can have multiple values, so mapping
is 'string' to 'dict of strings'.

By default, adding an attribute that already exists will cause the old
value to be overwritten. If the `append` option is true, the new
attribute value will not overwrite the old value, but will be appended
as a second value. (Note: ID attributes can have only 1 value.)

If the `oldvalue` option is set, the new value will replace the old
value. This is necessary for updating an attribute that has multiple
values without completely overwriting all old values. (Note: The
`append` option is ignored when `oldvalue` is set.)