diff options
Diffstat (limited to 'vobject/behavior.py')
-rw-r--r-- | vobject/behavior.py | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/vobject/behavior.py b/vobject/behavior.py new file mode 100644 index 0000000..226c0cc --- /dev/null +++ b/vobject/behavior.py @@ -0,0 +1,164 @@ +"""Behavior (validation, encoding, and transformations) for vobjects.""" + +import base + +#------------------------ Abstract class for behavior -------------------------- +class Behavior(object): + """Abstract class to describe vobject options, requirements and encodings. + + Behaviors are used for root components like VCALENDAR, for subcomponents + like VEVENT, and for individual lines in components. + + Behavior subclasses are not meant to be instantiated, all methods should + be classmethods. + + @cvar name: + The uppercase name of the object described by the class, or a generic + name if the class defines behavior for many objects. + @cvar description: + A brief excerpt from the RFC explaining the function of the component or + line. + @cvar versionString: + The string associated with the component, for instance, 2.0 if there's a + line like VERSION:2.0, an empty string otherwise. + @cvar knownChildren: + A dictionary with uppercased component/property names as keys and a + tuple (min, max, id) as value, where id is the id used by + L{registerBehavior}, min and max are the limits on how many of this child + must occur. None is used to denote no max or no id. + @cvar quotedPrintable: + A boolean describing whether the object should be encoded and decoded + using quoted printable line folding and character escaping. + @cvar defaultBehavior: + Behavior to apply to ContentLine children when no behavior is found. + @cvar hasNative: + A boolean describing whether the object can be transformed into a more + Pythonic object. + @cvar isComponent: + A boolean, True if the object should be a Component. + @cvar sortFirst: + The lower-case list of children which should come first when sorting. + @cvar allowGroup: + Whether or not vCard style group prefixes are allowed. + """ + name='' + description='' + versionString='' + knownChildren = {} + quotedPrintable = False + defaultBehavior = None + hasNative= False + isComponent = False + allowGroup = False + forceUTC = False + sortFirst = [] + + def __init__(self): + err="Behavior subclasses are not meant to be instantiated" + raise base.VObjectError(err) + + @classmethod + def validate(cls, obj, raiseException=False, complainUnrecognized=False): + """Check if the object satisfies this behavior's requirements. + + @param obj: + The L{ContentLine<base.ContentLine>} or + L{Component<base.Component>} to be validated. + @param raiseException: + If True, raise a L{base.ValidateError} on validation failure. + Otherwise return a boolean. + @param complainUnrecognized: + If True, fail to validate if an uncrecognized parameter or child is + found. Otherwise log the lack of recognition. + + """ + if not cls.allowGroup and obj.group is not None: + err = str(obj) + " has a group, but this object doesn't support groups" + raise base.VObjectError(err) + if isinstance(obj, base.ContentLine): + return cls.lineValidate(obj, raiseException, complainUnrecognized) + elif isinstance(obj, base.Component): + count = {} + for child in obj.getChildren(): + if not child.validate(raiseException, complainUnrecognized): + return False + name=child.name.upper() + count[name] = count.get(name, 0) + 1 + for key, val in cls.knownChildren.iteritems(): + if count.get(key,0) < val[0]: + if raiseException: + m = "%s components must contain at least %i %s" + raise base.ValidateError(m % (cls.name, val[0], key)) + return False + if val[1] and count.get(key,0) > val[1]: + if raiseException: + m = "%s components cannot contain more than %i %s" + raise base.ValidateError(m % (cls.name, val[1], key)) + return False + return True + else: + err = str(obj) + " is not a Component or Contentline" + raise base.VObjectError(err) + + @classmethod + def lineValidate(cls, line, raiseException, complainUnrecognized): + """Examine a line's parameters and values, return True if valid.""" + return True + + @classmethod + def decode(cls, line): + if line.encoded: line.encoded=0 + + @classmethod + def encode(cls, line): + if not line.encoded: line.encoded=1 + + @classmethod + def transformToNative(cls, obj): + """Turn a ContentLine or Component into a Python-native representation. + + If appropriate, turn dates or datetime strings into Python objects. + Components containing VTIMEZONEs turn into VtimezoneComponents. + + """ + return obj + + @classmethod + def transformFromNative(cls, obj): + """Inverse of transformToNative.""" + raise base.NativeError("No transformFromNative defined") + + @classmethod + def generateImplicitParameters(cls, obj): + """Generate any required information that don't yet exist.""" + pass + + @classmethod + def serialize(cls, obj, buf, lineLength, validate=True): + """Set implicit parameters, do encoding, return unicode string. + + If validate is True, raise VObjectError if the line doesn't validate + after implicit parameters are generated. + + Default is to call base.defaultSerialize. + + """ + + cls.generateImplicitParameters(obj) + if validate: cls.validate(obj, raiseException=True) + + if obj.isNative: + transformed = obj.transformFromNative() + undoTransform = True + else: + transformed = obj + undoTransform = False + + out = base.defaultSerialize(transformed, buf, lineLength) + if undoTransform: obj.transformToNative() + return out + + @classmethod + def valueRepr( cls, line ): + """return the representation of the given content line value""" + return line.value
\ No newline at end of file |