--- /dev/null
+# Copyright
+
+import io as _io
+import itertools as _itertools
+
+from .. import dtype as _dtype
+
+
+class Property (dict):
+ """An iCalendar property (e.g. VERSION)
+
+ As defined in :RFC:`5545`, section 3.5 (Property). Property names
+ are defined in sections 3.7 (Calendar Properties) and 3.8
+ (Component Properties). Parameters are defined in section 3.2
+ (Property Parameters), and value data types are defined in section
+ 3.3 (Property Value Data Types).
+ """
+ name = None
+ parameters = []
+ dtypes = []
+ separator = None
+
+ def __init__(self, parameters=None, value=None):
+ if not parameters:
+ parameters = {}
+ super(Property, self).__init__()
+ self.update(parameters)
+ self.value = value
+
+ def __hash__(self):
+ return id(self)
+
+ def __str__(self):
+ with _io.StringIO() as stream:
+ self.write(stream=stream, newline='\n')
+ return stream.getvalue()[:-1] # strip the trailing newline
+
+ def __repr__(self):
+ return '<{}.{} name:{} at {:#x}>'.format(
+ self.__module__, type(self).__name__, self.name, id(self))
+
+ def decode(self, value):
+ dtype = self._get_dtype()
+ return dtype.decode(property=self, value=value)
+
+ def encode(self, value):
+ dtype = self._get_dtype()
+ return dtype.encode(property=self, value=value)
+
+ def _get_dtype(self, dtype=None):
+ if not dtype:
+ if not self.dtypes:
+ raise NotImplementedError('no default types for {!r}'.format(
+ self))
+ dtype = self.get('VALUE', self.dtypes[0])
+ if dtype not in self.dtypes:
+ raise ValueError('invalid type {} for {!r}'.format(
+ dtype, self.name))
+ return _dtype.DTYPE[dtype]
+
+ def check_parameters(self):
+ for parameter in self.keys():
+ if parameter not in self.parameters:
+ raise ValueError(
+ 'invalid parameter {} for {!r}'.format(parameter, self))
+
+ def check_value(self):
+ pass
+
+ def write(self, stream, newline='\r\n', width=75):
+ name_param = self.name
+ line = '{}:{}'.format(
+ ';'.join(_itertools.chain(
+ [name_param],
+ ['{}={}'.format(key, value)
+ for key,value in sorted(self.items())])),
+ self.encode(self.value))
+ lines = []
+ if width:
+ while len(line) > width:
+ front = line[0:width]
+ line = line[width:]
+ if not lines:
+ width -= 1 # make room for the indent space
+ else:
+ front = ' {}'.format(front) # add the indent space
+ lines.append(front)
+ if lines: # indent the last line
+ line = ' {}'.format(line)
+ lines.append(line)
+ for line in lines:
+ stream.write('{}{}'.format(line, newline))