def _init_attr_value_utcoffset(self, utcoffset): # Value self.mValue = UTCOffsetValue() self.mValue.setValue(utcoffset.getValue()) # Parameters self.setupValueParameter()
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 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 _init_attr_value_datetime(self, dt): # Value self.mValue = DateTimeValue(value=dt) # Parameters self.setupValueParameter()
def _init_attr_value_int(self, ival): # Value self.mValue = IntegerValue(value=ival) # Parameters self.setupValueParameter()
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()
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)