Example #1
0
    def _init_attr_value_utcoffset(self, utcoffset):
        # Value
        self.mValue = UTCOffsetValue()
        self.mValue.setValue(utcoffset.getValue())

        # Parameters
        self.setupValueParameter()
Example #2
0
    def createValue(self, data):
        # Tidy first
        self.mValue = None

        # Get value type from property name
        value_type = self.determineValueType()

        # Check for multivalued
        if self.mName.upper() in self.sMultiValues:
            self.mValue = MultiValue(value_type)
        else:
            # Create the type
            self.mValue = Value.createFromType(value_type)

        # Now parse the data
        try:
            self.mValue.parse(data, self.sVariant)
        except ValueError:
            raise InvalidProperty("Invalid property value", data)

        self._postCreateValue(value_type)
Example #3
0
    def setValue(self, value):
        # Tidy first
        self.mValue = None

        # Get value type from property name
        value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_TEXT)

        # Check whether custom value is set
        if self.sValue in self.mParameters:
            value_type = self.sValueTypeMap.get(self.getParameterValue(self.sValue), value_type)

        # Check for multivalued
        if self.mName.upper() in self.sMultiValues:
            self.mValue = MultiValue(value_type)
        else:
            # Create the type
            self.mValue = Value.createFromType(value_type)

        self.mValue.setValue(value)

        # Special post-create for some types
        self._postCreateValue(value_type)
Example #4
0
    def _init_attr_value_datetime(self, dt):
        # Value
        self.mValue = DateTimeValue(value=dt)

        # Parameters
        self.setupValueParameter()
Example #5
0
    def _init_attr_value_int(self, ival):
        # Value
        self.mValue = IntegerValue(value=ival)

        # Parameters
        self.setupValueParameter()
Example #6
0
class PropertyBase(object):

    # Mappings between various tokens and internal definitions

    # Map a property name to its default value type
    sDefaultValueTypeMap = {}

    # Properties whose value parameter is always written out
    # even if equal to the default
    sAlwaysValueTypes = set()

    # Map a value name to a value type constant
    sValueTypeMap = {}

    # Map a value type constant to a value name
    sTypeValueMap = {}

    # Properties that are multi-valued
    sMultiValues = set()

    # Properties whose value needs special handling
    sSpecialVariants = {}

    sUsesGroup = False

    sVariant = "none"   # Used to differentiate different forms of text parsing

    sValue = None
    sText = None

    @classmethod
    def registerDefaultValue(cls, propname, valuetype, always_write_value=False):
        """
        Allow custom properties to be used by defining their name/value mappings.

        @param propname: name of the new property
        @type propname: L{str}
        @param valuetype: the L{Value} type used as the default value
        @type valuetype: L{int}
        @param always_write_value: if L{True} always write a VALUE= parameter even
            when the default value is used
        @type always_write_value: L{bool}
        """
        if propname not in cls.sDefaultValueTypeMap:
            cls.sDefaultValueTypeMap[propname] = valuetype
        if always_write_value:
            cls.sAlwaysValueTypes.add(propname)


    def __init__(self, name=None, value=None, valuetype=None):

        self.mName = name if name is not None else ""
        self.mParameters = {}
        self.mValue = None


    def duplicate(self):
        raise NotImplementedError


    def __hash__(self):
        raise NotImplementedError


    def __ne__(self, other):
        return not self.__eq__(other)


    def __eq__(self, other):
        raise NotImplementedError


    def __repr__(self):
        return "Property: %s" % (self.getText(),)


    def __str__(self):
        return self.getText()


    def getGroup(self):
        return self.mGroup if self.sUsesGroup else None


    def setGroup(self, group):
        if self.sUsesGroup:
            self.mGroup = group


    def getName(self):
        return self.mName


    def setName(self, name):
        self.mName = name


    def getParameters(self):
        return self.mParameters


    def setParameters(self, parameters):
        self.mParameters = dict([(k.upper(), v) for k, v in parameters.iteritems()])


    def hasParameter(self, attr):
        return attr.upper() in self.mParameters


    def getParameterValue(self, attr):
        return self.mParameters[attr.upper()][0].getFirstValue()


    def getParameterValues(self, attr):
        return self.mParameters[attr.upper()][0].getValues()


    def addParameter(self, attr):
        self.mParameters.setdefault(attr.getName().upper(), []).append(attr)


    def replaceParameter(self, attr):
        self.mParameters[attr.getName().upper()] = [attr]


    def removeParameters(self, attr):
        if attr.upper() in self.mParameters:
            del self.mParameters[attr.upper()]


    def getValue(self):
        return self.mValue


    def getBinaryValue(self):

        if isinstance(self.mValue, BinaryValue):
            return self.mValue
        else:
            return None


    def getCalAddressValue(self):

        if isinstance(self.mValue, CalAddressValue):
            return self.mValue
        else:
            return None


    def getDateTimeValue(self):

        if isinstance(self.mValue, DateTimeValue):
            return self.mValue
        else:
            return None


    def getDurationValue(self):

        if isinstance(self.mValue, DurationValue):
            return self.mValue
        else:
            return None


    def getIntegerValue(self):

        if isinstance(self.mValue, IntegerValue):
            return self.mValue
        else:
            return None


    def getMultiValue(self):

        if isinstance(self.mValue, MultiValue):
            return self.mValue
        else:
            return None


    def getPeriodValue(self):

        if isinstance(self.mValue, PeriodValue):
            return self.mValue
        else:
            return None


    def getTextValue(self):

        if isinstance(self.mValue, PlainTextValue):
            return self.mValue
        else:
            return None


    def getURIValue(self):

        if isinstance(self.mValue, URIValue):
            return self.mValue
        else:
            return None


    def getUTCOffsetValue(self):

        if isinstance(self.mValue, UTCOffsetValue):
            return self.mValue
        else:
            return None


    @classmethod
    def parseText(cls, data):
        """
        Parse the text format data and return a L{Property}

        @param data: text data
        @type data: C{str}
        """

        try:
            prop = cls()

            # Look for parameter or value delimiter
            prop_name, txt = stringutils.strduptokenstr(data, ";:")
            if not prop_name:
                raise InvalidProperty("Invalid property", data)

            # Get the name
            if prop.sUsesGroup:
                # Check for group prefix
                splits = prop_name.split(".", 1)
                if len(splits) == 2:
                    # We have both group and name
                    prop.mGroup = splits[0]
                    prop.mName = splits[1]
                else:
                    # We have the name
                    prop.mName = prop_name
            else:
                prop.mName = prop_name

            # Get the parameters
            txt = prop.parseTextParameters(txt, data)

            # Tidy first
            prop.mValue = None

            # Get value type from property name
            value_type = prop.determineValueType()

            # Check for multivalued
            if prop.mName.upper() in prop.sMultiValues:
                prop.mValue = MultiValue(value_type)
            else:
                # Create the type
                prop.mValue = Value.createFromType(value_type)

            # Now parse the data
            prop.mValue.parse(txt, prop.sVariant)

            prop._postCreateValue(value_type)

            return prop

        except Exception:
            raise InvalidProperty("Invalid property", data)


    def parseTextParameters(self, txt, data):
        """
        Parse parameters, return string point at value.
        """

        try:
            while txt:
                if txt[0] == ';':
                    # Parse parameter

                    # Move past delimiter
                    txt = txt[1:]

                    # Get quoted string or token
                    parameter_name, txt = stringutils.strduptokenstr(txt, "=")
                    if parameter_name is None:
                        raise InvalidProperty("Invalid property", data)
                    txt = txt[1:]
                    parameter_value, txt = stringutils.strduptokenstr(txt, ":;,")
                    if parameter_value is None:
                        raise InvalidProperty("Invalid property", data)

                    # Now add parameter value (decode ^-escaping)
                    attrvalue = Parameter(name=parameter_name, value=decodeParameterValue(parameter_value))
                    self.mParameters.setdefault(parameter_name.upper(), []).append(attrvalue)

                    # Look for additional values
                    while txt[0] == ',':
                        txt = txt[1:]
                        parameter_value2, txt = stringutils.strduptokenstr(txt, ":;,")
                        if parameter_value2 is None:
                            raise InvalidProperty("Invalid property", data)
                        attrvalue.addValue(decodeParameterValue(parameter_value2))
                elif txt[0] == ':':
                    return txt[1:]
                else:
                    # We should never get here but if we do we need to terminate the loop
                    raise InvalidProperty("Invalid property", data)

        except IndexError:
            raise InvalidProperty("Invalid property", data)


    def getText(self):
        os = StringIO.StringIO()
        self.generate(os)
        return os.getvalue()


    def generate(self, os):

        # Write it out always with value
        self.generateValue(os, False)


    def generateFiltered(self, os, filter):

        # Check for property in filter and whether value is written out
        test, novalue = filter.testPropertyValue(self.mName.upper())
        if test:
            self.generateValue(os, novalue)


    # Write out the actual property, possibly skipping the value
    def generateValue(self, os, novalue):

        self.setupValueParameter()

        # Must write to temp buffer and then wrap
        sout = StringIO.StringIO()
        if self.sUsesGroup and self.mGroup:
            sout.write(self.mGroup + ".")
        sout.write(self.mName)

        # Write all parameters
        for key in sorted(self.mParameters.keys()):
            for attr in self.mParameters[key]:
                sout.write(";")
                attr.generate(sout)

        # Write value
        sout.write(":")
        if self.mValue and not novalue:
            self.mValue.generate(sout)

        # Get string text
        temp = sout.getvalue()
        sout.close()

        # Look for line length exceed
        if len(temp) < 75:
            os.write(temp)
        else:
            # Look for valid utf8 range and write that out
            start = 0
            written = 0
            lineWrap = 74
            while written < len(temp):
                # Start 74 chars on from where we are
                offset = start + lineWrap
                if offset >= len(temp):
                    line = temp[start:]
                    os.write(line)
                    written = len(temp)
                else:
                    # Check whether next char is valid utf8 lead byte
                    while (temp[offset] > 0x7F) and ((ord(temp[offset]) & 0xC0) == 0x80):
                        # Step back until we have a valid char
                        offset -= 1

                    line = temp[start:offset]
                    os.write(line)
                    os.write("\r\n ")
                    lineWrap = 73   # We are now adding a space at the start
                    written += offset - start
                    start = offset

        os.write("\r\n")


    def writeXML(self, node, namespace):

        # Write it out always with value
        self.generateValueXML(node, namespace, False)


    def writeXMLFiltered(self, node, namespace, filter):

        # Check for property in filter and whether value is written out
        test, novalue = filter.testPropertyValue(self.mName.upper())
        if test:
            self.generateValueXML(node, namespace, novalue)


    # Write out the actual property, possibly skipping the value
    def generateValueXML(self, node, namespace, novalue):

        prop = XML.SubElement(node, xmlutils.makeTag(namespace, self.getName()))

        # Write all parameters
        if len(self.mParameters):
            params = XML.SubElement(prop, xmlutils.makeTag(namespace, xmldefinitions.parameters))
            for key in sorted(self.mParameters.keys()):
                for attr in self.mParameters[key]:
                    if attr.getName().lower() != "value":
                        attr.writeXML(params, namespace)

        # Write value
        if self.mValue and not novalue:
            self.mValue.writeXML(prop, namespace)


    @classmethod
    def parseJSON(cls, jobject):
        """
        Parse a JSON property of the form:

        [name, attrs, type, value1, value2, ...]

        @param jobject: a JSON array
        @type jobject: C{list}
        """

        try:
            prop = cls()

            # Get the name
            prop.mName = jobject[0].encode("utf-8").upper()

            # Get the parameters
            if jobject[1]:
                for name, value in jobject[1].items():
                    # Now add parameter value
                    name = name.upper()
                    attrvalue = Parameter(name=name.encode("utf-8"), value=value.encode("utf-8"))
                    prop.mParameters.setdefault(name, []).append(attrvalue)

            # Get default value type from property name and insert a VALUE parameter if current value type is not default
            value_type = cls.sValueTypeMap.get(jobject[2].upper(), Value.VALUETYPE_UNKNOWN)
            default_type = cls.sDefaultValueTypeMap.get(prop.mName.upper(), Value.VALUETYPE_UNKNOWN)
            if default_type != value_type:
                attrvalue = Parameter(name=cls.sValue, value=jobject[2].encode("utf-8").upper())
                prop.mParameters.setdefault(cls.sValue, []).append(attrvalue)

            # Get value type from property name
            value_type = prop.determineValueType()

            # Check for multivalued
            values = jobject[3:]
            if prop.mName.upper() in cls.sMultiValues:
                prop.mValue = MultiValue(value_type)
                prop.mValue.parseJSONValue(values)
            else:
                # Create the type
                prop.mValue = Value.createFromType(value_type)
                prop.mValue.parseJSONValue(values[0])

            # Special post-create for some types
            prop._postCreateValue(value_type)

            return prop

        except Exception:
            raise InvalidProperty("Invalid property", jobject)


    def writeJSON(self, jobject):

        # Write it out always with value
        self.generateValueJSON(jobject, False)


    def writeJSONFiltered(self, jobject, filter):

        # Check for property in filter and whether value is written out
        test, novalue = filter.testPropertyValue(self.mName.upper())
        if test:
            self.generateValueJSON(jobject, novalue)


    def generateValueJSON(self, jobject, novalue):

        prop = [
            self.getName().lower(),
            {},
        ]
        jobject.append(prop)

        # Write all parameters
        for key in sorted(self.mParameters.keys()):
            for attr in self.mParameters[key]:
                if attr.getName().lower() != "value":
                    attr.writeJSON(prop[1])

        # Write value
        if self.mValue and not novalue:
            self.mValue.writeJSON(prop)


    def determineValueType(self):
        # Get value type from property name
        value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN)

        # Check whether custom value is set
        if self.sValue in self.mParameters:
            attr = self.getParameterValue(self.sValue)
            value_type = self.sValueTypeMap.get(attr, value_type)

        # Check for specials
        if self.mName.upper() in self.sSpecialVariants:
            # Make sure we have the default value for the special
            if value_type == self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN):
                value_type = self.sSpecialVariants[self.mName.upper()]

        return value_type


    def createValue(self, data):
        # Tidy first
        self.mValue = None

        # Get value type from property name
        value_type = self.determineValueType()

        # Check for multivalued
        if self.mName.upper() in self.sMultiValues:
            self.mValue = MultiValue(value_type)
        else:
            # Create the type
            self.mValue = Value.createFromType(value_type)

        # Now parse the data
        try:
            self.mValue.parse(data, self.sVariant)
        except ValueError:
            raise InvalidProperty("Invalid property value", data)

        self._postCreateValue(value_type)


    def _postCreateValue(self, value_type):
        """
        Do some extra work after creating a value in this property.

        @param value_type: the iCalendar VALUE type for this property
        @type value_type: C{str}
        """
        pass


    def setValue(self, value):
        # Tidy first
        self.mValue = None

        # Get value type from property name
        value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_TEXT)

        # Check whether custom value is set
        if self.sValue in self.mParameters:
            value_type = self.sValueTypeMap.get(self.getParameterValue(self.sValue), value_type)

        # Check for multivalued
        if self.mName.upper() in self.sMultiValues:
            self.mValue = MultiValue(value_type)
        else:
            # Create the type
            self.mValue = Value.createFromType(value_type)

        self.mValue.setValue(value)

        # Special post-create for some types
        self._postCreateValue(value_type)


    def setupValueParameter(self):
        if self.sValue in self.mParameters:
            del self.mParameters[self.sValue]

        # Only if we have a value right now
        if self.mValue is None:
            return

        # See if current type is default for this property. If there is no mapping available,
        # then always add VALUE if it is not TEXT. Always add the value if listed in the
        # C{sAlwaysValueTypes} attribute.
        default_type = self.sDefaultValueTypeMap.get(self.mName.upper())
        if self.mName.upper() in self.sSpecialVariants:
            actual_type = default_type
        else:
            actual_type = self.mValue.getType()
        if default_type is None or default_type != actual_type or self.mName.upper() in self.sAlwaysValueTypes:
            actual_value = self.sTypeValueMap.get(actual_type)
            if actual_value is not None and (default_type is not None or actual_type != Value.VALUETYPE_TEXT):
                self.mParameters.setdefault(self.sValue, []).append(Parameter(name=self.sValue, value=actual_value))


    # Creation
    def _init_attr_value_int(self, ival):
        # Value
        self.mValue = IntegerValue(value=ival)

        # Parameters
        self.setupValueParameter()


    def _init_attr_value_text(self, txt, value_type):
        # Value
        self.mValue = Value.createFromType(value_type)
        if isinstance(self.mValue, PlainTextValue) or isinstance(self.mValue, UnknownValue):
            self.mValue.setValue(txt)

        # Parameters
        self.setupValueParameter()


    def _init_attr_value_datetime(self, dt):
        # Value
        self.mValue = DateTimeValue(value=dt)

        # Parameters
        self.setupValueParameter()


    def _init_attr_value_utcoffset(self, utcoffset):
        # Value
        self.mValue = UTCOffsetValue()
        self.mValue.setValue(utcoffset.getValue())

        # Parameters
        self.setupValueParameter()
Example #7
0
    def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto,
                  instanceCount):
        """
        Generate a VTIMEZONE sub-component for this Rule.

        @param vtz: VTIMEZONE to add to
        @type vtz: L{VTimezone}
        @param zonerule: the Zone rule line being used
        @type zonerule: L{ZoneRule}
        @param start: the start time for the first instance
        @type start: L{DateTime}
        @param end: the start time for the last instance
        @type end: L{DateTime}
        @param offsetfrom: the UTC offset-from
        @type offsetfrom: C{int}
        @param offsetto: the UTC offset-to
        @type offsetto: C{int}
        @param instanceCount: the number of instances in the set
        @type instanceCount: C{int}
        """

        # Determine type of component based on offset
        dstoffset = self.getOffset()
        if dstoffset == 0:
            comp = Standard(parent=vtz)
        else:
            comp = Daylight(parent=vtz)

        # Do offsets
        tzoffsetfrom = UTCOffsetValue(offsetfrom)
        tzoffsetto = UTCOffsetValue(offsetto)

        comp.addProperty(
            Property(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
        comp.addProperty(
            Property(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))

        # Do TZNAME
        if zonerule.format.find("%") != -1:
            tzname = zonerule.format % (self.letter
                                        if self.letter != "-" else "", )
        else:
            tzname = zonerule.format
        comp.addProperty(Property(definitions.cICalProperty_TZNAME, tzname))

        # Do DTSTART
        comp.addProperty(Property(definitions.cICalProperty_DTSTART, start))

        # Now determine the recurrences (use RDATE if only one year or
        # number of instances is one)
        if self.toYear != "only" and instanceCount != 1:
            rrule = Recurrence()
            rrule.setFreq(definitions.eRecurrence_YEARLY)
            rrule.setByMonth((Rule.MONTH_NAME_TO_POS[self.inMonth], ))
            if self.onDay in Rule.LASTDAY_NAME_TO_RDAY:

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.LASTDAY_NAME_TO_DAY[self.onDay]

                if dayOfWeek == indicatedDay:
                    rrule.setByDay(
                        ((-1, Rule.LASTDAY_NAME_TO_RDAY[self.onDay]), ))
                elif dayOfWeek < indicatedDay or dayOfWeek == 6 and indicatedDay == 0:
                    # This is OK as we have moved back a day and thus no month transition
                    # could have occurred
                    fakeOffset = daysInMonth(start.getMonth(),
                                             start.getYear()) - 6
                    offset, rday, bymday = self.getOnDayDetails(
                        start, indicatedDay, fakeOffset)
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday), ))
                else:
                    # This is bad news as we have moved forward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365,
                        334,
                        306,
                        275,
                        245,
                        214,
                        184,
                        153,
                        122,
                        92,
                        61,
                        31,
                        0  # Does not account for leap year
                    )
                    rrule.setByYearDay([
                        -(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[
                            self.inMonth]] + i) for i in range(7)
                    ])
                    rrule.setByDay(
                        ((0, divmod(Rule.LASTDAY_NAME_TO_DAY[self.onDay] + 1,
                                    7)[1]), ), )

            elif self.onDay.find(">=") != -1:
                indicatedDay, dayoffset = self.onDay.split(">=")

                # Need to check whether day has changed due to time shifting
                dayOfWeek = start.getDayOfWeek()
                indicatedDay = Rule.DAY_NAME_TO_DAY[indicatedDay]

                if dayOfWeek == indicatedDay:
                    offset, rday, bymday = self.getOnDayDetails(
                        start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday), ))
                elif dayoffset == 1 and divmod(dayoffset - indicatedDay,
                                               7)[1] == 6:
                    # This is bad news as we have moved backward a day possibly into the next month
                    # What we do is switch to using a BYYEARDAY rule with offset from the end of the year
                    rrule.setByMonth(())
                    daysBackStartOfMonth = (
                        365,
                        334,
                        306,
                        275,
                        245,
                        214,
                        184,
                        153,
                        122,
                        92,
                        61,
                        31,
                        0  # Does not account for leap year
                    )
                    rrule.setByYearDay([
                        -(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[
                            self.inMonth]] + i) for i in range(7)
                    ])
                    rrule.setByDay(((0, divmod(indicatedDay + 1, 7)[1]), ), )
                else:
                    # This is OK as we have moved forward a day and thus no month transition
                    # could have occurred
                    offset, rday, bymday = self.getOnDayDetails(
                        start, indicatedDay, int(dayoffset))
                    if bymday:
                        rrule.setByMonthDay(bymday)
                    rrule.setByDay(((offset, rday), ))
            else:
                try:
                    _ignore_day = int(self.onDay)
                except:
                    assert False, "onDay value is not recognized: %s" % (
                        self.onDay, )

            # Add any UNTIL
            if zonerule.getUntilDate().dt.getYear() < 9999 or self.endYear(
            ) < 9999:
                until = end.duplicate()
                until.offsetSeconds(-offsetfrom)
                until.setTimezoneUTC(True)
                rrule.setUseUntil(True)
                rrule.setUntil(until)

            comp.addProperty(Property(definitions.cICalProperty_RRULE, rrule))
        else:
            comp.addProperty(Property(definitions.cICalProperty_RDATE, start))

        comp.finalise()
        vtz.addComponent(comp)