[CalendarServer-changes] [11609] PyCalendar/branches/json-2/src

source_changes at macosforge.org source_changes at macosforge.org
Fri Aug 16 08:54:29 PDT 2013


Revision: 11609
          http://trac.calendarserver.org//changeset/11609
Author:   cdaboo at apple.com
Date:     2013-08-16 08:54:29 -0700 (Fri, 16 Aug 2013)
Log Message:
-----------
Re-do big class re-organization prior to merging in json support.

Modified Paths:
--------------
    PyCalendar/branches/json-2/src/pycalendar/__init__.py
    PyCalendar/branches/json-2/src/pycalendar/binaryvalue.py
    PyCalendar/branches/json-2/src/pycalendar/caladdressvalue.py
    PyCalendar/branches/json-2/src/pycalendar/componentbase.py
    PyCalendar/branches/json-2/src/pycalendar/datetime.py
    PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py
    PyCalendar/branches/json-2/src/pycalendar/duration.py
    PyCalendar/branches/json-2/src/pycalendar/durationvalue.py
    PyCalendar/branches/json-2/src/pycalendar/exceptions.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_validation.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/validation.py
    PyCalendar/branches/json-2/src/pycalendar/integervalue.py
    PyCalendar/branches/json-2/src/pycalendar/manager.py
    PyCalendar/branches/json-2/src/pycalendar/multivalue.py
    PyCalendar/branches/json-2/src/pycalendar/outputfilter.py
    PyCalendar/branches/json-2/src/pycalendar/period.py
    PyCalendar/branches/json-2/src/pycalendar/periodvalue.py
    PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py
    PyCalendar/branches/json-2/src/pycalendar/property.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_datetime.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_duration.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_multivalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py
    PyCalendar/branches/json-2/src/pycalendar/textvalue.py
    PyCalendar/branches/json-2/src/pycalendar/timezone.py
    PyCalendar/branches/json-2/src/pycalendar/timezonedb.py
    PyCalendar/branches/json-2/src/pycalendar/unknownvalue.py
    PyCalendar/branches/json-2/src/pycalendar/urivalue.py
    PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py
    PyCalendar/branches/json-2/src/pycalendar/utils.py
    PyCalendar/branches/json-2/src/pycalendar/validation.py
    PyCalendar/branches/json-2/src/pycalendar/validator.py
    PyCalendar/branches/json-2/src/pycalendar/value.py
    PyCalendar/branches/json-2/src/pycalendar/valueutils.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_card.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_validation.py
    PyCalendar/branches/json-2/src/zonal/rule.py
    PyCalendar/branches/json-2/src/zonal/tests/test_rule.py
    PyCalendar/branches/json-2/src/zonal/tests/test_zone.py
    PyCalendar/branches/json-2/src/zonal/tzconvert.py
    PyCalendar/branches/json-2/src/zonal/tzdump.py
    PyCalendar/branches/json-2/src/zonal/tzverify.py
    PyCalendar/branches/json-2/src/zonal/zone.py

Added Paths:
-----------
    PyCalendar/branches/json-2/src/pycalendar/icalendar/available.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/component.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/componentexpanded.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/componentrecur.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/definitions.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/freebusy.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/itipdefinitions.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrenceset.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_calendar.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_componentrecur.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_i18n.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_timezone.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_xml.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/valarm.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vavailability.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vevent.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vfreebusy.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vjournal.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezone.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonedaylight.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezoneelement.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonestandard.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vtodo.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/vunknown.py
    PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py
    PyCalendar/branches/json-2/src/pycalendar/parameter.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/n.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adr.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_n.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py
    PyCalendar/branches/json-2/src/pycalendar/vcard/xmldefinitions.py
    PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py
    PyCalendar/branches/json-2/src/pycalendar/xmlutils.py

Removed Paths:
-------------
    PyCalendar/branches/json-2/src/pycalendar/adr.py
    PyCalendar/branches/json-2/src/pycalendar/adrvalue.py
    PyCalendar/branches/json-2/src/pycalendar/attribute.py
    PyCalendar/branches/json-2/src/pycalendar/available.py
    PyCalendar/branches/json-2/src/pycalendar/calendar.py
    PyCalendar/branches/json-2/src/pycalendar/component.py
    PyCalendar/branches/json-2/src/pycalendar/componentexpanded.py
    PyCalendar/branches/json-2/src/pycalendar/componentrecur.py
    PyCalendar/branches/json-2/src/pycalendar/definitions.py
    PyCalendar/branches/json-2/src/pycalendar/freebusy.py
    PyCalendar/branches/json-2/src/pycalendar/itipdefinitions.py
    PyCalendar/branches/json-2/src/pycalendar/n.py
    PyCalendar/branches/json-2/src/pycalendar/nvalue.py
    PyCalendar/branches/json-2/src/pycalendar/orgvalue.py
    PyCalendar/branches/json-2/src/pycalendar/recurrence.py
    PyCalendar/branches/json-2/src/pycalendar/recurrenceset.py
    PyCalendar/branches/json-2/src/pycalendar/recurrencevalue.py
    PyCalendar/branches/json-2/src/pycalendar/requeststatusvalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_adr.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_adrvalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_calendar.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_componentrecur.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_i18n.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_n.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_nvalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_orgvalue.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_property.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_recurrence.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_requeststatus.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_timezone.py
    PyCalendar/branches/json-2/src/pycalendar/tests/test_xml.py
    PyCalendar/branches/json-2/src/pycalendar/valarm.py
    PyCalendar/branches/json-2/src/pycalendar/vavailability.py
    PyCalendar/branches/json-2/src/pycalendar/vevent.py
    PyCalendar/branches/json-2/src/pycalendar/vfreebusy.py
    PyCalendar/branches/json-2/src/pycalendar/vjournal.py
    PyCalendar/branches/json-2/src/pycalendar/vtimezone.py
    PyCalendar/branches/json-2/src/pycalendar/vtimezonedaylight.py
    PyCalendar/branches/json-2/src/pycalendar/vtimezoneelement.py
    PyCalendar/branches/json-2/src/pycalendar/vtimezonestandard.py
    PyCalendar/branches/json-2/src/pycalendar/vtodo.py
    PyCalendar/branches/json-2/src/pycalendar/vunknown.py
    PyCalendar/branches/json-2/src/pycalendar/xmldefs.py

Modified: PyCalendar/branches/json-2/src/pycalendar/__init__.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/__init__.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/__init__.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,62 +14,38 @@
 #    limitations under the License.
 ##
 
-__all__ = [
-    "attribute",
-    "available",
-    "binaryvalue",
-    "caladdressvalue",
-    "calendar",
-    "datetime",
-    "datetimevalue",
-    "definitions",
-    "duration",
-    "durationvalue",
-    "exceptions",
-    "freebusy",
-    "integervalue",
-    "locale",
-    "manager",
-    "multivalue",
-    "period",
-    "periodvalue",
-    "plaintextvalue",
-    "property",
-    "recurrence",
-    "recurrencevalue",
-    "requeststatusvalue",
-    "textvalue",
-    "timezone",
-    "timezonedb",
-    "unknownvalue",
-    "urivalue",
-    "utcoffsetvalue",
-    "valarm",
-    "value",
-    "vevent",
-    "vfreebusy",
-    "vjournal",
-    "vtimezone",
-    "vtimezonedaylight",
-    "vtimezonestandard",
-    "vtodo",
-    "vunknown",
-]
-
 # Import these to register the values
+
 import binaryvalue
 import caladdressvalue
 import datetimevalue
 import durationvalue
+import icalendar.recurrencevalue
+import icalendar.requeststatusvalue
 import integervalue
 import multivalue
 import periodvalue
-import recurrencevalue
-import requeststatusvalue
 import textvalue
 import unknownvalue
 import urivalue
 import utcoffsetvalue
+import vcard.adrvalue
+import vcard.nvalue
+import vcard.orgvalue
 
+# Import these to register the components
+
+import icalendar.available
+import icalendar.valarm
+import icalendar.vavailability
+import icalendar.vevent
+import icalendar.vfreebusy
+import icalendar.vjournal
+import icalendar.vtimezone
+import icalendar.vtimezonedaylight
+import icalendar.vtimezonestandard
+import icalendar.vtodo
+import icalendar.vunknown
+
 # Import these to force static initialisation
-import property
+#import property

Deleted: PyCalendar/branches/json-2/src/pycalendar/adr.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/adr.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/adr.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,127 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# vCard ADR value
-
-from pycalendar import utils
-from pycalendar.valueutils import ValueMixin
-
-class Adr(ValueMixin):
-    """
-    mValue is a tuple of seven str or tuples of str
-    """
-
-    (
-        POBOX,
-        EXTENDED,
-        STREET,
-        LOCALITY,
-        REGION,
-        POSTALCODE,
-        COUNTRY,
-        MAXITEMS
-    ) = range(8)
-
-    def __init__(self, pobox="", extended="", street="", locality="", region="", postalcode="", country=""):
-        self.mValue = (pobox, extended, street, locality, region, postalcode, country)
-
-
-    def duplicate(self):
-        return Adr(*self.mValue)
-
-
-    def __hash__(self):
-        return hash(self.mValue)
-
-
-    def __repr__(self):
-        return "ADR %s" % (self.getText(),)
-
-
-    def __eq__(self, comp):
-        return self.mValue == comp.mValue
-
-
-    def getPobox(self):
-        return self.mValue[Adr.POBOX]
-
-
-    def setPobox(self, value):
-        self.mValue[Adr.POBOX] = value
-
-
-    def getExtended(self):
-        return self.mValue[Adr.EXTENDED]
-
-
-    def setExtended(self, value):
-        self.mValue[Adr.EXTENDED] = value
-
-
-    def getStreet(self):
-        return self.mValue[Adr.STREET]
-
-
-    def setStreet(self, value):
-        self.mValue[Adr.STREET] = value
-
-
-    def getLocality(self):
-        return self.mValue[Adr.LOCALITY]
-
-
-    def setLocality(self, value):
-        self.mValue[Adr.LOCALITY] = value
-
-
-    def getRegion(self):
-        return self.mValue[Adr.REGION]
-
-
-    def setRegion(self, value):
-        self.mValue[Adr.REGION] = value
-
-
-    def getPostalCode(self):
-        return self.mValue[Adr.POSTALCODE]
-
-
-    def setPostalCode(self, value):
-        self.mValue[Adr.POSTALCODE] = value
-
-
-    def getCountry(self):
-        return self.mValue[Adr.COUNTRY]
-
-
-    def setCountry(self, value):
-        self.mValue[Adr.COUNTRY] = value
-
-
-    def parse(self, data):
-        self.mValue = utils.parseDoubleNestedList(data, Adr.MAXITEMS)
-
-
-    def generate(self, os):
-        utils.generateDoubleNestedList(os, self.mValue)
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value

Deleted: PyCalendar/branches/json-2/src/pycalendar/adrvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/adrvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/adrvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,51 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# vCard ADR value
-
-from pycalendar.adr import Adr
-from pycalendar.value import PyCalendarValue
-
-class AdrValue(PyCalendarValue):
-
-    def __init__(self, value=None):
-        self.mValue = value if value is not None else Adr()
-
-
-    def duplicate(self):
-        return AdrValue(self.mValue.duplicate())
-
-
-    def getType(self):
-        return PyCalendarValue.VALUETYPE_ADR
-
-
-    def parse(self, data):
-        self.mValue.parse(data)
-
-
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_ADR, AdrValue, None)

Deleted: PyCalendar/branches/json-2/src/pycalendar/attribute.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/attribute.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/attribute.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,128 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-"""
-ICalendar attribute.
-
-The attribute can consist of one or more values, all string.
-"""
-
-from pycalendar import xmldefs
-from pycalendar.utils import encodeParameterValue
-import xml.etree.cElementTree as XML
-
-class PyCalendarAttribute(object):
-
-    def __init__(self, name, value=None):
-        self.mName = name
-        if value is None:
-            self.mValues = []
-        elif isinstance(value, basestring):
-            self.mValues = [value]
-        else:
-            self.mValues = value
-
-
-    def duplicate(self):
-        other = PyCalendarAttribute(self.mName)
-        other.mValues = self.mValues[:]
-        return other
-
-
-    def __hash__(self):
-        return hash((self.mName.upper(), tuple(self.mValues)))
-
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-
-    def __eq__(self, other):
-        if not isinstance(other, PyCalendarAttribute):
-            return False
-        return self.mName.upper() == other.mName.upper() and self.mValues == other.mValues
-
-
-    def getName(self):
-        return self.mName
-
-
-    def setName(self, name):
-        self.mName = name
-
-
-    def getFirstValue(self):
-        return self.mValues[0]
-
-
-    def getValues(self):
-        return self.mValues
-
-
-    def setValues(self, values):
-        self.mValues = values
-
-
-    def addValue(self, value):
-        self.mValues.append(value)
-
-
-    def removeValue(self, value):
-        self.mValues.remove(value)
-        return len(self.mValues)
-
-
-    def generate(self, os):
-        try:
-            os.write(self.mName)
-
-            # To support vCard 2.1 syntax we allow parameters without values
-            if self.mValues:
-                os.write("=")
-
-                first = True
-                for s in self.mValues:
-                    if first:
-                        first = False
-                    else:
-                        os.write(",")
-
-                    # Write with quotation if required
-                    self.generateValue(os, s)
-
-        except:
-            # We ignore errors
-            pass
-
-
-    def generateValue(self, os, str):
-
-        # ^-escaping
-        str = encodeParameterValue(str)
-
-        # Look for quoting
-        if str.find(":") != -1 or str.find(";") != -1 or str.find(",") != -1:
-            os.write("\"%s\"" % (str,))
-        else:
-            os.write(str)
-
-
-    def writeXML(self, node, namespace):
-        param = XML.SubElement(node, xmldefs.makeTag(namespace, self.getName()))
-        for value in self.getValues():
-            # TODO: need to figure out proper value types
-            text = XML.SubElement(param, xmldefs.makeTag(namespace, xmldefs.value_text))
-            text.text = value

Deleted: PyCalendar/branches/json-2/src/pycalendar/available.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/available.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/available.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,90 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.componentrecur import PyCalendarComponentRecur
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-
-class PyCalendarAvailable(PyCalendarComponentRecur):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CREATED,
-        definitions.cICalProperty_DESCRIPTION,
-        definitions.cICalProperty_GEO,
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_LOCATION,
-        definitions.cICalProperty_RECURRENCE_ID,
-        definitions.cICalProperty_RRULE,
-        definitions.cICalProperty_SEQUENCE,
-        definitions.cICalProperty_SUMMARY,
-        definitions.cICalProperty_DTEND,
-        definitions.cICalProperty_DURATION,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarAvailable, self).__init__(parent=parent)
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarAvailable, self).duplicate(parent=parent)
-
-
-    def getType(self):
-        return definitions.cICalComponent_AVAILABLE
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        fixed, unfixed = super(PyCalendarAvailable, self).validate(doFix)
-
-        # Extra constraint: only one of DTEND or DURATION
-        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
-            # Fix by removing the DTEND
-            logProblem = "[%s] Properties must not both be present: %s, %s" % (
-                self.getType(),
-                definitions.cICalProperty_DTEND,
-                definitions.cICalProperty_DURATION,
-            )
-            if doFix:
-                self.removeProperties(definitions.cICalProperty_DTEND)
-                fixed.append(logProblem)
-            else:
-                unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_RECURRENCE_ID,
-            definitions.cICalProperty_DTSTART,
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_DTEND,
-        )

Modified: PyCalendar/branches/json-2/src/pycalendar/binaryvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/binaryvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/binaryvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,13 +16,13 @@
 
 # iCalendar Binary value
 
-from pycalendar import xmldefs
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.value import Value
 
-class PyCalendarBinaryValue(PyCalendarPlainTextValue):
+class BinaryValue(PlainTextValue):
 
     def getType(self):
-        return PyCalendarValue.VALUETYPE_BINARY
+        return Value.VALUETYPE_BINARY
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_BINARY, PyCalendarBinaryValue, xmldefs.value_binary)
+Value.registerType(Value.VALUETYPE_BINARY, BinaryValue, xmldefinitions.value_binary)

Modified: PyCalendar/branches/json-2/src/pycalendar/caladdressvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/caladdressvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/caladdressvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,13 +16,13 @@
 
 # iCalendar UTC Offset value
 
-from pycalendar import xmldefs
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.value import Value
 
-class PyCalendarCalAddressValue(PyCalendarPlainTextValue):
+class CalAddressValue(PlainTextValue):
 
     def getType(self):
-        return PyCalendarValue.VALUETYPE_CALADDRESS
+        return Value.VALUETYPE_CALADDRESS
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_CALADDRESS, PyCalendarCalAddressValue, xmldefs.value_cal_address)
+Value.registerType(Value.VALUETYPE_CALADDRESS, CalAddressValue, xmldefinitions.value_cal_address)

Deleted: PyCalendar/branches/json-2/src/pycalendar/calendar.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/calendar.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/calendar.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,754 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from cStringIO import StringIO
-from pycalendar import definitions, xmldefs
-from pycalendar.available import PyCalendarAvailable
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.componentexpanded import PyCalendarComponentExpanded
-from pycalendar.componentrecur import PyCalendarComponentRecur
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarInvalidData, \
-    PyCalendarValidationError
-from pycalendar.freebusy import PyCalendarFreeBusy
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.parser import ParserContext
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.property import PyCalendarProperty
-from pycalendar.utils import readFoldedLine
-from pycalendar.valarm import PyCalendarVAlarm
-from pycalendar.vavailability import PyCalendarVAvailability
-from pycalendar.vevent import PyCalendarVEvent
-from pycalendar.vfreebusy import PyCalendarVFreeBusy
-from pycalendar.vjournal import PyCalendarVJournal
-from pycalendar.vtimezone import PyCalendarVTimezone
-from pycalendar.vtimezonedaylight import PyCalendarVTimezoneDaylight
-from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
-from pycalendar.vtodo import PyCalendarVToDo
-from pycalendar.vunknown import PyCalendarUnknownComponent
-import collections
-import xml.etree.cElementTree as XML
-
-class PyCalendar(PyCalendarComponentBase):
-
-    REMOVE_ALL = 0
-    REMOVE_ONLY_THIS = 1
-    REMOVE_THIS_AND_FUTURE = 2
-
-    FIND_EXACT = 0
-    FIND_MASTER = 1
-
-    sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
-    sDomain = "mulberrymail.com"
-
-    @staticmethod
-    def setPRODID(prodid):
-        PyCalendar.sProdID = prodid
-
-
-    @staticmethod
-    def setDomain(domain):
-        PyCalendar.sDomain = domain
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_PRODID,
-        definitions.cICalProperty_VERSION,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CALSCALE,
-        definitions.cICalProperty_METHOD,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None, add_defaults=True):
-        super(PyCalendar, self).__init__(None)
-
-        self.mName = ""
-        self.mDescription = ""
-        self.mMasterComponentsByTypeAndUID = collections.defaultdict(lambda: collections.defaultdict(list))
-        self.mOverriddenComponentsByUID = collections.defaultdict(list)
-
-        if add_defaults:
-            self.addDefaultProperties()
-
-
-    def duplicate(self):
-        other = super(PyCalendar, self).duplicate()
-        other.mName = self.mName
-        other.mDescription = self.mDescription
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VCALENDAR
-
-
-    def getName(self):
-        return self.mName
-
-
-    def setName(self, name):
-        self.mName = name
-
-
-    def editName(self, name):
-        if self.mName != name:
-            # Updated cached value
-            self.mName = name
-
-            # Remove existing items
-            self.removeProperties(definitions.cICalProperty_XWRCALNAME)
-
-            # Now create properties
-            if len(name):
-                self.ddProperty(PyCalendarProperty(definitions.cICalProperty_XWRCALNAME, name))
-
-
-    def getDescription(self):
-        return self.mDescription
-
-
-    def setDescription(self, description):
-        self.mDescription = description
-
-
-    def editDescription(self, description):
-        if self.mDescription != description:
-            # Updated cached value
-            self.mDescription = description
-
-            # Remove existing items
-            self.removeProperties(definitions.cICalProperty_XWRCALDESC)
-
-            # Now create properties
-            if len(description):
-                self.addProperty(PyCalendarProperty(definitions.cICalProperty_XWRCALDESC, description))
-
-
-    def getMethod(self):
-        result = ""
-        if self.hasProperty(definitions.cICalProperty_METHOD):
-            result = self.loadValueString(definitions.cICalProperty_METHOD)
-        return result
-
-
-    def changeUID(self, oldUID, newUID):
-        """
-        Change the UID of all components with a matching UID to a new value. We need to
-        do this at the calendar level because this object maintains mappings based on UID
-        which need to be updated whenever the UID changes.
-
-        @param oldUID: the old value to match
-        @type oldUID: C{str}
-        @param newUID: the new value to match
-        @type newUID: C{str}
-        """
-
-        # Each component
-        for component in self.mComponents:
-            if component.getUID() == oldUID:
-                component.setUID(newUID)
-
-        # Maps
-        if oldUID in self.mOverriddenComponentsByUID:
-            self.mOverriddenComponentsByUID[newUID] = self.mOverriddenComponentsByUID[oldUID]
-            del self.mOverriddenComponentsByUID[oldUID]
-        for ctype in self.mMasterComponentsByTypeAndUID:
-            if oldUID in self.mMasterComponentsByTypeAndUID[ctype]:
-                self.mMasterComponentsByTypeAndUID[ctype][newUID] = self.mMasterComponentsByTypeAndUID[ctype][oldUID]
-                del self.mMasterComponentsByTypeAndUID[ctype][oldUID]
-
-
-    def finalise(self):
-        # Get calendar name if present
-
-        # Get X-WR-CALNAME
-        temps = self.loadValueString(definitions.cICalProperty_XWRCALNAME)
-        if temps is not None:
-            self.mName = temps
-
-        # Get X-WR-CALDESC
-        temps = self.loadValueString(definitions.cICalProperty_XWRCALDESC)
-        if temps is not None:
-            self.mDescription = temps
-
-
-    def validate(self, doFix=False, doRaise=False):
-        """
-        Validate the data in this component and optionally fix any problems. Return
-        a tuple containing two lists: the first describes problems that were fixed, the
-        second problems that were not fixed. Caller can then decide what to do with unfixed
-        issues.
-        """
-
-        # Optional raise behavior
-        fixed, unfixed = super(PyCalendar, self).validate(doFix)
-        if doRaise and unfixed:
-            raise PyCalendarValidationError(";".join(unfixed))
-        return fixed, unfixed
-
-
-    def sortedComponentNames(self):
-        return (
-            definitions.cICalComponent_VTIMEZONE,
-            definitions.cICalComponent_VEVENT,
-            definitions.cICalComponent_VTODO,
-            definitions.cICalComponent_VJOURNAL,
-            definitions.cICalComponent_VFREEBUSY,
-            definitions.cICalComponent_VAVAILABILITY,
-        )
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_VERSION,
-            definitions.cICalProperty_CALSCALE,
-            definitions.cICalProperty_METHOD,
-            definitions.cICalProperty_PRODID,
-        )
-
-
-    @staticmethod
-    def parseText(data):
-
-        cal = PyCalendar(add_defaults=False)
-        if cal.parse(StringIO(data)):
-            return cal
-        else:
-            return None
-
-
-    def parse(self, ins):
-
-        result = False
-
-        self.setProperties({})
-
-        LOOK_FOR_VCALENDAR = 0
-        GET_PROPERTY_OR_COMPONENT = 1
-
-        state = LOOK_FOR_VCALENDAR
-
-        # Get lines looking for start of calendar
-        lines = [None, None]
-        comp = self
-        compend = None
-        componentstack = []
-
-        while readFoldedLine(ins, lines):
-
-            line = lines[0]
-
-            if state == LOOK_FOR_VCALENDAR:
-
-                # Look for start
-                if line == self.getBeginDelimiter():
-                    # Next state
-                    state = GET_PROPERTY_OR_COMPONENT
-
-                    # Indicate success at this point
-                    result = True
-
-                # Handle blank line
-                elif len(line) == 0:
-                    # Raise if requested, otherwise just ignore
-                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("iCalendar data has blank lines")
-
-                # Unrecognized data
-                else:
-                    raise PyCalendarInvalidData("iCalendar data not recognized", line)
-
-            elif state == GET_PROPERTY_OR_COMPONENT:
-
-                # Parse property or look for start of component
-                if line.startswith("BEGIN:"):
-
-                    # Push previous details to stack
-                    componentstack.append((comp, compend,))
-
-                    # Start a new component
-                    comp = PyCalendar.makeComponent(line[6:], comp)
-                    compend = comp.getEndDelimiter()
-
-                # Look for end of object
-                elif line == self.getEndDelimiter():
-
-                    # Finalise the current calendar
-                    self.finalise()
-
-                    # Change state
-                    state = LOOK_FOR_VCALENDAR
-
-                # Look for end of current component
-                elif line == compend:
-
-                    # Finalise the component (this caches data from the properties)
-                    comp.finalise()
-
-                    # Embed component in parent and reset to use parent
-                    componentstack[-1][0].addComponent(comp)
-                    comp, compend = componentstack.pop()
-
-                # Blank line
-                elif len(line) == 0:
-                    # Raise if requested, otherwise just ignore
-                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("iCalendar data has blank lines")
-
-                # Must be a property
-                else:
-
-                    # Parse attribute/value for top-level calendar item
-                    prop = PyCalendarProperty()
-                    if prop.parse(line):
-
-                        # Check for valid property
-                        if comp is self:
-                            if not comp.validProperty(prop):
-                                raise PyCalendarInvalidData("Invalid property", str(prop))
-                            elif not comp.ignoreProperty(prop):
-                                comp.addProperty(prop)
-                        else:
-                            comp.addProperty(prop)
-
-        # Check for truncated data
-        if state != LOOK_FOR_VCALENDAR:
-            raise PyCalendarInvalidData("iCalendar data not complete")
-
-        # We need to store all timezones in the static object so they can be accessed by any date object
-        from timezonedb import PyCalendarTimezoneDatabase
-        PyCalendarTimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
-
-        # Validate some things
-        if result and not self.hasProperty("VERSION"):
-            raise PyCalendarInvalidData("iCalendar missing VERSION")
-
-        return result
-
-
-    def parseComponent(self, ins):
-
-        result = None
-
-        LOOK_FOR_VCALENDAR = 0
-        GET_PROPERTY_OR_COMPONENT = 1
-        GOT_VCALENDAR = 4
-
-        state = LOOK_FOR_VCALENDAR
-
-        # Get lines looking for start of calendar
-        lines = [None, None]
-        comp = self
-        compend = None
-        componentstack = []
-        got_timezone = False
-
-        while readFoldedLine(ins, lines):
-
-            if state == LOOK_FOR_VCALENDAR:
-
-                # Look for start
-                if lines[0] == self.getBeginDelimiter():
-                    # Next state
-                    state = GET_PROPERTY_OR_COMPONENT
-
-                # Handle blank line
-                elif len(lines[0]) == 0:
-                    # Raise if requested, otherwise just ignore
-                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("iCalendar data has blank lines")
-
-                # Unrecognized data
-                else:
-                    raise PyCalendarInvalidData("iCalendar data not recognized", lines[0])
-
-            elif state == GET_PROPERTY_OR_COMPONENT:
-
-                # Parse property or look for start of component
-                if lines[0].startswith("BEGIN:"):
-
-                    # Push previous details to stack
-                    componentstack.append((comp, compend,))
-
-                    # Start a new component
-                    comp = PyCalendar.makeComponent(lines[0][6:], comp)
-                    compend = comp.getEndDelimiter()
-
-                    # Cache as result - but only the first one, we ignore the rest
-                    if result is None:
-                        result = comp
-
-                    # Look for timezone component to trigger timezone merge only if one is present
-                    if comp.getType() == definitions.cICalComponent_VTIMEZONE:
-                        got_timezone = True
-
-                elif lines[0] == self.getEndDelimiter():
-
-                    # Change state
-                    state = GOT_VCALENDAR
-
-                # Look for end of current component
-                elif lines[0] == compend:
-
-                    # Finalise the component (this caches data from the properties)
-                    comp.finalise()
-
-                    # Embed component in parent and reset to use parent
-                    componentstack[-1][0].addComponent(comp)
-                    comp, compend = componentstack.pop()
-
-                # Blank line
-                elif len(lines[0]) == 0:
-                    # Raise if requested, otherwise just ignore
-                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("iCalendar data has blank lines")
-
-                # Ignore top-level items
-                elif comp is self:
-                    pass
-
-                # Must be a property
-                else:
-
-                    # Parse attribute/value for top-level calendar item
-                    prop = PyCalendarProperty()
-                    if prop.parse(lines[0]):
-
-                        # Check for valid property
-                        if comp is not self:
-                            comp.addProperty(prop)
-
-            # Exit if we have one - ignore all the rest
-            if state == GOT_VCALENDAR:
-                break
-
-        # We need to store all timezones in the static object so they can be accessed by any date object
-        # Only do this if we read in a timezone
-        if got_timezone:
-            from timezonedb import PyCalendarTimezoneDatabase
-            PyCalendarTimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
-
-        return result
-
-
-    def addComponent(self, component):
-        """
-        Override to track components by UID.
-        """
-        super(PyCalendar, self).addComponent(component)
-
-        if isinstance(component, PyCalendarComponentRecur):
-            uid = component.getUID()
-            rid = component.getRecurrenceID()
-            if rid:
-                self.mOverriddenComponentsByUID[uid].append(component)
-            else:
-                self.mMasterComponentsByTypeAndUID[component.getType()][uid] = component
-
-
-    def removeComponent(self, component):
-        """
-        Override to track components by UID.
-        """
-        super(PyCalendar, self).removeComponent(component)
-
-        if isinstance(component, PyCalendarComponentRecur):
-            uid = component.getUID()
-            rid = component.getRecurrenceID()
-            if rid:
-                self.mOverriddenComponentsByUID[uid].remove(component)
-            else:
-                del self.mMasterComponentsByTypeAndUID[component.getType()][uid]
-
-
-    def getText(self, includeTimezones=False):
-        s = StringIO()
-        self.generate(s, includeTimezones=includeTimezones)
-        return s.getvalue()
-
-
-    def generate(self, os, includeTimezones=False):
-        # Make sure all required timezones are in this object
-        if includeTimezones:
-            self.includeTimezones()
-        super(PyCalendar, self).generate(os)
-
-
-    def getTextXML(self, includeTimezones=False):
-        node = self.writeXML(includeTimezones)
-        return xmldefs.toString(node)
-
-
-    def writeXML(self, includeTimezones=False):
-        # Make sure all required timezones are in this object
-        if includeTimezones:
-            self.includeTimezones()
-
-        # Root node structure
-        root = XML.Element(xmldefs.makeTag(xmldefs.iCalendar20_namespace, xmldefs.icalendar))
-        super(PyCalendar, self).writeXML(root, xmldefs.iCalendar20_namespace)
-        return root
-
-
-    # Get expanded components
-    def getVEvents(self, period, list, all_day_at_top=True):
-        # Look at each VEvent
-        for vevent in self.getComponents(definitions.cICalComponent_VEVENT):
-            vevent.expandPeriod(period, list)
-
-        if (all_day_at_top):
-            list.sort(PyCalendarComponentExpanded.sort_by_dtstart_allday)
-        else:
-            list.sort(PyCalendarComponentExpanded.sort_by_dtstart)
-
-
-    def getVToDos(self, only_due, all_dates, upto_due_date, list):
-        # Get current date-time less one day to test for completed events during the last day
-        minusoneday = PyCalendarDateTime()
-        minusoneday.setNowUTC()
-        minusoneday.offsetDay(-1)
-
-        today = PyCalendarDateTime()
-        today.setToday()
-
-        # Look at each VToDo
-        for vtodo in self.getComponents(definitions.cICalComponent_VTODO):
-
-            # Filter out done (that were complted more than a day ago) or cancelled to dos if required
-            if only_due:
-                if vtodo.getStatus() == definitions.eStatus_VToDo_Cancelled:
-                    continue
-                elif ((vtodo.getStatus() == definitions.eStatus_VToDo_Completed) and
-                            (not vtodo.hasCompleted() or (vtodo.getCompleted() < minusoneday))):
-                    continue
-
-            # Filter out those with end after chosen date if required
-            if not all_dates:
-                if vtodo.hasEnd() and (vtodo.getEnd() > upto_due_date):
-                    continue
-                elif not vtodo.hasEnd() and (today > upto_due_date):
-                    continue
-
-            # TODO: fix this
-            #list.append(PyCalendarComponentExpandedShared(PyCalendarComponentExpanded(vtodo, None)))
-
-
-    def getRecurrenceInstancesItems(self, type, uid, items):
-        # Get instances from list
-        items.extend(self.mOverriddenComponentsByUID.get(uid, ()))
-
-
-    def getRecurrenceInstancesIds(self, type, uid, ids):
-        # Get instances from list
-        ids.extend([comp.getRecurrenceID() for comp in self.mOverriddenComponentsByUID.get(uid, ())])
-
-
-    # Freebusy generation
-    def getVFreeBusyList(self, period, list):
-        # Look at each VFreeBusy
-        for vfreebusy in self.getComponents(definitions.cICalComponent_VFREEBUSY):
-            vfreebusy.expandPeriod(period, list)
-
-
-    def getVFreeBusyFB(self, period, fb):
-        # First create expanded set
-        # TODO: fix this
-        #list = PyCalendarExpandedComponents()
-        self.getVEvents(period, list)
-        if len(list) == 0:
-            return
-
-        # Get start/end list for each non-all-day expanded components
-        dtstart = []
-        dtend = []
-        for dt in list:
-
-            # Ignore if all-day
-            if dt.getInstanceStart().isDateOnly():
-                continue
-
-            # Ignore if transparent to free-busy
-            transp = ""
-            if dt.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
-                continue
-
-            # Add start/end to list
-            dtstart.append(dt.getInstanceStart())
-            dtend.append(dt.getInstanceEnd())
-
-        # No longer need the expanded items
-        list.clear()
-
-        # Create non-overlapping periods as properties in the freebusy component
-        temp = PyCalendarPeriod(dtstart.front(), dtend.front())
-        dtstart_iter = dtstart.iter()
-        dtstart_iter.next()
-        dtend_iter = dtend.iter()
-        dtend_iter.next()
-        for i in i:
-
-            # Check for non-overlap
-            if dtstart_iter > temp.getEnd():
-
-                # Current period is complete
-                fb.addProperty(PyCalendarProperty(definitions.cICalProperty_FREEBUSY, temp))
-
-                # Reset period to new range
-                temp = PyCalendarPeriod(dtstart_iter, dtend_iter)
-
-            # They overlap - check for extended end
-            if dtend_iter > temp.getEnd():
-
-                # Extend the end
-                temp = PyCalendarPeriod(temp.getStart(), dtend_iter)
-
-        # Add remaining period as property
-        fb.addProperty(PyCalendarProperty(definitions.cICalProperty_FREEBUSY, temp))
-
-
-    def getFreeBusy(self, period, fb):
-        # First create expanded set
-
-        list = []
-        self.getVEvents(period, list)
-
-        # Get start/end list for each non-all-day expanded components
-        for comp in list:
-
-            # Ignore if all-day
-            if comp.getInstanceStart().isDateOnly():
-                continue
-
-            # Ignore if transparent to free-busy
-            transp = ""
-            if comp.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
-                continue
-
-            # Add free busy item to list
-            status = comp.getMaster().getStatus()
-            if status in (definitions.eStatus_VEvent_None, definitions.eStatus_VEvent_Confirmed):
-                fb.append(PyCalendarFreeBusy(PyCalendarFreeBusy.BUSY, PyCalendarPeriod(comp.getInstanceStart(), comp.getInstanceEnd())))
-            elif status == definitions.eStatus_VEvent_Tentative:
-                fb.append(PyCalendarFreeBusy(PyCalendarFreeBusy.BUSYTENTATIVE, PyCalendarPeriod(comp.getInstanceStart(), comp.getInstanceEnd())))
-                break
-            elif status == definitions.eStatus_VEvent_Cancelled:
-                # Cancelled => does not contribute to busy time
-                pass
-
-        # Now get the VFREEBUSY info
-        list2 = []
-        self.getVFreeBusy(period, list2)
-
-        # Get start/end list for each free-busy
-        for comp in list2:
-
-            # Expand component and add free busy info to list
-            comp.expandPeriod(period, fb)
-
-        # Add remaining period as property
-        PyCalendarFreeBusy.resolveOverlaps(fb)
-
-
-    def getTimezoneOffsetSeconds(self, tzid, dt):
-        # Find timezone that matches the name (which is the same as the map key)
-        timezone = self.getTimezone(tzid)
-        return timezone.getTimezoneOffsetSeconds(dt) if timezone else 0
-
-
-    def getTimezoneDescriptor(self, tzid, dt):
-        # Find timezone that matches the name (which is the same as the map key)
-        timezone = self.getTimezone(tzid)
-        return timezone.getTimezoneDescriptor(dt) if timezone else ""
-
-
-    def getTimezone(self, tzid):
-        # Find timezone that matches the name (which is the same as the map key)
-        for timezone in self.getComponents(definitions.cICalComponent_VTIMEZONE):
-            if timezone.getID() == tzid:
-                return timezone
-        else:
-            return None
-
-
-    def addDefaultProperties(self):
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_PRODID, PyCalendar.sProdID))
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_VERSION, "2.0"))
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_CALSCALE, "GREGORIAN"))
-
-
-    def validProperty(self, prop):
-        if prop.getName() == definitions.cICalProperty_VERSION:
-
-            tvalue = prop.getTextValue()
-            if ((tvalue is None) or (tvalue.getValue() != "2.0")):
-                return False
-
-        elif prop.getName() == definitions.cICalProperty_CALSCALE:
-
-            tvalue = prop.getTextValue()
-            if ((tvalue is None) or (tvalue.getValue() != "GREGORIAN")):
-                return False
-
-        return True
-
-
-    def ignoreProperty(self, prop):
-        return False #prop.getName() in (definitions.cICalProperty_VERSION, definitions.cICalProperty_CALSCALE, definitions.cICalProperty_PRODID)
-
-
-    def includeTimezones(self):
-        # Get timezone names from each component
-        tzids = set()
-        for component in self.mComponents:
-            if component.getType() != definitions.cICalComponent_VTIMEZONE:
-                component.getTimezones(tzids)
-
-        # Make sure each timezone is in current calendar
-        from timezonedb import PyCalendarTimezoneDatabase
-        for tzid in tzids:
-            tz = self.getTimezone(tzid)
-            if tz is None:
-                # Find it in the static object
-                tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
-                if tz is not None:
-                    dup = tz.duplicate()
-                    self.addComponent(dup)
-
-
-    @staticmethod
-    def makeComponent(compname, parent):
-
-        mapper = {
-            definitions.cICalComponent_VEVENT: PyCalendarVEvent,
-            definitions.cICalComponent_VTODO: PyCalendarVToDo,
-            definitions.cICalComponent_VJOURNAL: PyCalendarVJournal,
-            definitions.cICalComponent_VFREEBUSY: PyCalendarVFreeBusy,
-            definitions.cICalComponent_VTIMEZONE: PyCalendarVTimezone,
-            definitions.cICalComponent_VAVAILABILITY: PyCalendarVAvailability,
-            definitions.cICalComponent_VALARM: PyCalendarVAlarm,
-            definitions.cICalComponent_AVAILABLE: PyCalendarAvailable,
-            definitions.cICalComponent_STANDARD: PyCalendarVTimezoneStandard,
-            definitions.cICalComponent_DAYLIGHT: PyCalendarVTimezoneDaylight,
-        }
-
-        try:
-            cls = mapper[compname]
-            return cls(parent=parent)
-        except KeyError:
-            return PyCalendarUnknownComponent(parent=parent, comptype=compname)

Deleted: PyCalendar/branches/json-2/src/pycalendar/component.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/component.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/component.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,192 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import stringutils
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.property import PyCalendarProperty
-import os
-import time
-import uuid
-
-class PyCalendarComponent(PyCalendarComponentBase):
-
-    uid_ctr = 1
-
-    def __init__(self, parent=None):
-
-        super(PyCalendarComponent, self).__init__(parent)
-        self.mUID = ""
-        self.mSeq = 0
-        self.mOriginalSeq = 0
-        self.mChanged = False
-
-
-    def duplicate(self, parent=None, **args):
-
-        other = super(PyCalendarComponent, self).duplicate(parent=parent, **args)
-        other.mUID = self.mUID
-        other.mSeq = self.mSeq
-        other.mOriginalSeq = self.mOriginalSeq
-
-        other.mChanged = self.mChanged
-
-        return other
-
-
-    def __repr__(self):
-        return "%s: UID: %s" % (self.getType(), self.getMapKey(),)
-
-
-    def getMimeComponentName(self):
-        raise NotImplementedError
-
-
-    def getMapKey(self):
-        if hasattr(self, "mMapKey"):
-            return self.mMapKey
-        elif self.mUID:
-            return self.mUID
-        else:
-            self.mMapKey = str(uuid.uuid4())
-            return self.mMapKey
-
-
-    def getSortKey(self):
-        return self.getMapKey()
-
-
-    def getMasterKey(self):
-        return self.mUID
-
-
-    def getUID(self):
-        return self.mUID
-
-
-    def setUID(self, uid):
-        if uid:
-            self.mUID = uid
-        else:
-            # Get left-side of UID (first 24 chars of MD5 digest of time, pid
-            # and ctr)
-            lhs_txt = ""
-            lhs_txt += str(time.time())
-            lhs_txt += "."
-            lhs_txt += str(os.getpid())
-            lhs_txt += "."
-            lhs_txt += str(PyCalendarComponent.uid_ctr)
-            PyCalendarComponent.uid_ctr += 1
-            lhs = stringutils.md5digest(lhs_txt)
-
-            # Get right side (domain) of message-id
-            rhs = None
-
-            # Use app name
-            from pycalendar.calendar import PyCalendar
-            domain = PyCalendar.sDomain
-            domain += str(PyCalendarComponent.uid_ctr)
-
-            # Use first 24 chars of MD5 digest of the domain as the
-            # right-side of message-id
-            rhs = stringutils.md5digest(domain)
-
-            # Generate the UID string
-            new_uid = lhs
-            new_uid += "@"
-            new_uid += rhs
-
-            self.mUID = new_uid
-
-        self.removeProperties(definitions.cICalProperty_UID)
-
-        prop = PyCalendarProperty(definitions.cICalProperty_UID, self.mUID)
-        self.addProperty(prop)
-
-
-    def getSeq(self):
-        return self.mSeq
-
-
-    def setSeq(self, seq):
-        self.mSeq = seq
-
-        self.removeProperties(definitions.cICalProperty_SEQUENCE)
-
-        prop = PyCalendarProperty(definitions.cICalProperty_SEQUENCE, self.mSeq)
-        self.addProperty(prop)
-
-
-    def getOriginalSeq(self):
-        return self.mOriginalSeq
-
-
-    def getChanged(self):
-        return self.mChanged
-
-
-    def setChanged(self, changed):
-        self.mChanged = changed
-
-
-    def initDTSTAMP(self):
-        self.removeProperties(definitions.cICalProperty_DTSTAMP)
-
-        prop = PyCalendarProperty(definitions.cICalProperty_DTSTAMP,
-                                  PyCalendarDateTime.getNowUTC())
-        self.addProperty(prop)
-
-
-    def updateLastModified(self):
-        self.removeProperties(definitions.cICalProperty_LAST_MODIFIED)
-
-        prop = PyCalendarProperty(definitions.cICalProperty_LAST_MODIFIED,
-                                  PyCalendarDateTime.getNowUTC())
-        self.addProperty(prop)
-
-
-    def finalise(self):
-        # Get UID
-        temps = self.loadValueString(definitions.cICalProperty_UID)
-        if temps is not None:
-            self.mUID = temps
-
-        # Get SEQ
-        temp = self.loadValueInteger(definitions.cICalProperty_SEQUENCE)
-        if temp is not None:
-            self.mSeq = temp
-
-        # Cache the original sequence when the component is read in.
-        # This will be used to synchronise changes between two instances of the
-        # same calendar
-        self.mOriginalSeq = self.mSeq
-
-
-    def canGenerateInstance(self):
-        return True
-
-
-    def getTimezones(self, tzids):
-        # Look for all date-time properties
-        for props in self.mProperties.itervalues():
-            for prop in props:
-                # Try to get a date-time value from the property
-                dtv = prop.getDateTimeValue()
-                if dtv is not None:
-                    # Add timezone id if appropriate
-                    if dtv.getValue().getTimezoneID():
-                        tzids.add(dtv.getValue().getTimezoneID())

Modified: PyCalendar/branches/json-2/src/pycalendar/componentbase.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/componentbase.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/componentbase.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,14 +15,13 @@
 ##
 
 from cStringIO import StringIO
-from pycalendar import xmldefs
-from pycalendar.datetimevalue import PyCalendarDateTimeValue
-from pycalendar.periodvalue import PyCalendarPeriodValue
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions, xmlutils
+from pycalendar.datetimevalue import DateTimeValue
+from pycalendar.periodvalue import PeriodValue
+from pycalendar.value import Value
 import xml.etree.cElementTree as XML
 
-class PyCalendarComponentBase(object):
+class ComponentBase(object):
 
     # These are class attributes for sets of properties for testing cardinality constraints. The sets
     # must contain property names.
@@ -35,6 +34,9 @@
 
     sortSubComponents = True
 
+    sComponentType = None
+    sPropertyType = None
+
     def __init__(self, parent=None):
         self.mParentComponent = parent
         self.mComponents = []
@@ -70,7 +72,7 @@
 
 
     def __eq__(self, other):
-        if not isinstance(other, PyCalendarComponentBase):
+        if not isinstance(other, ComponentBase):
             return False
         return self.getType() == other.getType() and self.compareProperties(other) and self.compareComponents(other)
 
@@ -276,7 +278,7 @@
             elif self.countProperty(propname) == 0: # Possibly fix by adding empty property
                 logProblem = "[%s] Missing required property: %s" % (self.getType(), propname)
                 if doFix:
-                    self.addProperty(PyCalendarProperty(propname, ""))
+                    self.addProperty(self.sPropertyType(propname, ""))
                     fixed.append(logProblem)
                 else:
                     unfixed.append(logProblem)
@@ -337,7 +339,7 @@
     def writeXML(self, node, namespace):
 
         # Component element
-        comp = XML.SubElement(node, xmldefs.makeTag(namespace, self.getType()))
+        comp = XML.SubElement(node, xmlutils.makeTag(namespace, self.getType()))
 
         # Each property
         self.writePropertiesXML(comp, namespace)
@@ -348,7 +350,7 @@
 
     def writeXMLFiltered(self, node, namespace, filter):
         # Component element
-        comp = XML.SubElement(node, xmldefs.makeTag(namespace, self.getType()))
+        comp = XML.SubElement(node, xmlutils.makeTag(namespace, self.getType()))
 
         # Each property
         self.writePropertiesFilteredXML(comp, namespace, filter)
@@ -407,7 +409,7 @@
     def writeComponentsXML(self, node, namespace):
 
         if self.mComponents:
-            comps = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.components))
+            comps = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.components))
 
             # Write out the remainder
             for component in self.sortedComponents():
@@ -417,7 +419,7 @@
     def writeComponentsFilteredXML(self, node, namespace, filter):
 
         if self.mComponents:
-            comps = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.components))
+            comps = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.components))
 
             # Shortcut for all sub-components
             if filter.isAllSubComponents():
@@ -426,7 +428,7 @@
                 for subcomp in self.sortedcomponents():
                     subfilter = filter.getSubComponentFilter(subcomp.getType())
                     if subfilter is not None:
-                        subcomp.writeFilteredXML(comps, namespace, subfilter)
+                        subcomp.writeXMLFiltered(comps, namespace, subfilter)
 
 
     def loadValue(self, value_name):
@@ -439,18 +441,18 @@
     def loadValueInteger(self, value_name, type=None):
         if type:
             if self.hasProperty(value_name):
-                if type == PyCalendarValue.VALUETYPE_INTEGER:
+                if type == Value.VALUETYPE_INTEGER:
                     ivalue = self.findFirstProperty(value_name).getIntegerValue()
                     if ivalue is not None:
                         return ivalue.getValue()
-                elif type == PyCalendarValue.VALUETYPE_UTC_OFFSET:
+                elif type == Value.VALUETYPE_UTC_OFFSET:
                     uvalue = self.findFirstProperty(value_name).getUTCOffsetValue()
                     if (uvalue is not None):
                         return uvalue.getValue()
 
             return None
         else:
-            return self.loadValueInteger(value_name, PyCalendarValue.VALUETYPE_INTEGER)
+            return self.loadValueInteger(value_name, Value.VALUETYPE_INTEGER)
 
 
     def loadValueString(self, value_name):
@@ -513,12 +515,12 @@
                 if (mvalue is not None):
                     for obj in mvalue.getValues():
                         # cast to date-time
-                        if isinstance(obj, PyCalendarDateTimeValue):
+                        if isinstance(obj, DateTimeValue):
                             if add:
                                 value.addDT(obj.getValue())
                             else:
                                 value.subtractDT(obj.getValue())
-                        elif isinstance(obj, PyCalendarPeriodValue):
+                        elif isinstance(obj, PeriodValue):
                             if add:
                                 value.addPeriod(obj.getValue().getStart())
                             else:
@@ -573,7 +575,7 @@
 
     def writePropertiesXML(self, node, namespace):
 
-        properties = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.properties))
+        properties = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.properties))
 
         # Sort properties by name
         keys = self.sortedPropertyKeys()
@@ -585,7 +587,7 @@
 
     def writePropertiesFilteredXML(self, node, namespace, filter):
 
-        props = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.properties))
+        props = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.properties))
 
         # Sort properties by name
         keys = self.sortedPropertyKeys()
@@ -598,7 +600,7 @@
         elif filter.hasPropertyFilters():
             for key in keys:
                 for prop in self.getProperties(key):
-                    prop.writeFilteredXML(props, namespace, filter)
+                    prop.writeXMLFiltered(props, namespace, filter)
 
 
     def loadPrivateValue(self, value_name):
@@ -611,7 +613,7 @@
 
 
     def writePrivateProperty(self, os, key, value):
-        prop = PyCalendarProperty(name=key, value=value)
+        prop = self.sPropertyType(name=key, value=value)
         prop.generate(os)
 
 
@@ -622,4 +624,4 @@
 
         # Now create properties
         if propvalue:
-            self.addProperty(PyCalendarProperty(name=propname, value=propvalue))
+            self.addProperty(self.sPropertyType(name=propname, value=propvalue))

Deleted: PyCalendar/branches/json-2/src/pycalendar/componentexpanded.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/componentexpanded.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/componentexpanded.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,158 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.datetime import PyCalendarDateTime
-
-class PyCalendarComponentExpanded(object):
-
-    @staticmethod
-    def sort_by_dtstart_allday(e1, e2):
-
-        if e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly():
-            return e1.mInstanceStart < e2.mInstanceStart
-        elif e1.mInstanceStart.isDateOnly():
-            return True
-        elif e2.mInstanceStart.isDateOnly():
-            return False
-        elif e1.mInstanceStart == e2.mInstanceStart:
-            if e1.mInstanceEnd == e2.mInstanceEnd:
-                # Put ones created earlier in earlier columns in day view
-                return e1.getOwner().getStamp() < e2.getOwner().getStamp()
-            else:
-                # Put ones that end later in earlier columns in day view
-                return e1.mInstanceEnd > e2.mInstanceEnd
-        else:
-            return e1.mInstanceStart < e2.mInstanceStart
-
-
-    @staticmethod
-    def sort_by_dtstart(e1, e2):
-        if e1.mInstanceStart == e2.mInstanceStart:
-            if (e1.mInstanceStart.isDateOnly() and not e2.mInstanceStart.isDateOnly() or
-                not e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly()):
-                return e1.mInstanceStart.isDateOnly()
-            else:
-                return False
-        else:
-            return e1.mInstanceStart < e2.mInstanceStart
-
-
-    def __init__(self, owner, rid):
-
-        self.mOwner = owner
-        self.initFromOwner(rid)
-
-
-    def duplicate(self):
-        other = PyCalendarComponentExpanded(self.mOwner, None)
-        other.mInstanceStart = self.mInstanceStart.duplicate()
-        other.mInstanceEnd = self.mInstanceEnd.duplicate()
-        other.mRecurring = self.mRecurring
-        return other
-
-
-    def close(self):
-        # Clean-up
-        self.mOwner = None
-
-
-    def getOwner(self):
-        return self.mOwner
-
-
-    def getMaster(self):
-        return self.mOwner
-
-
-    def getTrueMaster(self):
-        return self.mOwner.getMaster()
-
-
-    def getInstanceStart(self):
-        return self.mInstanceStart
-
-
-    def getInstanceEnd(self):
-        return self.mInstanceEnd
-
-
-    def recurring(self):
-        return self.mRecurring
-
-
-    def isNow(self):
-        # Check instance start/end against current date-time
-        now = PyCalendarDateTime.getNowUTC()
-        return self.mInstanceStart <= now and self.mInstanceEnd > now
-
-
-    def initFromOwner(self, rid):
-        # There are four possibilities here:
-        #
-        # 1: this instance is the instance for the master component
-        #
-        # 2: this instance is an expanded instance derived directly from the
-        # master component
-        #
-        # 3: This instance is the instance for a slave (overridden recurrence
-        # instance)
-        #
-        # 4: This instance is the expanded instance for a slave with a RANGE
-        # parameter
-        #
-
-        # rid is not set if the owner is the master (case 1)
-        if rid is None:
-            # Just get start/end from owner
-            self.mInstanceStart = self.mOwner.getStart()
-            self.mInstanceEnd = self.mOwner.getEnd()
-            self.mRecurring = False
-
-        # If the owner is not a recurrence instance then it is case 2
-        elif not self.mOwner.isRecurrenceInstance():
-            # Derive start/end from rid and duration of master
-
-            # Start of the recurrence instance is the recurrence id
-            self.mInstanceStart = rid
-
-            # End is based on original events settings
-            if self.mOwner.hasEnd():
-                self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
-            else:
-                self.mInstanceEnd = self.mInstanceStart.duplicate()
-
-            self.mRecurring = True
-
-        # If the owner is a recurrence item and the passed in rid is the same
-        # as the component rid we have case 3
-        elif rid == self.mOwner.getRecurrenceID():
-            # Derive start/end directly from the owner
-            self.mInstanceStart = self.mOwner.getStart()
-            self.mInstanceEnd = self.mOwner.getEnd()
-
-            self.mRecurring = True
-
-        # case 4 - the complicated one!
-        else:
-            # We need to use the rid as the starting point, but adjust it by
-            # the offset between the slave's
-            # rid and its start
-            self.mInstanceStart = rid + (self.mOwner.getStart() - self.mOwner.getRecurrenceID())
-
-            # End is based on duration of owner
-            self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
-
-            self.mRecurring = True

Deleted: PyCalendar/branches/json-2/src/pycalendar/componentrecur.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/componentrecur.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/componentrecur.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,714 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.componentexpanded import PyCalendarComponentExpanded
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.property import PyCalendarProperty
-from pycalendar.recurrenceset import PyCalendarRecurrenceSet
-from pycalendar.timezone import PyCalendarTimezone
-from pycalendar.utils import set_difference
-import uuid
-
-class PyCalendarComponentRecur(PyCalendarComponent):
-
-    propertyCardinality_STATUS_Fix = (
-        definitions.cICalProperty_STATUS,
-    )
-
-    @staticmethod
-    def mapKey(uid, rid=None):
-        if uid:
-            result = "u:" + uid
-            if rid is not None:
-                result += rid
-            return result
-        else:
-            return None
-
-
-    @staticmethod
-    def sort_by_dtstart_allday(e1, e2):
-
-        if e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly():
-            return e1.self.mStart < e2.self.mStart
-        elif e1.self.mStart.isDateOnly():
-            return True
-        elif (e2.self.mStart.isDateOnly()):
-            return False
-        elif e1.self.mStart == e2.self.mStart:
-            if e1.self.mEnd == e2.self.mEnd:
-                # Put ones created earlier in earlier columns in day view
-                return e1.self.mStamp < e2.self.mStamp
-            else:
-                # Put ones that end later in earlier columns in day view
-                return e1.self.mEnd > e2.self.mEnd
-        else:
-            return e1.self.mStart < e2.self.mStart
-
-
-    @staticmethod
-    def sort_by_dtstart(e1, e2):
-        if e1.self.mStart == e2.self.mStart:
-            if (e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly() or
-                not e1.self.mStart.isDateOnly() and not e2.self.mStart.isDateOnly()):
-                return False
-            else:
-                return e1.self.mStart.isDateOnly()
-        else:
-            return e1.self.mStart < e2.self.mStart
-
-
-    def __init__(self, parent=None):
-        super(PyCalendarComponentRecur, self).__init__(parent=parent)
-        self.mMaster = self
-        self.mMapKey = None
-        self.mSummary = None
-        self.mStamp = PyCalendarDateTime()
-        self.mHasStamp = False
-        self.mStart = PyCalendarDateTime()
-        self.mHasStart = False
-        self.mEnd = PyCalendarDateTime()
-        self.mHasEnd = False
-        self.mDuration = False
-        self.mHasRecurrenceID = False
-        self.mAdjustFuture = False
-        self.mAdjustPrior = False
-        self.mRecurrenceID = None
-        self.mRecurrences = None
-
-        # This is a special check we do only for STATUS due to a calendarserver bug
-        self.cardinalityChecks += (
-            self.check_cardinality_STATUS_Fix,
-        )
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarComponentRecur, self).duplicate(parent=parent)
-
-        # Special determination of master
-        other.mMaster = self.mMaster if self.recurring() else self
-
-        other.mMapKey = self.mMapKey
-
-        other.mSummary = self.mSummary
-
-        if (self.mStamp is not None):
-            other.mStamp = self.mStamp.duplicate()
-        other.mHasStamp = self.mHasStamp
-
-        other.mStart = self.mStart.duplicate()
-        other.mHasStart = self.mHasStart
-        other.mEnd = self.mEnd.duplicate()
-        other.mHasEnd = self.mHasEnd
-        other.mDuration = self.mDuration
-
-        other.mHasRecurrenceID = self.mHasRecurrenceID
-        other.mAdjustFuture = self.mAdjustFuture
-        other.mAdjustPrior = self.mAdjustPrior
-        if self.mRecurrenceID is not None:
-            other.mRecurrenceID = self.mRecurrenceID.duplicate()
-
-        other._resetRecurrenceSet()
-
-        return other
-
-
-    def canGenerateInstance(self):
-        return not self.mHasRecurrenceID
-
-
-    def recurring(self):
-        return (self.mMaster is not None) and (self.mMaster is not self)
-
-
-    def setMaster(self, master):
-        self.mMaster = master
-        self.initFromMaster()
-
-
-    def getMaster(self):
-        return self.mMaster
-
-
-    def getMapKey(self):
-
-        if self.mMapKey is None:
-            self.mMapKey = str(uuid.uuid4())
-        return self.mMapKey
-
-
-    def getMasterKey(self):
-        return PyCalendarComponentRecur.mapKey(self.mUID)
-
-
-    def initDTSTAMP(self):
-        # Save new one
-        super(PyCalendarComponentRecur, self).initDTSTAMP()
-
-        # Get the new one
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
-        self.mHasStamp = temp is not None
-        if self.mHasStamp:
-            self.mStamp = temp
-
-
-    def getStamp(self):
-        return self.mStamp
-
-
-    def hasStamp(self):
-        return self.mHasStamp
-
-
-    def getStart(self):
-        return self.mStart
-
-
-    def hasStart(self):
-        return self.mHasStart
-
-
-    def getEnd(self):
-        return self.mEnd
-
-
-    def hasEnd(self):
-        return self.mHasEnd
-
-
-    def useDuration(self):
-        return self.mDuration
-
-
-    def isRecurrenceInstance(self):
-        return self.mHasRecurrenceID
-
-
-    def isAdjustFuture(self):
-        return self.mAdjustFuture
-
-
-    def isAdjustPrior(self):
-        return self.mAdjustPrior
-
-
-    def getRecurrenceID(self):
-        return self.mRecurrenceID
-
-
-    def isRecurring(self):
-        return (self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
-
-
-    def getRecurrenceSet(self):
-        return self.mRecurrences
-
-
-    def setUID(self, uid):
-        super(PyCalendarComponentRecur, self).setUID(uid)
-
-        # Update the map key
-        if self.mHasRecurrenceID:
-            self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
-        else:
-            self.mMapKey = self.mapKey(self.mUID)
-
-
-    def getSummary(self):
-        return self.mSummary
-
-
-    def setSummary(self, summary):
-        self.mSummary = summary
-
-
-    def getDescription(self):
-        # Get DESCRIPTION
-        txt = self.loadValueString(definitions.cICalProperty_DESCRIPTION)
-        if txt is not None:
-            return txt
-        else:
-            return ""
-
-
-    def getLocation(self):
-        # Get LOCATION
-        txt = self.loadValueString(definitions.cICalProperty_LOCATION)
-        if txt is not None:
-            return txt
-        else:
-            return ""
-
-
-    def finalise(self):
-        super(PyCalendarComponentRecur, self).finalise()
-
-        # Get DTSTAMP
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
-        self.mHasStamp = temp is not None
-        if self.mHasStamp:
-            self.mStamp = temp
-
-        # Get DTSTART
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
-        self.mHasStart = temp is not None
-        if self.mHasStart:
-            self.mStart = temp
-
-        # Get DTEND
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
-        if temp is None:
-            # Try DURATION instead
-            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
-            if temp is not None:
-                self.mHasEnd = False
-                self.mEnd = self.mStart + temp
-                self.mDuration = True
-            else:
-                # If no end or duration then use the start
-                self.mHasEnd = False
-                self.mEnd = self.mStart.duplicate()
-                self.mDuration = False
-        else:
-            self.mHasEnd = True
-            self.mEnd = temp
-            self.mDuration = False
-
-        # Get SUMMARY
-        temp = self.loadValueString(definitions.cICalProperty_SUMMARY)
-        if temp is not None:
-            self.mSummary = temp
-
-        # Get RECURRENCE-ID
-        self.mHasRecurrenceID = (self.countProperty(definitions.cICalProperty_RECURRENCE_ID) != 0)
-        if self.mHasRecurrenceID:
-            self.mRecurrenceID = self.loadValueDateTime(definitions.cICalProperty_RECURRENCE_ID)
-
-        # Update the map key
-        if self.mHasRecurrenceID:
-            self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
-
-            # Also get the RANGE attribute
-            attrs = self.findFirstProperty(definitions.cICalProperty_RECURRENCE_ID).getAttributes()
-            if definitions.cICalAttribute_RANGE in attrs:
-                self.mAdjustFuture = (attrs[definitions.cICalAttribute_RANGE][0].getFirstValue() == definitions.cICalAttribute_RANGE_THISANDFUTURE)
-                self.mAdjustPrior = (attrs[definitions.cICalAttribute_RANGE][0].getFirstValue() == definitions.cICalAttribute_RANGE_THISANDPRIOR)
-            else:
-                self.mAdjustFuture = False
-                self.mAdjustPrior = False
-        else:
-            self.mMapKey = self.mapKey(self.mUID)
-
-        self._resetRecurrenceSet()
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems. Return
-        a tuple containing two lists: the first describes problems that were fixed, the
-        second problems that were not fixed. Caller can then decide what to do with unfixed
-        issues.
-        """
-
-        # Do normal checks
-        fixed, unfixed = super(PyCalendarComponentRecur, self).validate(doFix)
-
-        # Check that any UNTIL value matches that for DTSTART
-        if self.mHasStart and self.mRecurrences:
-            dtutc = self.mStart.duplicateAsUTC()
-            for rrule in self.mRecurrences.getRules():
-                if rrule.getUseUntil():
-                    if rrule.getUntil().isDateOnly() ^ self.mStart.isDateOnly():
-                        logProblem = "[%s] Value types must match: %s, %s" % (
-                            self.getType(),
-                            definitions.cICalProperty_DTSTART,
-                            definitions.cICalValue_RECUR_UNTIL,
-                        )
-                        if doFix:
-                            rrule.getUntil().setDateOnly(self.mStart.isDateOnly())
-                            if not self.mStart.isDateOnly():
-                                rrule.getUntil().setHHMMSS(dtutc.getHours(), dtutc.getMinutes(), dtutc.getSeconds())
-                                rrule.getUntil().setTimezone(PyCalendarTimezone(utc=True))
-                            self.mRecurrences.changed()
-                            fixed.append(logProblem)
-                        else:
-                            unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    def check_cardinality_STATUS_Fix(self, fixed, unfixed, doFix):
-        """
-        Special for bug with STATUS where STATUS:CANCELLED is added alongside
-        another STATUS. In this case we want STATUS:CANCELLED to win.
-        """
-        for propname in self.propertyCardinality_STATUS_Fix:
-            if self.countProperty(propname) > 1:
-                logProblem = "[%s] Too many properties: %s" % (self.getType(), propname)
-                if doFix:
-                    # Check that one of them is STATUS:CANCELLED
-                    for prop in self.getProperties(propname):
-                        if prop.getTextValue().getValue().upper() == definitions.cICalProperty_STATUS_CANCELLED:
-                            self.removeProperties(propname)
-                            self.addProperty(PyCalendarProperty(propname, definitions.cICalProperty_STATUS_CANCELLED))
-                            fixed.append(logProblem)
-                            break
-                    else:
-                        unfixed.append(logProblem)
-                else:
-                    unfixed.append(logProblem)
-
-
-    def _resetRecurrenceSet(self):
-        # May need to create items
-        self.mRecurrences = None
-        if ((self.countProperty(definitions.cICalProperty_RRULE) != 0) or
-            (self.countProperty(definitions.cICalProperty_RDATE) != 0) or
-            (self.countProperty(definitions.cICalProperty_EXRULE) != 0) or
-            (self.countProperty(definitions.cICalProperty_EXDATE) != 0)):
-
-            self.mRecurrences = PyCalendarRecurrenceSet()
-
-            # Get RRULEs
-            self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
-
-            # Get RDATEs
-            self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
-
-            # Get EXRULEs
-            self.loadValueRRULE(definitions.cICalProperty_EXRULE, self.mRecurrences, False)
-
-            # Get EXDATEs
-            self.loadValueRDATE(definitions.cICalProperty_EXDATE, self.mRecurrences, False)
-
-
-    def FixStartEnd(self):
-        # End is always greater than start if start exists
-        if self.mHasStart and self.mEnd <= self.mStart:
-            # Use the start
-            self.mEnd = self.mStart.duplicate()
-            self.mDuration = False
-
-            # Adjust to approriate non-inclusive end point
-            if self.mStart.isDateOnly():
-                self.mEnd.offsetDay(1)
-
-                # For all day events it makes sense to use duration
-                self.mDuration = True
-            else:
-                # Use end of current day
-                self.mEnd.offsetDay(1)
-                self.mEnd.setHHMMSS(0, 0, 0)
-
-
-    def expandPeriod(self, period, results):
-        # Check for recurrence and True master
-        if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
-                and not self.isRecurrenceInstance()):
-            # Expand recurrences within the range
-            items = []
-            self.mRecurrences.expand(self.mStart, period, items)
-
-            # Look for overridden recurrence items
-            cal = self.mParentComponent
-            if cal is not None:
-                # Remove recurrence instances from the list of items
-                recurs = []
-                cal.getRecurrenceInstancesIds(definitions.cICalComponent_VEVENT, self.getUID(), recurs)
-                recurs.sort()
-                if len(recurs) != 0:
-                    temp = []
-                    temp = set_difference(items, recurs)
-                    items = temp
-
-                    # Now get actual instances
-                    instances = []
-                    cal.getRecurrenceInstancesItems(definitions.cICalComponent_VEVENT, self.getUID(), instances)
-
-                    # Get list of each ones with RANGE
-                    prior = []
-                    future = []
-                    for iter in instances:
-                        if iter.isAdjustPrior():
-                            prior.append(iter)
-                        if iter.isAdjustFuture():
-                            future.append(iter)
-
-                    # Check for special behaviour
-                    if len(prior) + len(future) == 0:
-                        # Add each expanded item
-                        for iter in items:
-                            results.append(self.createExpanded(self, iter))
-                    else:
-                        # Sort each list first
-                        prior.sort(self.sort_by_dtstart)
-                        future.sort(self.sort_by_dtstart)
-
-                        # Add each expanded item
-                        for iter1 in items:
-
-                            # Now step through each using the slave item
-                            # instead of the master as appropriate
-                            slave = None
-
-                            # Find most appropriate THISANDPRIOR item
-                            for i in range(len(prior) - 1, 0, -1):
-                                riter2 = prior[i]
-                                if riter2.getStart() > iter1:
-                                    slave = riter2
-                                    break
-
-                            # Find most appropriate THISANDFUTURE item
-                            for i in range(len(future) - 1, 0, -1):
-                                riter2 = future.elementAt(i)
-                                if riter2.getStart() < iter1:
-                                    slave = riter2
-                                    break
-
-                            if slave is None:
-                                slave = self
-                            results.append(self.createExpanded(slave, iter1))
-                else:
-                    # Add each expanded item
-                    for iter in items:
-                        results.append(self.createExpanded(self, iter))
-
-        elif self.withinPeriod(period):
-            if self.isRecurrenceInstance():
-                rid = self.mRecurrenceID
-            else:
-                rid = None
-            results.append(PyCalendarComponentExpanded(self, rid))
-
-
-    def withinPeriod(self, period):
-        # Check for recurrence
-        if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()):
-            items = []
-            self.mRecurrences.expand(self.mStart, period, items)
-            return len(items) != 0
-        else:
-            # Does event span the period (assume self.mEnd > self.mStart)
-            # Check start (inclusive) and end (exclusive)
-            if self.mEnd <= period.getStart() or self.mStart >= period.getEnd():
-                return False
-            else:
-                return True
-
-
-    def changedRecurrence(self):
-        # Clear cached values
-        if self.mRecurrences is not None:
-            self.mRecurrences.changed()
-
-
-    # Editing
-    def editSummary(self, summary):
-        # Updated cached value
-        self.mSummary = summary
-
-        # Remove existing items
-        self.editProperty(definitions.cICalProperty_SUMMARY, summary)
-
-
-    def editDetails(self, description, location):
-
-        # Edit existing items
-        self.editProperty(definitions.cICalProperty_DESCRIPTION, description)
-        self.editProperty(definitions.cICalProperty_LOCATION, location)
-
-
-    def editTiming(self):
-        # Updated cached values
-        self.mHasStart = False
-        self.mHasEnd = False
-        self.mDuration = False
-        self.mStart.setToday()
-        self.mEnd.setToday()
-
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-        self.removeProperties(definitions.cICalProperty_DUE)
-
-
-    def editTimingDue(self, due):
-        # Updated cached values
-        self.mHasStart = False
-        self.mHasEnd = True
-        self.mDuration = False
-        self.mStart = due
-        self.mEnd = due
-
-        # Remove existing DUE & DTSTART & DTEND & DURATION items
-        self.removeProperties(definitions.cICalProperty_DUE)
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-
-        # Now create properties
-        prop = PyCalendarProperty(definitions.cICalProperty_DUE, due)
-        self.addProperty(prop)
-
-
-    def editTimingStartEnd(self, start, end):
-        # Updated cached values
-        self.mHasStart = self.mHasEnd = True
-        self.mStart = start
-        self.mEnd = end
-        self.mDuration = False
-        self.FixStartEnd()
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-        self.removeProperties(definitions.cICalProperty_DUE)
-
-        # Now create properties
-        prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
-        self.addProperty(prop)
-
-        # If its an all day event and the end one day after the start, ignore it
-        temp = start.duplicate()
-        temp.offsetDay(1)
-        if not start.isDateOnly() or end != temp:
-            prop = PyCalendarProperty(definitions.cICalProperty_DTEND, end)
-            self.addProperty(prop)
-
-
-    def editTimingStartDuration(self, start, duration):
-        # Updated cached values
-        self.mHasStart = True
-        self.mHasEnd = False
-        self.mStart = start
-        self.mEnd = start + duration
-        self.mDuration = True
-
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-        self.removeProperties(definitions.cICalProperty_DUE)
-
-        # Now create properties
-        prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
-        self.addProperty(prop)
-
-        # If its an all day event and the duration is one day, ignore it
-        if (not start.isDateOnly() or (duration.getWeeks() != 0)
-                or (duration.getDays() > 1)):
-            prop = PyCalendarProperty(definitions.cICalProperty_DURATION, duration)
-            self.addProperty(prop)
-
-
-    def editRecurrenceSet(self, recurs):
-        # Must have items
-        if self.mRecurrences is None:
-            self.mRecurrences = PyCalendarRecurrenceSet()
-
-        # Updated cached values
-        self.mRecurrences = recurs
-
-        # Remove existing RRULE, EXRULE, RDATE & EXDATE
-        self.removeProperties(definitions.cICalProperty_RRULE)
-        self.removeProperties(definitions.cICalProperty_EXRULE)
-        self.removeProperties(definitions.cICalProperty_RDATE)
-        self.removeProperties(definitions.cICalProperty_EXDATE)
-
-        # Now create properties
-        for iter in self.mRecurrences.getRules():
-            prop = PyCalendarProperty(definitions.cICalProperty_RRULE, iter)
-            self.addProperty(prop)
-        for iter in self.getExrules():
-            prop = PyCalendarProperty(definitions.cICalProperty_EXRULE, iter)
-            self.addProperty(prop)
-        for iter in self.mRecurrences.getDates():
-            prop = PyCalendarProperty(definitions.cICalProperty_RDATE, iter)
-            self.addProperty(prop)
-        for iter in self.mRecurrences.getExdates():
-            prop = PyCalendarProperty(definitions.cICalProperty_EXDATE, iter)
-            self.addProperty(prop)
-
-
-    def excludeRecurrence(self, start):
-        # Must have items
-        if self.mRecurrences is None:
-            return
-
-        # Add to recurrence set and clear cache
-        self.mRecurrences.subtract(start)
-
-        # Add property
-        prop = PyCalendarProperty(definitions.cICalProperty_EXDATE, start)
-        self.addProperty(prop)
-
-
-    def excludeFutureRecurrence(self, start):
-        # Must have items
-        if self.mRecurrences is None:
-            return
-
-        # Adjust RRULES to end before start
-        self.mRecurrences.excludeFutureRecurrence(start)
-
-        # Remove existing RRULE & RDATE
-        self.removeProperties(definitions.cICalProperty_RRULE)
-        self.removeProperties(definitions.cICalProperty_RDATE)
-
-        # Now create properties
-        for iter in self.mRecurrences.getRules():
-            prop = PyCalendarProperty(definitions.cICalProperty_RRULE, iter)
-            self.addProperty(prop)
-        for iter in  self.mRecurrences.getDates():
-            prop = PyCalendarProperty(definitions.cICalProperty_RDATE, iter)
-            self.addProperty(prop)
-
-
-    def initFromMaster(self):
-        # Only if not master
-        if self.recurring():
-            # Redo this to get cached values from master
-            self.finalise()
-
-            # If this component does not have its own start property, use the
-            # recurrence id
-            # i.e. the start time of this instance has not changed - something
-            # else has
-            if not self.hasProperty(definitions.cICalProperty_DTSTART):
-                self.mStart = self.mRecurrenceID
-
-            # If this component does not have its own end/duration property,
-            # the determine
-            # the end from the master duration
-            if (not self.hasProperty(definitions.cICalProperty_DTEND) and
-                not self.hasProperty(definitions.cICalProperty_DURATION)):
-                # End is based on original events settings
-                self.mEnd = self.mStart + (self.mMaster.getEnd() - self.mMaster.getStart())
-
-            # If this instance has a duration, but no start of its own, then we
-            # need to readjust the end
-            # to account for the start being changed to the recurrence id
-            elif (self.hasProperty(definitions.cICalProperty_DURATION) and
-                  not self.hasProperty(definitions.cICalProperty_DTSTART)):
-                temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
-                self.mEnd = self.mStart + temp
-
-
-    def createExpanded(self, master, recurid):
-        return PyCalendarComponentExpanded(master, recurid)

Modified: PyCalendar/branches/json-2/src/pycalendar/datetime.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/datetime.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/datetime.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,18 +14,17 @@
 #    limitations under the License.
 ##
 
-from pycalendar import definitions
-from pycalendar import locale
+from pycalendar import locale, xmldefinitions, xmlutils
 from pycalendar import utils
-from pycalendar import xmldefs
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.duration import Duration
+from pycalendar.icalendar import definitions
+from pycalendar.timezone import Timezone
 from pycalendar.valueutils import ValueMixin
 import cStringIO as StringIO
 import time
 import xml.etree.cElementTree as XML
 
-class PyCalendarDateTime(ValueMixin):
+class DateTime(ValueMixin):
 
     SUNDAY = 0
     MONDAY = 1
@@ -83,7 +82,7 @@
 
 
     def duplicate(self):
-        other = PyCalendarDateTime(self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
+        other = DateTime(self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
 
         other.mDateOnly = self.mDateOnly
 
@@ -104,7 +103,7 @@
 
 
     def __repr__(self):
-        return "PyCalendarDateTime: %s" % (self.getText(),)
+        return "DateTime: %s" % (self.getText(),)
 
 
     def __hash__(self):
@@ -122,7 +121,7 @@
 
     def __sub__(self, dateorduration):
 
-        if isinstance(dateorduration, PyCalendarDateTime):
+        if isinstance(dateorduration, DateTime):
 
             date = dateorduration
 
@@ -151,9 +150,9 @@
             else:
                 # Do diff of date-time in seconds
                 diff = self.getPosixTime() - date.getPosixTime()
-                return PyCalendarDuration(duration=diff)
+                return Duration(duration=diff)
 
-        elif isinstance(dateorduration, PyCalendarDuration):
+        elif isinstance(dateorduration, Duration):
 
             duration = dateorduration
             result = self.duplicate()
@@ -216,7 +215,7 @@
 
         # If they have the same timezone do simple compare - no posix calc
         # needed
-        elif (PyCalendarTimezone.same(self.mTZUTC, self.mTZID, comp.mTZUTC, comp.mTZID)):
+        elif (Timezone.same(self.mTZUTC, self.mTZID, comp.mTZUTC, comp.mTZID)):
             if self.mYear == comp.mYear:
                 if self.mMonth == comp.mMonth:
                     if self.mDay == comp.mDay:
@@ -426,14 +425,14 @@
             return
 
         # What day does the current year start on, and diff that with the current day
-        temp = PyCalendarDateTime(year=self.mYear, month=1, day=1)
+        temp = DateTime(year=self.mYear, month=1, day=1)
         first_day = temp.getDayOfWeek()
         current_day = self.getDayOfWeek()
 
         # Calculate and set yearday for start of week. The first week is the one that contains at least
         # four days (with week start defaulting to MONDAY), so that means the 1st of January would fall
         # on MO, TU, WE, TH.
-        if first_day in (PyCalendarDateTime.MONDAY, PyCalendarDateTime.TUESDAY, PyCalendarDateTime.WEDNESDAY, PyCalendarDateTime.THURSDAY):
+        if first_day in (DateTime.MONDAY, DateTime.TUESDAY, DateTime.WEDNESDAY, DateTime.THURSDAY):
             year_day = (weekno - 1) * 7 + current_day - first_day
         else:
             year_day = weekno * 7 + current_day - first_day
@@ -453,7 +452,7 @@
         """
 
         # What day does the current year start on
-        temp = PyCalendarDateTime(year=self.mYear, month=1, day=1)
+        temp = DateTime(year=self.mYear, month=1, day=1)
         first_day = temp.getDayOfWeek()
         if first_day == 0:
             first_day = 7
@@ -467,13 +466,13 @@
         # Might need to adjust forward/backwards based on year boundaries
         if week_no == 0:
             # Last week of previous year
-            temp = PyCalendarDateTime(year=self.mYear - 1, month=12, day=31)
+            temp = DateTime(year=self.mYear - 1, month=12, day=31)
             week_no = temp.getWeekNo()
         elif week_no == 53:
             # Might be first week of next year
-            temp = PyCalendarDateTime(year=self.mYear + 1, month=1, day=1)
+            temp = DateTime(year=self.mYear + 1, month=1, day=1)
             first_day = temp.getDayOfWeek()
-            if first_day in (PyCalendarDateTime.MONDAY, PyCalendarDateTime.TUESDAY, PyCalendarDateTime.WEDNESDAY, PyCalendarDateTime.THURSDAY):
+            if first_day in (DateTime.MONDAY, DateTime.TUESDAY, DateTime.WEDNESDAY, DateTime.THURSDAY):
                 week_no = 1
 
         return week_no
@@ -579,7 +578,7 @@
 
     def getDayOfWeek(self):
         # Count days since 01-Jan-1970 which was a Thursday
-        result = PyCalendarDateTime.THURSDAY + self.daysSince1970()
+        result = DateTime.THURSDAY + self.daysSince1970()
         result %= 7
         if result < 0:
             result += 7
@@ -686,7 +685,7 @@
 
 
     def getTimezone(self):
-        return PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
+        return Timezone(utc=self.mTZUTC, tzid=self.mTZID)
 
 
     def setTimezone(self, tzid):
@@ -711,7 +710,7 @@
             # Cache and restore and adjust the posix value to avoid a recalc since it won't change during this adjust
             tempPosix = self.mPosixTime if self.mPosixTimeCached else None
 
-            utc = PyCalendarTimezone(utc=True)
+            utc = Timezone(utc=True)
 
             offset_from = self.timeZoneSecondsOffset()
             self.setTimezone(utc)
@@ -735,7 +734,7 @@
 
 
     def setToday(self):
-        tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
+        tz = Timezone(utc=self.mTZUTC, tzid=self.mTZID)
         self.copy_ICalendarDateTime(self.getToday(tz))
 
 
@@ -745,24 +744,24 @@
         now = time.time()
         now_tm = time.localtime(now)
 
-        temp = PyCalendarDateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, tzid=tzid)
+        temp = DateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, tzid=tzid)
         return temp
 
 
     def setNow(self):
-        tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
+        tz = Timezone(utc=self.mTZUTC, tzid=self.mTZID)
         self.copy_ICalendarDateTime(self.getNow(tz))
 
 
     @staticmethod
     def getNow(tzid):
-        utc = PyCalendarDateTime.getNowUTC()
-        utc.adjustTimezone(tzid if tzid is not None else PyCalendarTimezone())
+        utc = DateTime.getNowUTC()
+        utc.adjustTimezone(tzid if tzid is not None else Timezone())
         return utc
 
 
     def setNowUTC(self):
-        self.copy_PyCalendarDateTime(self.getNowUTC())
+        self.copy_DateTime(self.getNowUTC())
 
 
     @staticmethod
@@ -770,9 +769,9 @@
         # Get from posix time
         now = time.time()
         now_tm = time.gmtime(now)
-        tzid = PyCalendarTimezone(utc=True)
+        tzid = Timezone(utc=True)
 
-        return PyCalendarDateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, hours=now_tm.tm_hour, minutes=now_tm.tm_min, seconds=now_tm.tm_sec, tzid=tzid)
+        return DateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, hours=now_tm.tm_hour, minutes=now_tm.tm_min, seconds=now_tm.tm_sec, tzid=tzid)
 
 
     def recur(self, freq, interval):
@@ -809,7 +808,7 @@
 
         buf = StringIO.StringIO()
 
-        if locale == PyCalendarDateTime.FULLDATE:
+        if locale == DateTime.FULLDATE:
             buf.write(locale.getDay(self.getDayOfWeek(), locale.LONG))
             buf.write(", ")
             buf.write(locale.getMonth(self.mMonth, locale.LONG))
@@ -817,7 +816,7 @@
             buf.write(str(self.mDay))
             buf.write(", ")
             buf.write(str(self.mYear))
-        elif locale == PyCalendarDateTime.ABBREVDATE:
+        elif locale == DateTime.ABBREVDATE:
             buf.write(locale.getDay(self.getDayOfWeek(), locale.SHORT))
             buf.write(", ")
             buf.write(locale.getMonth(self.mMonth, locale.SHORT))
@@ -825,25 +824,25 @@
             buf.write(str(self.mDay))
             buf.write(", ")
             buf.write(str(self.mYear))
-        elif locale == PyCalendarDateTime.NUMERICDATE:
+        elif locale == DateTime.NUMERICDATE:
             buf.write(str(self.mMonth))
             buf.write("/")
             buf.write(str(self.mDay))
             buf.write("/")
             buf.write(str(self.mYear))
-        elif locale == PyCalendarDateTime.FULLDATENOYEAR:
+        elif locale == DateTime.FULLDATENOYEAR:
             buf.write(locale.getDay(self.getDayOfWeek(), locale.LONG))
             buf.write(", ")
             buf.write(locale.getMonth(self.mMonth, locale.LONG))
             buf.write(" ")
             buf.write(str(self.mDay))
-        elif locale == PyCalendarDateTime.ABBREVDATENOYEAR:
+        elif locale == DateTime.ABBREVDATENOYEAR:
             buf.write(locale.getDay(self. getDayOfWeek(), locale.SHORT))
             buf.write(", ")
             buf.write(locale.getMonth(self.mMonth, locale.SHORT))
             buf.write(" ")
             buf.write(str(self.mDay))
-        elif locale == PyCalendarDateTime.NUMERICDATENOYEAR:
+        elif locale == DateTime.NUMERICDATENOYEAR:
             buf.write(str(self.mMonth))
             buf.write("/")
             buf.write(str(self.mDay))
@@ -1086,9 +1085,9 @@
     def writeXML(self, node, namespace):
         value = XML.SubElement(
             node,
-            xmldefs.makeTag(
+            xmlutils.makeTag(
                 namespace,
-                xmldefs.value_date if self.isDateOnly() else xmldefs.value_date_time
+                xmldefinitions.value_date if self.isDateOnly() else xmldefinitions.value_date_time
         ))
         value.text = self.getXMLText()
 
@@ -1160,13 +1159,13 @@
         if self.mTZUTC:
             return 0
         if self.mTZOffset is None:
-            tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
+            tz = Timezone(utc=self.mTZUTC, tzid=self.mTZID)
             self.mTZOffset = tz.timeZoneSecondsOffset(self)
         return self.mTZOffset
 
 
     def timeZoneDescriptor(self):
-        tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
+        tz = Timezone(utc=self.mTZUTC, tzid=self.mTZID)
         return tz.timeZoneDescriptor(self)
 
 

Modified: PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,42 +14,27 @@
 #    limitations under the License.
 ##
 
-from pycalendar import xmldefs
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.datetime import DateTime
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
 
-class PyCalendarDateTimeValue(PyCalendarValue):
+class DateTimeValue(WrapperValue, Value):
 
     def __init__(self, value=None):
-        self.mValue = value if value is not None else PyCalendarDateTime()
+        self.mValue = value if value is not None else DateTime()
 
 
-    def duplicate(self):
-        return PyCalendarDateTimeValue(self.mValue.duplicate())
-
-
     def getType(self):
-        return  (PyCalendarValue.VALUETYPE_DATETIME, PyCalendarValue.VALUETYPE_DATE)[self.mValue.isDateOnly()]
+        return  (Value.VALUETYPE_DATETIME, Value.VALUETYPE_DATE)[self.mValue.isDateOnly()]
 
 
-    def parse(self, data, fullISO=False):
-        self.mValue.parse(data, fullISO)
+    def parse(self, data, variant):
+        self.mValue.parse(data, fullISO=(variant == "vcard"))
 
 
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
     def writeXML(self, node, namespace):
         self.mValue.writeXML(node, namespace)
 
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DATE, PyCalendarDateTimeValue, xmldefs.value_date)
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DATETIME, PyCalendarDateTimeValue, xmldefs.value_date_time)
+Value.registerType(Value.VALUETYPE_DATE, DateTimeValue, xmldefinitions.value_date)
+Value.registerType(Value.VALUETYPE_DATETIME, DateTimeValue, xmldefinitions.value_date_time)

Deleted: PyCalendar/branches/json-2/src/pycalendar/definitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/definitions.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/definitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,349 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-#     5545 Components
-
-cICalComponent_VCALENDAR = "VCALENDAR"
-cICalComponent_VEVENT = "VEVENT"
-cICalComponent_VTODO = "VTODO"
-cICalComponent_VJOURNAL = "VJOURNAL"
-cICalComponent_VFREEBUSY = "VFREEBUSY"
-cICalComponent_VTIMEZONE = "VTIMEZONE"
-cICalComponent_VALARM = "VALARM"
-cICalComponent_STANDARD = "STANDARD"
-cICalComponent_DAYLIGHT = "DAYLIGHT"
-
-#     5545 Calendar Property Attributes
-
-#     5545 Section 3.2
-cICalAttribute_ALTREP = "ALTREP"
-cICalAttribute_CN = "CN"
-cICalAttribute_CUTYPE = "CUTYPE"
-cICalAttribute_DELEGATED_FROM = "DELEGATED-FROM"
-cICalAttribute_DELEGATED_TO = "DELEGATED-TO"
-cICalAttribute_DIR = "DIR"
-cICalAttribute_ENCODING = "ENCODING"
-cICalAttribute_FMTTYPE = "FMTTYPE"
-cICalAttribute_FBTYPE = "FBTYPE"
-cICalAttribute_LANGUAGE = "LANGUAGE"
-cICalAttribute_MEMBER = "MEMBER"
-cICalAttribute_PARTSTAT = "PARTSTAT"
-cICalAttribute_RANGE = "RANGE"
-cICalAttribute_RELATED = "RELATED"
-cICalAttribute_RELTYPE = "RELTYPE"
-cICalAttribute_ROLE = "ROLE"
-cICalAttribute_RSVP = "RSVP"
-cICalAttribute_RSVP_TRUE = "TRUE"
-cICalAttribute_RSVP_FALSE = "FALSE"
-cICalAttribute_SENT_BY = "SENT-BY"
-cICalAttribute_TZID = "TZID"
-cICalAttribute_VALUE = "VALUE"
-
-#     5545 Section 3.2.9
-cICalAttribute_FBTYPE_FREE = "FREE"
-cICalAttribute_FBTYPE_BUSY = "BUSY"
-cICalAttribute_FBTYPE_BUSYUNAVAILABLE = "BUSY-UNAVAILABLE"
-cICalAttribute_FBTYPE_BUSYTENTATIVE = "BUSY-TENTATIVE"
-
-#     5545 Section 3.2.12
-ePartStat_NeedsAction = 0
-ePartStat_Accepted = 1
-ePartStat_Declined = 2
-ePartStat_Tentative = 3
-ePartStat_Delegated = 4
-ePartStat_Completed = 5
-ePartStat_InProcess = 6
-
-cICalAttribute_PARTSTAT_NEEDSACTION = "NEEDS-ACTION"
-cICalAttribute_PARTSTAT_ACCEPTED = "ACCEPTED"
-cICalAttribute_PARTSTAT_DECLINED = "DECLINED"
-cICalAttribute_PARTSTAT_TENTATIVE = "TENTATIVE"
-cICalAttribute_PARTSTAT_DELEGATED = "DELEGATED"
-cICalAttribute_PARTSTAT_COMPLETED = "COMPLETE"
-cICalAttribute_PARTSTAT_INPROCESS = "IN-PROCESS"
-
-#     5545 Section 3.2.13
-cICalAttribute_RANGE_THISANDFUTURE = "THISANDFUTURE"
-cICalAttribute_RANGE_THISANDPRIOR = "THISANDPRIOR"      # 2445 only
-
-#     5545 Section 3.2.14
-cICalAttribute_RELATED_START = "START"
-cICalAttribute_RELATED_END = "END"
-
-#     5545 Section 3.2.16
-ePartRole_Chair = 0
-ePartRole_Required = 1
-ePartRole_Optional = 2
-ePartRole_Non = 3
-
-cICalAttribute_ROLE_CHAIR = "CHAIR"
-cICalAttribute_ROLE_REQ_PART = "REQ-PARTICIPANT"
-cICalAttribute_ROLE_OPT_PART = "OPT-PARTICIPANT"
-cICalAttribute_ROLE_NON_PART = "NON-PARTICIPANT"
-
-#     5545 Section 3.2.3
-eCutype_Individual = 0
-eCutype_Group = 1
-eCutype_Resource = 2
-eCutype_Room = 3
-eCutype_Unknown = 4
-
-cICalAttribute_CUTYPE_INDIVIDUAL = "INDIVIDUAL"
-cICalAttribute_CUTYPE_GROUP = "GROUP"
-cICalAttribute_CUTYPE_RESOURCE = "RESOURCE"
-cICalAttribute_CUTYPE_ROOM = "ROOM"
-cICalAttribute_CUTYPE_UNKNOWN = "UNKNOWN"
-
-#     5545 Value types
-
-#     5545 Section 3.3
-cICalValue_BINARY = "BINARY"
-cICalValue_BOOLEAN = "BOOLEAN"
-cICalValue_CAL_ADDRESS = "CAL-ADDRESS"
-cICalValue_DATE = "DATE"
-cICalValue_DATE_TIME = "DATE-TIME"
-cICalValue_DURATION = "DURATION"
-cICalValue_FLOAT = "FLOAT"
-cICalValue_INTEGER = "INTEGER"
-cICalValue_PERIOD = "PERIOD"
-cICalValue_RECUR = "RECUR"
-cICalValue_TEXT = "TEXT"
-cICalValue_TIME = "TIME"
-cICalValue_URI = "URI"
-cICalValue_UTC_OFFSET = "UTC-OFFSET"
-
-#     5545 Calendar Properties
-
-#     5545 Section  3.7
-
-cICalProperty_CALSCALE = "CALSCALE"
-cICalProperty_METHOD = "METHOD"
-cICalProperty_PRODID = "PRODID"
-cICalProperty_VERSION = "VERSION"
-
-#     Apple Extensions
-cICalProperty_XWRCALNAME = "X-WR-CALNAME"
-cICalProperty_XWRCALDESC = "X-WR-CALDESC"
-cICalProperty_XWRALARMUID = "X-WR-ALARMUID"
-
-#     5545 Component Property names
-
-#     5545 Section 3.8.1
-cICalProperty_ATTACH = "ATTACH"
-cICalProperty_CATEGORIES = "CATEGORIES"
-cICalProperty_CLASS = "CLASS"
-cICalProperty_COMMENT = "COMMENT"
-cICalProperty_DESCRIPTION = "DESCRIPTION"
-cICalProperty_GEO = "GEO"
-cICalProperty_LOCATION = "LOCATION"
-cICalProperty_PERCENT_COMPLETE = "PERCENT-COMPLETE"
-cICalProperty_PRIORITY = "PRIORITY"
-cICalProperty_RESOURCES = "RESOURCES"
-cICalProperty_STATUS = "STATUS"
-cICalProperty_SUMMARY = "SUMMARY"
-
-#     5545 Section 3.8.2
-cICalProperty_COMPLETED = "COMPLETED"
-cICalProperty_DTEND = "DTEND"
-cICalProperty_DUE = "DUE"
-cICalProperty_DTSTART = "DTSTART"
-cICalProperty_DURATION = "DURATION"
-cICalProperty_FREEBUSY = "FREEBUSY"
-cICalProperty_TRANSP = "TRANSP"
-cICalProperty_OPAQUE = "OPAQUE"
-cICalProperty_TRANSPARENT = "TRANSPARENT"
-
-#     5545 Section 3.8.3
-cICalProperty_TZID = "TZID"
-cICalProperty_TZNAME = "TZNAME"
-cICalProperty_TZOFFSETFROM = "TZOFFSETFROM"
-cICalProperty_TZOFFSETTO = "TZOFFSETTO"
-cICalProperty_TZURL = "TZURL"
-
-#     5545 Section 3.8.4
-cICalProperty_ATTENDEE = "ATTENDEE"
-cICalProperty_CONTACT = "CONTACT"
-cICalProperty_ORGANIZER = "ORGANIZER"
-cICalProperty_RECURRENCE_ID = "RECURRENCE-ID"
-cICalProperty_RELATED_TO = "RELATED-TO"
-cICalProperty_URL = "URL"
-cICalProperty_UID = "UID"
-
-#     5545 Section 3.8.5
-cICalProperty_EXDATE = "EXDATE"
-cICalProperty_EXRULE = "EXRULE"     # 2445 only
-cICalProperty_RDATE = "RDATE"
-cICalProperty_RRULE = "RRULE"
-
-#     5545 Section 3.8.6
-cICalProperty_ACTION = "ACTION"
-cICalProperty_REPEAT = "REPEAT"
-cICalProperty_TRIGGER = "TRIGGER"
-
-#     5545 Section 3.8.7
-cICalProperty_CREATED = "CREATED"
-cICalProperty_DTSTAMP = "DTSTAMP"
-cICalProperty_LAST_MODIFIED = "LAST-MODIFIED"
-cICalProperty_SEQUENCE = "SEQUENCE"
-
-#     5545 Section 3.8.8.3
-cICalProperty_REQUEST_STATUS = "REQUEST-STATUS"
-
-#     Enums
-#     Use ascending order for sensible sorting
-
-#     5545 Section 3.3.10
-
-eRecurrence_SECONDLY = 0
-eRecurrence_MINUTELY = 1
-eRecurrence_HOURLY = 2
-eRecurrence_DAILY = 3
-eRecurrence_WEEKLY = 4
-eRecurrence_MONTHLY = 5
-eRecurrence_YEARLY = 6
-
-eRecurrence_FREQ = 0
-eRecurrence_UNTIL = 1
-eRecurrence_COUNT = 2
-eRecurrence_INTERVAL = 3
-eRecurrence_BYSECOND = 4
-eRecurrence_BYMINUTE = 5
-eRecurrence_BYHOUR = 6
-eRecurrence_BYDAY = 7
-eRecurrence_BYMONTHDAY = 8
-eRecurrence_BYYEARDAY = 9
-eRecurrence_BYWEEKNO = 10
-eRecurrence_BYMONTH = 11
-eRecurrence_BYSETPOS = 12
-eRecurrence_WKST = 13
-
-cICalValue_RECUR_FREQ = "FREQ"
-cICalValue_RECUR_FREQ_LEN = 5
-
-cICalValue_RECUR_SECONDLY = "SECONDLY"
-cICalValue_RECUR_MINUTELY = "MINUTELY"
-cICalValue_RECUR_HOURLY = "HOURLY"
-cICalValue_RECUR_DAILY = "DAILY"
-cICalValue_RECUR_WEEKLY = "WEEKLY"
-cICalValue_RECUR_MONTHLY = "MONTHLY"
-cICalValue_RECUR_YEARLY = "YEARLY"
-
-cICalValue_RECUR_UNTIL = "UNTIL"
-cICalValue_RECUR_COUNT = "COUNT"
-
-cICalValue_RECUR_INTERVAL = "INTERVAL"
-cICalValue_RECUR_BYSECOND = "BYSECOND"
-cICalValue_RECUR_BYMINUTE = "BYMINUTE"
-cICalValue_RECUR_BYHOUR = "BYHOUR"
-cICalValue_RECUR_BYDAY = "BYDAY"
-cICalValue_RECUR_BYMONTHDAY = "BYMONTHDAY"
-cICalValue_RECUR_BYYEARDAY = "BYYEARDAY"
-cICalValue_RECUR_BYWEEKNO = "BYWEEKNO"
-cICalValue_RECUR_BYMONTH = "BYMONTH"
-cICalValue_RECUR_BYSETPOS = "BYSETPOS"
-cICalValue_RECUR_WKST = "WKST"
-
-eRecurrence_WEEKDAY_SU = 0
-eRecurrence_WEEKDAY_MO = 1
-eRecurrence_WEEKDAY_TU = 2
-eRecurrence_WEEKDAY_WE = 3
-eRecurrence_WEEKDAY_TH = 4
-eRecurrence_WEEKDAY_FR = 5
-eRecurrence_WEEKDAY_SA = 6
-
-cICalValue_RECUR_WEEKDAY_SU = "SU"
-cICalValue_RECUR_WEEKDAY_MO = "MO"
-cICalValue_RECUR_WEEKDAY_TU = "TU"
-cICalValue_RECUR_WEEKDAY_WE = "WE"
-cICalValue_RECUR_WEEKDAY_TH = "TH"
-cICalValue_RECUR_WEEKDAY_FR = "FR"
-cICalValue_RECUR_WEEKDAY_SA = "SA"
-
-#     5545 Section 3.8.1.11
-eStatus_VEvent_None = 0
-eStatus_VEvent_Confirmed = 1
-eStatus_VEvent_Tentative = 2
-eStatus_VEvent_Cancelled = 3
-
-eStatus_VToDo_None = 0
-eStatus_VToDo_NeedsAction = 1
-eStatus_VToDo_InProcess = 2
-eStatus_VToDo_Completed = 3
-eStatus_VToDo_Cancelled = 4
-
-eStatus_VJournal_None = 0
-eStatus_VJournal_Final = 1
-eStatus_VJournal_Draft = 2
-eStatus_VJournal_Cancelled = 3
-
-cICalProperty_STATUS_TENTATIVE = "TENTATIVE"
-cICalProperty_STATUS_CONFIRMED = "CONFIRMED"
-cICalProperty_STATUS_CANCELLED = "CANCELLED"
-cICalProperty_STATUS_NEEDS_ACTION = "NEEDS-ACTION"
-cICalProperty_STATUS_COMPLETED = "COMPLETED"
-cICalProperty_STATUS_IN_PROCESS = "IN-PROCESS"
-cICalProperty_STATUS_DRAFT = "DRAFT"
-cICalProperty_STATUS_FINAL = "FINAL"
-
-#     5545 Section 3.8.6.1
-eAction_VAlarm_Audio = 0
-eAction_VAlarm_Display = 1
-eAction_VAlarm_Email = 2
-eAction_VAlarm_Procedure = 3
-eAction_VAlarm_Unknown = 4
-
-cICalProperty_ACTION_AUDIO = "AUDIO"
-cICalProperty_ACTION_DISPLAY = "DISPLAY"
-cICalProperty_ACTION_EMAIL = "EMAIL"
-cICalProperty_ACTION_PROCEDURE = "PROCEDURE"
-
-#     Extensions: draft-daboo-calendar-availability-02
-
-#     Section 3.1
-cICalComponent_VAVAILABILITY = "VAVAILABILITY"
-cICalComponent_AVAILABLE = "AVAILABLE"
-
-#     Section 3.2
-cICalProperty_BUSYTYPE = "BUSYTYPE"
-
-#     Extensions: draft-daboo-valarm-extensions-03
-
-#     Section 5
-eAction_VAlarm_URI = 5
-cICalProperty_ACTION_URI = "URI"
-
-#     Section 7.1
-cICalProperty_ACKNOWLEDGED = "ACKNOWLEDGED"
-
-eAction_VAlarm_None = 6
-cICalProperty_ACTION_NONE = "NONE"
-
-#     Mulberry extensions
-cICalProperty_ACTION_X_SPEAKTEXT = "X-MULBERRY-SPEAK-TEXT"
-cICalProperty_ALARM_X_LASTTRIGGER = "X-MULBERRY-LAST-TRIGGER"
-
-cICalProperty_ALARM_X_ALARMSTATUS = "X-MULBERRY-ALARM-STATUS"
-
-eAlarm_Status_Pending = 0
-eAlarm_Status_Completed = 1
-eAlarm_Status_Disabled = 2
-
-cICalProperty_ALARM_X_ALARMSTATUS_PENDING = "PENDING"
-cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED = "COMPLETED"
-cICalProperty_ALARM_X_ALARMSTATUS_DISABLED = "DISABLED"
-
-cICalAttribute_ORGANIZER_X_IDENTITY = "X-MULBERRY-IDENTITY"
-cICalAttribute_ATTENDEE_X_NEEDS_ITIP = "X-MULBERRY-NEEDS-ITIP"

Modified: PyCalendar/branches/json-2/src/pycalendar/duration.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/duration.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/duration.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -18,7 +18,7 @@
 from pycalendar.stringutils import strtoul
 from pycalendar.valueutils import ValueMixin
 
-class PyCalendarDuration(ValueMixin):
+class Duration(ValueMixin):
 
     def __init__(self, duration=None, weeks=0, days=0, hours=0, minutes=0, seconds=0):
         self.mForward = True
@@ -36,7 +36,7 @@
 
 
     def duplicate(self):
-        other = PyCalendarDuration(None)
+        other = Duration(None)
         other.mForward = self.mForward
 
         other.mWeeks = self.mWeeks

Modified: PyCalendar/branches/json-2/src/pycalendar/durationvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/durationvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/durationvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,42 +14,18 @@
 #    limitations under the License.
 ##
 
-from pycalendar import xmldefs
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.duration import Duration
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
 
-class PyCalendarDurationValue(PyCalendarValue):
+class DurationValue(WrapperValue, Value):
 
     def __init__(self, value=None):
-        self.mValue = value if value is not None else PyCalendarDuration()
+        self.mValue = value if value is not None else Duration()
 
 
-    def duplicate(self):
-        return PyCalendarDurationValue(self.mValue.duplicate())
-
-
     def getType(self):
-        return PyCalendarValue.VALUETYPE_DURATION
+        return Value.VALUETYPE_DURATION
 
-
-    def parse(self, data):
-        self.mValue.parse(data)
-
-
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
-    def writeXML(self, node, namespace):
-        value = self.getXMLNode(node, namespace)
-        value.text = self.mValue.writeXML()
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DURATION, PyCalendarDurationValue, xmldefs.value_duration)
+Value.registerType(Value.VALUETYPE_DURATION, DurationValue, xmldefinitions.value_duration)

Modified: PyCalendar/branches/json-2/src/pycalendar/exceptions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/exceptions.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/exceptions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-class PyCalendarError(Exception):
+class ErrorBase(Exception):
 
     def __init__(self, reason, data=""):
         self.mReason = reason
@@ -22,22 +22,22 @@
 
 
 
-class PyCalendarInvalidData(PyCalendarError):
+class InvalidData(ErrorBase):
     pass
 
 
 
-class PyCalendarInvalidProperty(PyCalendarError):
+class InvalidProperty(ErrorBase):
     pass
 
 
 
-class PyCalendarValidationError(PyCalendarError):
+class ValidationError(ErrorBase):
     pass
 
 
 
-class PyCalendarNoTimezoneInDatabase(Exception):
+class NoTimezoneInDatabase(Exception):
 
     def __init__(self, dbpath, tzid):
         self.mTZDBpath = dbpath

Deleted: PyCalendar/branches/json-2/src/pycalendar/freebusy.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/freebusy.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/freebusy.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,56 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-class PyCalendarFreeBusy(object):
-
-    FREE = 0
-    BUSYTENTATIVE = 1
-    BUSYUNAVAILABLE = 2
-    BUSY = 3
-
-    def __init__(self, type=None, period=None):
-
-        self.mType = type if type else PyCalendarFreeBusy.FREE
-        self.mPeriod = period.duplicate() if period is not None else None
-
-
-    def duplicate(self):
-        return PyCalendarFreeBusy(self.mType, self.mPeriod)
-
-
-    def setType(self, type):
-        self.mType = type
-
-
-    def getType(self):
-        return self.mType
-
-
-    def setPeriod(self, period):
-        self.mPeriod = period.duplicate()
-
-
-    def getPeriod(self):
-        return self.mPeriod
-
-
-    def isPeriodOverlap(self, period):
-        return self.mPeriod.isPeriodOverlap(period)
-
-
-    def resolveOverlaps(self, fb):
-        # TODO:
-        pass

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/available.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/available.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/available.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/available.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,92 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentrecur import ComponentRecur
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+
+class Available(ComponentRecur):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CREATED,
+        definitions.cICalProperty_DESCRIPTION,
+        definitions.cICalProperty_GEO,
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_LOCATION,
+        definitions.cICalProperty_RECURRENCE_ID,
+        definitions.cICalProperty_RRULE,
+        definitions.cICalProperty_SEQUENCE,
+        definitions.cICalProperty_SUMMARY,
+        definitions.cICalProperty_DTEND,
+        definitions.cICalProperty_DURATION,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(Available, self).__init__(parent=parent)
+
+
+    def duplicate(self, parent=None):
+        return super(Available, self).duplicate(parent=parent)
+
+
+    def getType(self):
+        return definitions.cICalComponent_AVAILABLE
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        fixed, unfixed = super(Available, self).validate(doFix)
+
+        # Extra constraint: only one of DTEND or DURATION
+        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
+            # Fix by removing the DTEND
+            logProblem = "[%s] Properties must not both be present: %s, %s" % (
+                self.getType(),
+                definitions.cICalProperty_DTEND,
+                definitions.cICalProperty_DURATION,
+            )
+            if doFix:
+                self.removeProperties(definitions.cICalProperty_DTEND)
+                fixed.append(logProblem)
+            else:
+                unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_RECURRENCE_ID,
+            definitions.cICalProperty_DTSTART,
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_DTEND,
+        )
+Component.registerComponent(definitions.cICalComponent_AVAILABLE, Available)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py (from rev 11594, PyCalendar/branches/json-2/src/pycalendar/calendar.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,724 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from cStringIO import StringIO
+from pycalendar import xmlutils
+from pycalendar.componentbase import ComponentBase
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData, ValidationError
+from pycalendar.icalendar import definitions, xmldefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentexpanded import ComponentExpanded
+from pycalendar.icalendar.componentrecur import ComponentRecur
+from pycalendar.icalendar.freebusy import FreeBusy
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+from pycalendar.parser import ParserContext
+from pycalendar.period import Period
+from pycalendar.utils import readFoldedLine
+import collections
+import xml.etree.cElementTree as XML
+
+class Calendar(ComponentBase):
+
+    REMOVE_ALL = 0
+    REMOVE_ONLY_THIS = 1
+    REMOVE_THIS_AND_FUTURE = 2
+
+    FIND_EXACT = 0
+    FIND_MASTER = 1
+
+    sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
+    sDomain = "mulberrymail.com"
+
+    sComponentType = Component
+    sPropertyType = Property
+
+    @staticmethod
+    def setPRODID(prodid):
+        Calendar.sProdID = prodid
+
+
+    @staticmethod
+    def setDomain(domain):
+        Calendar.sDomain = domain
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_PRODID,
+        definitions.cICalProperty_VERSION,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CALSCALE,
+        definitions.cICalProperty_METHOD,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None, add_defaults=True):
+        super(Calendar, self).__init__(None)
+
+        self.mName = ""
+        self.mDescription = ""
+        self.mMasterComponentsByTypeAndUID = collections.defaultdict(lambda: collections.defaultdict(list))
+        self.mOverriddenComponentsByUID = collections.defaultdict(list)
+
+        if add_defaults:
+            self.addDefaultProperties()
+
+
+    def duplicate(self):
+        other = super(Calendar, self).duplicate()
+        other.mName = self.mName
+        other.mDescription = self.mDescription
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VCALENDAR
+
+
+    def getName(self):
+        return self.mName
+
+
+    def setName(self, name):
+        self.mName = name
+
+
+    def editName(self, name):
+        if self.mName != name:
+            # Updated cached value
+            self.mName = name
+
+            # Remove existing items
+            self.removeProperties(definitions.cICalProperty_XWRCALNAME)
+
+            # Now create properties
+            if len(name):
+                self.ddProperty(Property(definitions.cICalProperty_XWRCALNAME, name))
+
+
+    def getDescription(self):
+        return self.mDescription
+
+
+    def setDescription(self, description):
+        self.mDescription = description
+
+
+    def editDescription(self, description):
+        if self.mDescription != description:
+            # Updated cached value
+            self.mDescription = description
+
+            # Remove existing items
+            self.removeProperties(definitions.cICalProperty_XWRCALDESC)
+
+            # Now create properties
+            if len(description):
+                self.addProperty(Property(definitions.cICalProperty_XWRCALDESC, description))
+
+
+    def getMethod(self):
+        result = ""
+        if self.hasProperty(definitions.cICalProperty_METHOD):
+            result = self.loadValueString(definitions.cICalProperty_METHOD)
+        return result
+
+
+    def changeUID(self, oldUID, newUID):
+        """
+        Change the UID of all components with a matching UID to a new value. We need to
+        do this at the calendar level because this object maintains mappings based on UID
+        which need to be updated whenever the UID changes.
+
+        @param oldUID: the old value to match
+        @type oldUID: C{str}
+        @param newUID: the new value to match
+        @type newUID: C{str}
+        """
+
+        # Each component
+        for component in self.mComponents:
+            if component.getUID() == oldUID:
+                component.setUID(newUID)
+
+        # Maps
+        if oldUID in self.mOverriddenComponentsByUID:
+            self.mOverriddenComponentsByUID[newUID] = self.mOverriddenComponentsByUID[oldUID]
+            del self.mOverriddenComponentsByUID[oldUID]
+        for ctype in self.mMasterComponentsByTypeAndUID:
+            if oldUID in self.mMasterComponentsByTypeAndUID[ctype]:
+                self.mMasterComponentsByTypeAndUID[ctype][newUID] = self.mMasterComponentsByTypeAndUID[ctype][oldUID]
+                del self.mMasterComponentsByTypeAndUID[ctype][oldUID]
+
+
+    def finalise(self):
+        # Get calendar name if present
+
+        # Get X-WR-CALNAME
+        temps = self.loadValueString(definitions.cICalProperty_XWRCALNAME)
+        if temps is not None:
+            self.mName = temps
+
+        # Get X-WR-CALDESC
+        temps = self.loadValueString(definitions.cICalProperty_XWRCALDESC)
+        if temps is not None:
+            self.mDescription = temps
+
+
+    def validate(self, doFix=False, doRaise=False):
+        """
+        Validate the data in this component and optionally fix any problems. Return
+        a tuple containing two lists: the first describes problems that were fixed, the
+        second problems that were not fixed. Caller can then decide what to do with unfixed
+        issues.
+        """
+
+        # Optional raise behavior
+        fixed, unfixed = super(Calendar, self).validate(doFix)
+        if doRaise and unfixed:
+            raise ValidationError(";".join(unfixed))
+        return fixed, unfixed
+
+
+    def sortedComponentNames(self):
+        return (
+            definitions.cICalComponent_VTIMEZONE,
+            definitions.cICalComponent_VEVENT,
+            definitions.cICalComponent_VTODO,
+            definitions.cICalComponent_VJOURNAL,
+            definitions.cICalComponent_VFREEBUSY,
+            definitions.cICalComponent_VAVAILABILITY,
+        )
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_VERSION,
+            definitions.cICalProperty_CALSCALE,
+            definitions.cICalProperty_METHOD,
+            definitions.cICalProperty_PRODID,
+        )
+
+
+    @staticmethod
+    def parseText(data):
+
+        cal = Calendar(add_defaults=False)
+        if cal.parse(StringIO(data)):
+            return cal
+        else:
+            return None
+
+
+    def parse(self, ins):
+
+        result = False
+
+        self.setProperties({})
+
+        LOOK_FOR_VCALENDAR = 0
+        GET_PROPERTY_OR_COMPONENT = 1
+
+        state = LOOK_FOR_VCALENDAR
+
+        # Get lines looking for start of calendar
+        lines = [None, None]
+        comp = self
+        compend = None
+        componentstack = []
+
+        while readFoldedLine(ins, lines):
+
+            line = lines[0]
+
+            if state == LOOK_FOR_VCALENDAR:
+
+                # Look for start
+                if line == self.getBeginDelimiter():
+                    # Next state
+                    state = GET_PROPERTY_OR_COMPONENT
+
+                    # Indicate success at this point
+                    result = True
+
+                # Handle blank line
+                elif len(line) == 0:
+                    # Raise if requested, otherwise just ignore
+                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+                        raise InvalidData("iCalendar data has blank lines")
+
+                # Unrecognized data
+                else:
+                    raise InvalidData("iCalendar data not recognized", line)
+
+            elif state == GET_PROPERTY_OR_COMPONENT:
+
+                # Parse property or look for start of component
+                if line.startswith("BEGIN:"):
+
+                    # Push previous details to stack
+                    componentstack.append((comp, compend,))
+
+                    # Start a new component
+                    comp = Component.makeComponent(line[6:], comp)
+                    compend = comp.getEndDelimiter()
+
+                # Look for end of object
+                elif line == self.getEndDelimiter():
+
+                    # Finalise the current calendar
+                    self.finalise()
+
+                    # Change state
+                    state = LOOK_FOR_VCALENDAR
+
+                # Look for end of current component
+                elif line == compend:
+
+                    # Finalise the component (this caches data from the properties)
+                    comp.finalise()
+
+                    # Embed component in parent and reset to use parent
+                    componentstack[-1][0].addComponent(comp)
+                    comp, compend = componentstack.pop()
+
+                # Blank line
+                elif len(line) == 0:
+                    # Raise if requested, otherwise just ignore
+                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+                        raise InvalidData("iCalendar data has blank lines")
+
+                # Must be a property
+                else:
+
+                    # Parse parameter/value for top-level calendar item
+                    prop = Property()
+                    if prop.parse(line):
+
+                        # Check for valid property
+                        if comp is self:
+                            if not comp.validProperty(prop):
+                                raise InvalidData("Invalid property", str(prop))
+                            elif not comp.ignoreProperty(prop):
+                                comp.addProperty(prop)
+                        else:
+                            comp.addProperty(prop)
+
+        # Check for truncated data
+        if state != LOOK_FOR_VCALENDAR:
+            raise InvalidData("iCalendar data not complete")
+
+        # We need to store all timezones in the static object so they can be accessed by any date object
+        from pycalendar.timezonedb import TimezoneDatabase
+        TimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
+
+        # Validate some things
+        if result and not self.hasProperty("VERSION"):
+            raise InvalidData("iCalendar missing VERSION")
+
+        return result
+
+
+    def parseComponent(self, ins):
+
+        result = None
+
+        LOOK_FOR_VCALENDAR = 0
+        GET_PROPERTY_OR_COMPONENT = 1
+        GOT_VCALENDAR = 4
+
+        state = LOOK_FOR_VCALENDAR
+
+        # Get lines looking for start of calendar
+        lines = [None, None]
+        comp = self
+        compend = None
+        componentstack = []
+        got_timezone = False
+
+        while readFoldedLine(ins, lines):
+
+            if state == LOOK_FOR_VCALENDAR:
+
+                # Look for start
+                if lines[0] == self.getBeginDelimiter():
+                    # Next state
+                    state = GET_PROPERTY_OR_COMPONENT
+
+                # Handle blank line
+                elif len(lines[0]) == 0:
+                    # Raise if requested, otherwise just ignore
+                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+                        raise InvalidData("iCalendar data has blank lines")
+
+                # Unrecognized data
+                else:
+                    raise InvalidData("iCalendar data not recognized", lines[0])
+
+            elif state == GET_PROPERTY_OR_COMPONENT:
+
+                # Parse property or look for start of component
+                if lines[0].startswith("BEGIN:"):
+
+                    # Push previous details to stack
+                    componentstack.append((comp, compend,))
+
+                    # Start a new component
+                    comp = Component.makeComponent(lines[0][6:], comp)
+                    compend = comp.getEndDelimiter()
+
+                    # Cache as result - but only the first one, we ignore the rest
+                    if result is None:
+                        result = comp
+
+                    # Look for timezone component to trigger timezone merge only if one is present
+                    if comp.getType() == definitions.cICalComponent_VTIMEZONE:
+                        got_timezone = True
+
+                elif lines[0] == self.getEndDelimiter():
+
+                    # Change state
+                    state = GOT_VCALENDAR
+
+                # Look for end of current component
+                elif lines[0] == compend:
+
+                    # Finalise the component (this caches data from the properties)
+                    comp.finalise()
+
+                    # Embed component in parent and reset to use parent
+                    componentstack[-1][0].addComponent(comp)
+                    comp, compend = componentstack.pop()
+
+                # Blank line
+                elif len(lines[0]) == 0:
+                    # Raise if requested, otherwise just ignore
+                    if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
+                        raise InvalidData("iCalendar data has blank lines")
+
+                # Ignore top-level items
+                elif comp is self:
+                    pass
+
+                # Must be a property
+                else:
+
+                    # Parse parameter/value for top-level calendar item
+                    prop = Property()
+                    if prop.parse(lines[0]):
+
+                        # Check for valid property
+                        if comp is not self:
+                            comp.addProperty(prop)
+
+            # Exit if we have one - ignore all the rest
+            if state == GOT_VCALENDAR:
+                break
+
+        # We need to store all timezones in the static object so they can be accessed by any date object
+        # Only do this if we read in a timezone
+        if got_timezone:
+            from pycalendar.timezonedb import TimezoneDatabase
+            TimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
+
+        return result
+
+
+    def addComponent(self, component):
+        """
+        Override to track components by UID.
+        """
+        super(Calendar, self).addComponent(component)
+
+        if isinstance(component, ComponentRecur):
+            uid = component.getUID()
+            rid = component.getRecurrenceID()
+            if rid:
+                self.mOverriddenComponentsByUID[uid].append(component)
+            else:
+                self.mMasterComponentsByTypeAndUID[component.getType()][uid] = component
+
+
+    def removeComponent(self, component):
+        """
+        Override to track components by UID.
+        """
+        super(Calendar, self).removeComponent(component)
+
+        if isinstance(component, ComponentRecur):
+            uid = component.getUID()
+            rid = component.getRecurrenceID()
+            if rid:
+                self.mOverriddenComponentsByUID[uid].remove(component)
+            else:
+                del self.mMasterComponentsByTypeAndUID[component.getType()][uid]
+
+
+    def getText(self, includeTimezones=False):
+        s = StringIO()
+        self.generate(s, includeTimezones=includeTimezones)
+        return s.getvalue()
+
+
+    def generate(self, os, includeTimezones=False):
+        # Make sure all required timezones are in this object
+        if includeTimezones:
+            self.includeTimezones()
+        super(Calendar, self).generate(os)
+
+
+    def getTextXML(self, includeTimezones=False):
+        node = self.writeXML(includeTimezones)
+        return xmlutils.toString(node)
+
+
+    def writeXML(self, includeTimezones=False):
+        # Make sure all required timezones are in this object
+        if includeTimezones:
+            self.includeTimezones()
+
+        # Root node structure
+        root = XML.Element(xmlutils.makeTag(xmldefinitions.iCalendar20_namespace, xmldefinitions.icalendar))
+        super(Calendar, self).writeXML(root, xmldefinitions.iCalendar20_namespace)
+        return root
+
+
+    # Get expanded components
+    def getVEvents(self, period, list, all_day_at_top=True):
+        # Look at each VEvent
+        for vevent in self.getComponents(definitions.cICalComponent_VEVENT):
+            vevent.expandPeriod(period, list)
+
+        if (all_day_at_top):
+            list.sort(ComponentExpanded.sort_by_dtstart_allday)
+        else:
+            list.sort(ComponentExpanded.sort_by_dtstart)
+
+
+    def getVToDos(self, only_due, all_dates, upto_due_date, list):
+        # Get current date-time less one day to test for completed events during the last day
+        minusoneday = DateTime()
+        minusoneday.setNowUTC()
+        minusoneday.offsetDay(-1)
+
+        today = DateTime()
+        today.setToday()
+
+        # Look at each VToDo
+        for vtodo in self.getComponents(definitions.cICalComponent_VTODO):
+
+            # Filter out done (that were complted more than a day ago) or cancelled to dos if required
+            if only_due:
+                if vtodo.getStatus() == definitions.eStatus_VToDo_Cancelled:
+                    continue
+                elif ((vtodo.getStatus() == definitions.eStatus_VToDo_Completed) and
+                            (not vtodo.hasCompleted() or (vtodo.getCompleted() < minusoneday))):
+                    continue
+
+            # Filter out those with end after chosen date if required
+            if not all_dates:
+                if vtodo.hasEnd() and (vtodo.getEnd() > upto_due_date):
+                    continue
+                elif not vtodo.hasEnd() and (today > upto_due_date):
+                    continue
+
+            # TODO: fix this
+            #list.append(ComponentExpandedShared(ComponentExpanded(vtodo, None)))
+
+
+    def getRecurrenceInstancesItems(self, type, uid, items):
+        # Get instances from list
+        items.extend(self.mOverriddenComponentsByUID.get(uid, ()))
+
+
+    def getRecurrenceInstancesIds(self, type, uid, ids):
+        # Get instances from list
+        ids.extend([comp.getRecurrenceID() for comp in self.mOverriddenComponentsByUID.get(uid, ())])
+
+
+    # Freebusy generation
+    def getVFreeBusyList(self, period, list):
+        # Look at each VFreeBusy
+        for vfreebusy in self.getComponents(definitions.cICalComponent_VFREEBUSY):
+            vfreebusy.expandPeriod(period, list)
+
+
+    def getVFreeBusyFB(self, period, fb):
+        # First create expanded set
+        # TODO: fix this
+        #list = ExpandedComponents()
+        self.getVEvents(period, list)
+        if len(list) == 0:
+            return
+
+        # Get start/end list for each non-all-day expanded components
+        dtstart = []
+        dtend = []
+        for dt in list:
+
+            # Ignore if all-day
+            if dt.getInstanceStart().isDateOnly():
+                continue
+
+            # Ignore if transparent to free-busy
+            transp = ""
+            if dt.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
+                continue
+
+            # Add start/end to list
+            dtstart.append(dt.getInstanceStart())
+            dtend.append(dt.getInstanceEnd())
+
+        # No longer need the expanded items
+        list.clear()
+
+        # Create non-overlapping periods as properties in the freebusy component
+        temp = Period(dtstart.front(), dtend.front())
+        dtstart_iter = dtstart.iter()
+        dtstart_iter.next()
+        dtend_iter = dtend.iter()
+        dtend_iter.next()
+        for i in i:
+
+            # Check for non-overlap
+            if dtstart_iter > temp.getEnd():
+
+                # Current period is complete
+                fb.addProperty(Property(definitions.cICalProperty_FREEBUSY, temp))
+
+                # Reset period to new range
+                temp = Period(dtstart_iter, dtend_iter)
+
+            # They overlap - check for extended end
+            if dtend_iter > temp.getEnd():
+
+                # Extend the end
+                temp = Period(temp.getStart(), dtend_iter)
+
+        # Add remaining period as property
+        fb.addProperty(Property(definitions.cICalProperty_FREEBUSY, temp))
+
+
+    def getFreeBusy(self, period, fb):
+        # First create expanded set
+
+        list = []
+        self.getVEvents(period, list)
+
+        # Get start/end list for each non-all-day expanded components
+        for comp in list:
+
+            # Ignore if all-day
+            if comp.getInstanceStart().isDateOnly():
+                continue
+
+            # Ignore if transparent to free-busy
+            transp = ""
+            if comp.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
+                continue
+
+            # Add free busy item to list
+            status = comp.getMaster().getStatus()
+            if status in (definitions.eStatus_VEvent_None, definitions.eStatus_VEvent_Confirmed):
+                fb.append(FreeBusy(FreeBusy.BUSY, Period(comp.getInstanceStart(), comp.getInstanceEnd())))
+            elif status == definitions.eStatus_VEvent_Tentative:
+                fb.append(FreeBusy(FreeBusy.BUSYTENTATIVE, Period(comp.getInstanceStart(), comp.getInstanceEnd())))
+                break
+            elif status == definitions.eStatus_VEvent_Cancelled:
+                # Cancelled => does not contribute to busy time
+                pass
+
+        # Now get the VFREEBUSY info
+        list2 = []
+        self.getVFreeBusy(period, list2)
+
+        # Get start/end list for each free-busy
+        for comp in list2:
+
+            # Expand component and add free busy info to list
+            comp.expandPeriod(period, fb)
+
+        # Add remaining period as property
+        FreeBusy.resolveOverlaps(fb)
+
+
+    def getTimezoneOffsetSeconds(self, tzid, dt):
+        # Find timezone that matches the name (which is the same as the map key)
+        timezone = self.getTimezone(tzid)
+        return timezone.getTimezoneOffsetSeconds(dt) if timezone else 0
+
+
+    def getTimezoneDescriptor(self, tzid, dt):
+        # Find timezone that matches the name (which is the same as the map key)
+        timezone = self.getTimezone(tzid)
+        return timezone.getTimezoneDescriptor(dt) if timezone else ""
+
+
+    def getTimezone(self, tzid):
+        # Find timezone that matches the name (which is the same as the map key)
+        for timezone in self.getComponents(definitions.cICalComponent_VTIMEZONE):
+            if timezone.getID() == tzid:
+                return timezone
+        else:
+            return None
+
+
+    def addDefaultProperties(self):
+        self.addProperty(Property(definitions.cICalProperty_PRODID, Calendar.sProdID))
+        self.addProperty(Property(definitions.cICalProperty_VERSION, "2.0"))
+        self.addProperty(Property(definitions.cICalProperty_CALSCALE, "GREGORIAN"))
+
+
+    def validProperty(self, prop):
+        if prop.getName() == definitions.cICalProperty_VERSION:
+
+            tvalue = prop.getTextValue()
+            if ((tvalue is None) or (tvalue.getValue() != "2.0")):
+                return False
+
+        elif prop.getName() == definitions.cICalProperty_CALSCALE:
+
+            tvalue = prop.getTextValue()
+            if ((tvalue is None) or (tvalue.getValue() != "GREGORIAN")):
+                return False
+
+        return True
+
+
+    def ignoreProperty(self, prop):
+        return False #prop.getName() in (definitions.cICalProperty_VERSION, definitions.cICalProperty_CALSCALE, definitions.cICalProperty_PRODID)
+
+
+    def includeTimezones(self):
+        # Get timezone names from each component
+        tzids = set()
+        for component in self.mComponents:
+            if component.getType() != definitions.cICalComponent_VTIMEZONE:
+                component.getTimezones(tzids)
+
+        # Make sure each timezone is in current calendar
+        from pycalendar.timezonedb import TimezoneDatabase
+        for tzid in tzids:
+            tz = self.getTimezone(tzid)
+            if tz is None:
+                # Find it in the static object
+                tz = TimezoneDatabase.getTimezone(tzid)
+                if tz is not None:
+                    dup = tz.duplicate()
+                    self.addComponent(dup)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/component.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/component.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/component.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/component.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,212 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar import stringutils
+from pycalendar.componentbase import ComponentBase
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.property import Property
+import os
+import time
+import uuid
+
+class Component(ComponentBase):
+
+    uid_ctr = 1
+
+    mapper = {}
+
+    sComponentType = None
+    sPropertyType = Property
+
+    @classmethod
+    def registerComponent(cls, name, comptype):
+        cls.mapper[name] = comptype
+
+
+    @classmethod
+    def makeComponent(cls, compname, parent):
+        try:
+            return cls.mapper[compname](parent=parent)
+        except KeyError:
+            return cls.mapper[definitions.cICalComponent_UNKNOWN](parent=parent, comptype=compname)
+
+
+    def __init__(self, parent=None):
+
+        super(Component, self).__init__(parent)
+        self.mUID = ""
+        self.mSeq = 0
+        self.mOriginalSeq = 0
+        self.mChanged = False
+
+
+    def duplicate(self, parent=None, **args):
+
+        other = super(Component, self).duplicate(parent=parent, **args)
+        other.mUID = self.mUID
+        other.mSeq = self.mSeq
+        other.mOriginalSeq = self.mOriginalSeq
+
+        other.mChanged = self.mChanged
+
+        return other
+
+
+    def __repr__(self):
+        return "%s: UID: %s" % (self.getType(), self.getMapKey(),)
+
+
+    def getMimeComponentName(self):
+        raise NotImplementedError
+
+
+    def getMapKey(self):
+        if hasattr(self, "mMapKey"):
+            return self.mMapKey
+        elif self.mUID:
+            return self.mUID
+        else:
+            self.mMapKey = str(uuid.uuid4())
+            return self.mMapKey
+
+
+    def getSortKey(self):
+        return self.getMapKey()
+
+
+    def getMasterKey(self):
+        return self.mUID
+
+
+    def getUID(self):
+        return self.mUID
+
+
+    def setUID(self, uid):
+        if uid:
+            self.mUID = uid
+        else:
+            # Get left-side of UID (first 24 chars of MD5 digest of time, pid
+            # and ctr)
+            lhs_txt = ""
+            lhs_txt += str(time.time())
+            lhs_txt += "."
+            lhs_txt += str(os.getpid())
+            lhs_txt += "."
+            lhs_txt += str(Component.uid_ctr)
+            Component.uid_ctr += 1
+            lhs = stringutils.md5digest(lhs_txt)
+
+            # Get right side (domain) of message-id
+            rhs = None
+
+            # Use app name
+            from pycalendar.icalendar.calendar import Calendar
+            domain = Calendar.sDomain
+            domain += str(Component.uid_ctr)
+
+            # Use first 24 chars of MD5 digest of the domain as the
+            # right-side of message-id
+            rhs = stringutils.md5digest(domain)
+
+            # Generate the UID string
+            new_uid = lhs
+            new_uid += "@"
+            new_uid += rhs
+
+            self.mUID = new_uid
+
+        self.removeProperties(definitions.cICalProperty_UID)
+
+        prop = Property(definitions.cICalProperty_UID, self.mUID)
+        self.addProperty(prop)
+
+
+    def getSeq(self):
+        return self.mSeq
+
+
+    def setSeq(self, seq):
+        self.mSeq = seq
+
+        self.removeProperties(definitions.cICalProperty_SEQUENCE)
+
+        prop = Property(definitions.cICalProperty_SEQUENCE, self.mSeq)
+        self.addProperty(prop)
+
+
+    def getOriginalSeq(self):
+        return self.mOriginalSeq
+
+
+    def getChanged(self):
+        return self.mChanged
+
+
+    def setChanged(self, changed):
+        self.mChanged = changed
+
+
+    def initDTSTAMP(self):
+        self.removeProperties(definitions.cICalProperty_DTSTAMP)
+
+        prop = Property(definitions.cICalProperty_DTSTAMP,
+                                  DateTime.getNowUTC())
+        self.addProperty(prop)
+
+
+    def updateLastModified(self):
+        self.removeProperties(definitions.cICalProperty_LAST_MODIFIED)
+
+        prop = Property(definitions.cICalProperty_LAST_MODIFIED,
+                                  DateTime.getNowUTC())
+        self.addProperty(prop)
+
+
+    def finalise(self):
+        # Get UID
+        temps = self.loadValueString(definitions.cICalProperty_UID)
+        if temps is not None:
+            self.mUID = temps
+
+        # Get SEQ
+        temp = self.loadValueInteger(definitions.cICalProperty_SEQUENCE)
+        if temp is not None:
+            self.mSeq = temp
+
+        # Cache the original sequence when the component is read in.
+        # This will be used to synchronise changes between two instances of the
+        # same calendar
+        self.mOriginalSeq = self.mSeq
+
+
+    def canGenerateInstance(self):
+        return True
+
+
+    def getTimezones(self, tzids):
+        # Look for all date-time properties
+        for props in self.mProperties.itervalues():
+            for prop in props:
+                # Try to get a date-time value from the property
+                dtv = prop.getDateTimeValue()
+                if dtv is not None:
+                    # Add timezone id if appropriate
+                    if dtv.getValue().getTimezoneID():
+                        tzids.add(dtv.getValue().getTimezoneID())
+
+Component.sComponentType = Component

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/componentexpanded.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/componentexpanded.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/componentexpanded.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/componentexpanded.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,158 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+
+class ComponentExpanded(object):
+
+    @staticmethod
+    def sort_by_dtstart_allday(e1, e2):
+
+        if e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly():
+            return e1.mInstanceStart < e2.mInstanceStart
+        elif e1.mInstanceStart.isDateOnly():
+            return True
+        elif e2.mInstanceStart.isDateOnly():
+            return False
+        elif e1.mInstanceStart == e2.mInstanceStart:
+            if e1.mInstanceEnd == e2.mInstanceEnd:
+                # Put ones created earlier in earlier columns in day view
+                return e1.getOwner().getStamp() < e2.getOwner().getStamp()
+            else:
+                # Put ones that end later in earlier columns in day view
+                return e1.mInstanceEnd > e2.mInstanceEnd
+        else:
+            return e1.mInstanceStart < e2.mInstanceStart
+
+
+    @staticmethod
+    def sort_by_dtstart(e1, e2):
+        if e1.mInstanceStart == e2.mInstanceStart:
+            if (e1.mInstanceStart.isDateOnly() and not e2.mInstanceStart.isDateOnly() or
+                not e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly()):
+                return e1.mInstanceStart.isDateOnly()
+            else:
+                return False
+        else:
+            return e1.mInstanceStart < e2.mInstanceStart
+
+
+    def __init__(self, owner, rid):
+
+        self.mOwner = owner
+        self.initFromOwner(rid)
+
+
+    def duplicate(self):
+        other = ComponentExpanded(self.mOwner, None)
+        other.mInstanceStart = self.mInstanceStart.duplicate()
+        other.mInstanceEnd = self.mInstanceEnd.duplicate()
+        other.mRecurring = self.mRecurring
+        return other
+
+
+    def close(self):
+        # Clean-up
+        self.mOwner = None
+
+
+    def getOwner(self):
+        return self.mOwner
+
+
+    def getMaster(self):
+        return self.mOwner
+
+
+    def getTrueMaster(self):
+        return self.mOwner.getMaster()
+
+
+    def getInstanceStart(self):
+        return self.mInstanceStart
+
+
+    def getInstanceEnd(self):
+        return self.mInstanceEnd
+
+
+    def recurring(self):
+        return self.mRecurring
+
+
+    def isNow(self):
+        # Check instance start/end against current date-time
+        now = DateTime.getNowUTC()
+        return self.mInstanceStart <= now and self.mInstanceEnd > now
+
+
+    def initFromOwner(self, rid):
+        # There are four possibilities here:
+        #
+        # 1: this instance is the instance for the master component
+        #
+        # 2: this instance is an expanded instance derived directly from the
+        # master component
+        #
+        # 3: This instance is the instance for a slave (overridden recurrence
+        # instance)
+        #
+        # 4: This instance is the expanded instance for a slave with a RANGE
+        # parameter
+        #
+
+        # rid is not set if the owner is the master (case 1)
+        if rid is None:
+            # Just get start/end from owner
+            self.mInstanceStart = self.mOwner.getStart()
+            self.mInstanceEnd = self.mOwner.getEnd()
+            self.mRecurring = False
+
+        # If the owner is not a recurrence instance then it is case 2
+        elif not self.mOwner.isRecurrenceInstance():
+            # Derive start/end from rid and duration of master
+
+            # Start of the recurrence instance is the recurrence id
+            self.mInstanceStart = rid
+
+            # End is based on original events settings
+            if self.mOwner.hasEnd():
+                self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
+            else:
+                self.mInstanceEnd = self.mInstanceStart.duplicate()
+
+            self.mRecurring = True
+
+        # If the owner is a recurrence item and the passed in rid is the same
+        # as the component rid we have case 3
+        elif rid == self.mOwner.getRecurrenceID():
+            # Derive start/end directly from the owner
+            self.mInstanceStart = self.mOwner.getStart()
+            self.mInstanceEnd = self.mOwner.getEnd()
+
+            self.mRecurring = True
+
+        # case 4 - the complicated one!
+        else:
+            # We need to use the rid as the starting point, but adjust it by
+            # the offset between the slave's
+            # rid and its start
+            self.mInstanceStart = rid + (self.mOwner.getStart() - self.mOwner.getRecurrenceID())
+
+            # End is based on duration of owner
+            self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
+
+            self.mRecurring = True

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/componentrecur.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/componentrecur.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/componentrecur.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/componentrecur.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,714 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentexpanded import ComponentExpanded
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.recurrenceset import RecurrenceSet
+from pycalendar.timezone import Timezone
+from pycalendar.utils import set_difference
+import uuid
+
+class ComponentRecur(Component):
+
+    propertyCardinality_STATUS_Fix = (
+        definitions.cICalProperty_STATUS,
+    )
+
+    @staticmethod
+    def mapKey(uid, rid=None):
+        if uid:
+            result = "u:" + uid
+            if rid is not None:
+                result += rid
+            return result
+        else:
+            return None
+
+
+    @staticmethod
+    def sort_by_dtstart_allday(e1, e2):
+
+        if e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly():
+            return e1.self.mStart < e2.self.mStart
+        elif e1.self.mStart.isDateOnly():
+            return True
+        elif (e2.self.mStart.isDateOnly()):
+            return False
+        elif e1.self.mStart == e2.self.mStart:
+            if e1.self.mEnd == e2.self.mEnd:
+                # Put ones created earlier in earlier columns in day view
+                return e1.self.mStamp < e2.self.mStamp
+            else:
+                # Put ones that end later in earlier columns in day view
+                return e1.self.mEnd > e2.self.mEnd
+        else:
+            return e1.self.mStart < e2.self.mStart
+
+
+    @staticmethod
+    def sort_by_dtstart(e1, e2):
+        if e1.self.mStart == e2.self.mStart:
+            if (e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly() or
+                not e1.self.mStart.isDateOnly() and not e2.self.mStart.isDateOnly()):
+                return False
+            else:
+                return e1.self.mStart.isDateOnly()
+        else:
+            return e1.self.mStart < e2.self.mStart
+
+
+    def __init__(self, parent=None):
+        super(ComponentRecur, self).__init__(parent=parent)
+        self.mMaster = self
+        self.mMapKey = None
+        self.mSummary = None
+        self.mStamp = DateTime()
+        self.mHasStamp = False
+        self.mStart = DateTime()
+        self.mHasStart = False
+        self.mEnd = DateTime()
+        self.mHasEnd = False
+        self.mDuration = False
+        self.mHasRecurrenceID = False
+        self.mAdjustFuture = False
+        self.mAdjustPrior = False
+        self.mRecurrenceID = None
+        self.mRecurrences = None
+
+        # This is a special check we do only for STATUS due to a calendarserver bug
+        self.cardinalityChecks += (
+            self.check_cardinality_STATUS_Fix,
+        )
+
+
+    def duplicate(self, parent=None):
+        other = super(ComponentRecur, self).duplicate(parent=parent)
+
+        # Special determination of master
+        other.mMaster = self.mMaster if self.recurring() else self
+
+        other.mMapKey = self.mMapKey
+
+        other.mSummary = self.mSummary
+
+        if (self.mStamp is not None):
+            other.mStamp = self.mStamp.duplicate()
+        other.mHasStamp = self.mHasStamp
+
+        other.mStart = self.mStart.duplicate()
+        other.mHasStart = self.mHasStart
+        other.mEnd = self.mEnd.duplicate()
+        other.mHasEnd = self.mHasEnd
+        other.mDuration = self.mDuration
+
+        other.mHasRecurrenceID = self.mHasRecurrenceID
+        other.mAdjustFuture = self.mAdjustFuture
+        other.mAdjustPrior = self.mAdjustPrior
+        if self.mRecurrenceID is not None:
+            other.mRecurrenceID = self.mRecurrenceID.duplicate()
+
+        other._resetRecurrenceSet()
+
+        return other
+
+
+    def canGenerateInstance(self):
+        return not self.mHasRecurrenceID
+
+
+    def recurring(self):
+        return (self.mMaster is not None) and (self.mMaster is not self)
+
+
+    def setMaster(self, master):
+        self.mMaster = master
+        self.initFromMaster()
+
+
+    def getMaster(self):
+        return self.mMaster
+
+
+    def getMapKey(self):
+
+        if self.mMapKey is None:
+            self.mMapKey = str(uuid.uuid4())
+        return self.mMapKey
+
+
+    def getMasterKey(self):
+        return ComponentRecur.mapKey(self.mUID)
+
+
+    def initDTSTAMP(self):
+        # Save new one
+        super(ComponentRecur, self).initDTSTAMP()
+
+        # Get the new one
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
+        self.mHasStamp = temp is not None
+        if self.mHasStamp:
+            self.mStamp = temp
+
+
+    def getStamp(self):
+        return self.mStamp
+
+
+    def hasStamp(self):
+        return self.mHasStamp
+
+
+    def getStart(self):
+        return self.mStart
+
+
+    def hasStart(self):
+        return self.mHasStart
+
+
+    def getEnd(self):
+        return self.mEnd
+
+
+    def hasEnd(self):
+        return self.mHasEnd
+
+
+    def useDuration(self):
+        return self.mDuration
+
+
+    def isRecurrenceInstance(self):
+        return self.mHasRecurrenceID
+
+
+    def isAdjustFuture(self):
+        return self.mAdjustFuture
+
+
+    def isAdjustPrior(self):
+        return self.mAdjustPrior
+
+
+    def getRecurrenceID(self):
+        return self.mRecurrenceID
+
+
+    def isRecurring(self):
+        return (self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
+
+
+    def getRecurrenceSet(self):
+        return self.mRecurrences
+
+
+    def setUID(self, uid):
+        super(ComponentRecur, self).setUID(uid)
+
+        # Update the map key
+        if self.mHasRecurrenceID:
+            self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
+        else:
+            self.mMapKey = self.mapKey(self.mUID)
+
+
+    def getSummary(self):
+        return self.mSummary
+
+
+    def setSummary(self, summary):
+        self.mSummary = summary
+
+
+    def getDescription(self):
+        # Get DESCRIPTION
+        txt = self.loadValueString(definitions.cICalProperty_DESCRIPTION)
+        if txt is not None:
+            return txt
+        else:
+            return ""
+
+
+    def getLocation(self):
+        # Get LOCATION
+        txt = self.loadValueString(definitions.cICalProperty_LOCATION)
+        if txt is not None:
+            return txt
+        else:
+            return ""
+
+
+    def finalise(self):
+        super(ComponentRecur, self).finalise()
+
+        # Get DTSTAMP
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
+        self.mHasStamp = temp is not None
+        if self.mHasStamp:
+            self.mStamp = temp
+
+        # Get DTSTART
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
+        self.mHasStart = temp is not None
+        if self.mHasStart:
+            self.mStart = temp
+
+        # Get DTEND
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
+        if temp is None:
+            # Try DURATION instead
+            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
+            if temp is not None:
+                self.mHasEnd = False
+                self.mEnd = self.mStart + temp
+                self.mDuration = True
+            else:
+                # If no end or duration then use the start
+                self.mHasEnd = False
+                self.mEnd = self.mStart.duplicate()
+                self.mDuration = False
+        else:
+            self.mHasEnd = True
+            self.mEnd = temp
+            self.mDuration = False
+
+        # Get SUMMARY
+        temp = self.loadValueString(definitions.cICalProperty_SUMMARY)
+        if temp is not None:
+            self.mSummary = temp
+
+        # Get RECURRENCE-ID
+        self.mHasRecurrenceID = (self.countProperty(definitions.cICalProperty_RECURRENCE_ID) != 0)
+        if self.mHasRecurrenceID:
+            self.mRecurrenceID = self.loadValueDateTime(definitions.cICalProperty_RECURRENCE_ID)
+
+        # Update the map key
+        if self.mHasRecurrenceID:
+            self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
+
+            # Also get the RANGE parameter
+            attrs = self.findFirstProperty(definitions.cICalProperty_RECURRENCE_ID).getParameters()
+            if definitions.cICalParameter_RANGE in attrs:
+                self.mAdjustFuture = (attrs[definitions.cICalParameter_RANGE][0].getFirstValue() == definitions.cICalParameter_RANGE_THISANDFUTURE)
+                self.mAdjustPrior = (attrs[definitions.cICalParameter_RANGE][0].getFirstValue() == definitions.cICalParameter_RANGE_THISANDPRIOR)
+            else:
+                self.mAdjustFuture = False
+                self.mAdjustPrior = False
+        else:
+            self.mMapKey = self.mapKey(self.mUID)
+
+        self._resetRecurrenceSet()
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems. Return
+        a tuple containing two lists: the first describes problems that were fixed, the
+        second problems that were not fixed. Caller can then decide what to do with unfixed
+        issues.
+        """
+
+        # Do normal checks
+        fixed, unfixed = super(ComponentRecur, self).validate(doFix)
+
+        # Check that any UNTIL value matches that for DTSTART
+        if self.mHasStart and self.mRecurrences:
+            dtutc = self.mStart.duplicateAsUTC()
+            for rrule in self.mRecurrences.getRules():
+                if rrule.getUseUntil():
+                    if rrule.getUntil().isDateOnly() ^ self.mStart.isDateOnly():
+                        logProblem = "[%s] Value types must match: %s, %s" % (
+                            self.getType(),
+                            definitions.cICalProperty_DTSTART,
+                            definitions.cICalValue_RECUR_UNTIL,
+                        )
+                        if doFix:
+                            rrule.getUntil().setDateOnly(self.mStart.isDateOnly())
+                            if not self.mStart.isDateOnly():
+                                rrule.getUntil().setHHMMSS(dtutc.getHours(), dtutc.getMinutes(), dtutc.getSeconds())
+                                rrule.getUntil().setTimezone(Timezone(utc=True))
+                            self.mRecurrences.changed()
+                            fixed.append(logProblem)
+                        else:
+                            unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    def check_cardinality_STATUS_Fix(self, fixed, unfixed, doFix):
+        """
+        Special for bug with STATUS where STATUS:CANCELLED is added alongside
+        another STATUS. In this case we want STATUS:CANCELLED to win.
+        """
+        for propname in self.propertyCardinality_STATUS_Fix:
+            if self.countProperty(propname) > 1:
+                logProblem = "[%s] Too many properties: %s" % (self.getType(), propname)
+                if doFix:
+                    # Check that one of them is STATUS:CANCELLED
+                    for prop in self.getProperties(propname):
+                        if prop.getTextValue().getValue().upper() == definitions.cICalProperty_STATUS_CANCELLED:
+                            self.removeProperties(propname)
+                            self.addProperty(Property(propname, definitions.cICalProperty_STATUS_CANCELLED))
+                            fixed.append(logProblem)
+                            break
+                    else:
+                        unfixed.append(logProblem)
+                else:
+                    unfixed.append(logProblem)
+
+
+    def _resetRecurrenceSet(self):
+        # May need to create items
+        self.mRecurrences = None
+        if ((self.countProperty(definitions.cICalProperty_RRULE) != 0) or
+            (self.countProperty(definitions.cICalProperty_RDATE) != 0) or
+            (self.countProperty(definitions.cICalProperty_EXRULE) != 0) or
+            (self.countProperty(definitions.cICalProperty_EXDATE) != 0)):
+
+            self.mRecurrences = RecurrenceSet()
+
+            # Get RRULEs
+            self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
+
+            # Get RDATEs
+            self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
+
+            # Get EXRULEs
+            self.loadValueRRULE(definitions.cICalProperty_EXRULE, self.mRecurrences, False)
+
+            # Get EXDATEs
+            self.loadValueRDATE(definitions.cICalProperty_EXDATE, self.mRecurrences, False)
+
+
+    def FixStartEnd(self):
+        # End is always greater than start if start exists
+        if self.mHasStart and self.mEnd <= self.mStart:
+            # Use the start
+            self.mEnd = self.mStart.duplicate()
+            self.mDuration = False
+
+            # Adjust to approriate non-inclusive end point
+            if self.mStart.isDateOnly():
+                self.mEnd.offsetDay(1)
+
+                # For all day events it makes sense to use duration
+                self.mDuration = True
+            else:
+                # Use end of current day
+                self.mEnd.offsetDay(1)
+                self.mEnd.setHHMMSS(0, 0, 0)
+
+
+    def expandPeriod(self, period, results):
+        # Check for recurrence and True master
+        if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
+                and not self.isRecurrenceInstance()):
+            # Expand recurrences within the range
+            items = []
+            self.mRecurrences.expand(self.mStart, period, items)
+
+            # Look for overridden recurrence items
+            cal = self.mParentComponent
+            if cal is not None:
+                # Remove recurrence instances from the list of items
+                recurs = []
+                cal.getRecurrenceInstancesIds(definitions.cICalComponent_VEVENT, self.getUID(), recurs)
+                recurs.sort()
+                if len(recurs) != 0:
+                    temp = []
+                    temp = set_difference(items, recurs)
+                    items = temp
+
+                    # Now get actual instances
+                    instances = []
+                    cal.getRecurrenceInstancesItems(definitions.cICalComponent_VEVENT, self.getUID(), instances)
+
+                    # Get list of each ones with RANGE
+                    prior = []
+                    future = []
+                    for iter in instances:
+                        if iter.isAdjustPrior():
+                            prior.append(iter)
+                        if iter.isAdjustFuture():
+                            future.append(iter)
+
+                    # Check for special behaviour
+                    if len(prior) + len(future) == 0:
+                        # Add each expanded item
+                        for iter in items:
+                            results.append(self.createExpanded(self, iter))
+                    else:
+                        # Sort each list first
+                        prior.sort(self.sort_by_dtstart)
+                        future.sort(self.sort_by_dtstart)
+
+                        # Add each expanded item
+                        for iter1 in items:
+
+                            # Now step through each using the slave item
+                            # instead of the master as appropriate
+                            slave = None
+
+                            # Find most appropriate THISANDPRIOR item
+                            for i in range(len(prior) - 1, 0, -1):
+                                riter2 = prior[i]
+                                if riter2.getStart() > iter1:
+                                    slave = riter2
+                                    break
+
+                            # Find most appropriate THISANDFUTURE item
+                            for i in range(len(future) - 1, 0, -1):
+                                riter2 = future.elementAt(i)
+                                if riter2.getStart() < iter1:
+                                    slave = riter2
+                                    break
+
+                            if slave is None:
+                                slave = self
+                            results.append(self.createExpanded(slave, iter1))
+                else:
+                    # Add each expanded item
+                    for iter in items:
+                        results.append(self.createExpanded(self, iter))
+
+        elif self.withinPeriod(period):
+            if self.isRecurrenceInstance():
+                rid = self.mRecurrenceID
+            else:
+                rid = None
+            results.append(ComponentExpanded(self, rid))
+
+
+    def withinPeriod(self, period):
+        # Check for recurrence
+        if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()):
+            items = []
+            self.mRecurrences.expand(self.mStart, period, items)
+            return len(items) != 0
+        else:
+            # Does event span the period (assume self.mEnd > self.mStart)
+            # Check start (inclusive) and end (exclusive)
+            if self.mEnd <= period.getStart() or self.mStart >= period.getEnd():
+                return False
+            else:
+                return True
+
+
+    def changedRecurrence(self):
+        # Clear cached values
+        if self.mRecurrences is not None:
+            self.mRecurrences.changed()
+
+
+    # Editing
+    def editSummary(self, summary):
+        # Updated cached value
+        self.mSummary = summary
+
+        # Remove existing items
+        self.editProperty(definitions.cICalProperty_SUMMARY, summary)
+
+
+    def editDetails(self, description, location):
+
+        # Edit existing items
+        self.editProperty(definitions.cICalProperty_DESCRIPTION, description)
+        self.editProperty(definitions.cICalProperty_LOCATION, location)
+
+
+    def editTiming(self):
+        # Updated cached values
+        self.mHasStart = False
+        self.mHasEnd = False
+        self.mDuration = False
+        self.mStart.setToday()
+        self.mEnd.setToday()
+
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+        self.removeProperties(definitions.cICalProperty_DUE)
+
+
+    def editTimingDue(self, due):
+        # Updated cached values
+        self.mHasStart = False
+        self.mHasEnd = True
+        self.mDuration = False
+        self.mStart = due
+        self.mEnd = due
+
+        # Remove existing DUE & DTSTART & DTEND & DURATION items
+        self.removeProperties(definitions.cICalProperty_DUE)
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+
+        # Now create properties
+        prop = Property(definitions.cICalProperty_DUE, due)
+        self.addProperty(prop)
+
+
+    def editTimingStartEnd(self, start, end):
+        # Updated cached values
+        self.mHasStart = self.mHasEnd = True
+        self.mStart = start
+        self.mEnd = end
+        self.mDuration = False
+        self.FixStartEnd()
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+        self.removeProperties(definitions.cICalProperty_DUE)
+
+        # Now create properties
+        prop = Property(definitions.cICalProperty_DTSTART, start)
+        self.addProperty(prop)
+
+        # If its an all day event and the end one day after the start, ignore it
+        temp = start.duplicate()
+        temp.offsetDay(1)
+        if not start.isDateOnly() or end != temp:
+            prop = Property(definitions.cICalProperty_DTEND, end)
+            self.addProperty(prop)
+
+
+    def editTimingStartDuration(self, start, duration):
+        # Updated cached values
+        self.mHasStart = True
+        self.mHasEnd = False
+        self.mStart = start
+        self.mEnd = start + duration
+        self.mDuration = True
+
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+        self.removeProperties(definitions.cICalProperty_DUE)
+
+        # Now create properties
+        prop = Property(definitions.cICalProperty_DTSTART, start)
+        self.addProperty(prop)
+
+        # If its an all day event and the duration is one day, ignore it
+        if (not start.isDateOnly() or (duration.getWeeks() != 0)
+                or (duration.getDays() > 1)):
+            prop = Property(definitions.cICalProperty_DURATION, duration)
+            self.addProperty(prop)
+
+
+    def editRecurrenceSet(self, recurs):
+        # Must have items
+        if self.mRecurrences is None:
+            self.mRecurrences = RecurrenceSet()
+
+        # Updated cached values
+        self.mRecurrences = recurs
+
+        # Remove existing RRULE, EXRULE, RDATE & EXDATE
+        self.removeProperties(definitions.cICalProperty_RRULE)
+        self.removeProperties(definitions.cICalProperty_EXRULE)
+        self.removeProperties(definitions.cICalProperty_RDATE)
+        self.removeProperties(definitions.cICalProperty_EXDATE)
+
+        # Now create properties
+        for iter in self.mRecurrences.getRules():
+            prop = Property(definitions.cICalProperty_RRULE, iter)
+            self.addProperty(prop)
+        for iter in self.getExrules():
+            prop = Property(definitions.cICalProperty_EXRULE, iter)
+            self.addProperty(prop)
+        for iter in self.mRecurrences.getDates():
+            prop = Property(definitions.cICalProperty_RDATE, iter)
+            self.addProperty(prop)
+        for iter in self.mRecurrences.getExdates():
+            prop = Property(definitions.cICalProperty_EXDATE, iter)
+            self.addProperty(prop)
+
+
+    def excludeRecurrence(self, start):
+        # Must have items
+        if self.mRecurrences is None:
+            return
+
+        # Add to recurrence set and clear cache
+        self.mRecurrences.subtract(start)
+
+        # Add property
+        prop = Property(definitions.cICalProperty_EXDATE, start)
+        self.addProperty(prop)
+
+
+    def excludeFutureRecurrence(self, start):
+        # Must have items
+        if self.mRecurrences is None:
+            return
+
+        # Adjust RRULES to end before start
+        self.mRecurrences.excludeFutureRecurrence(start)
+
+        # Remove existing RRULE & RDATE
+        self.removeProperties(definitions.cICalProperty_RRULE)
+        self.removeProperties(definitions.cICalProperty_RDATE)
+
+        # Now create properties
+        for iter in self.mRecurrences.getRules():
+            prop = Property(definitions.cICalProperty_RRULE, iter)
+            self.addProperty(prop)
+        for iter in  self.mRecurrences.getDates():
+            prop = Property(definitions.cICalProperty_RDATE, iter)
+            self.addProperty(prop)
+
+
+    def initFromMaster(self):
+        # Only if not master
+        if self.recurring():
+            # Redo this to get cached values from master
+            self.finalise()
+
+            # If this component does not have its own start property, use the
+            # recurrence id
+            # i.e. the start time of this instance has not changed - something
+            # else has
+            if not self.hasProperty(definitions.cICalProperty_DTSTART):
+                self.mStart = self.mRecurrenceID
+
+            # If this component does not have its own end/duration property,
+            # the determine
+            # the end from the master duration
+            if (not self.hasProperty(definitions.cICalProperty_DTEND) and
+                not self.hasProperty(definitions.cICalProperty_DURATION)):
+                # End is based on original events settings
+                self.mEnd = self.mStart + (self.mMaster.getEnd() - self.mMaster.getStart())
+
+            # If this instance has a duration, but no start of its own, then we
+            # need to readjust the end
+            # to account for the start being changed to the recurrence id
+            elif (self.hasProperty(definitions.cICalProperty_DURATION) and
+                  not self.hasProperty(definitions.cICalProperty_DTSTART)):
+                temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
+                self.mEnd = self.mStart + temp
+
+
+    def createExpanded(self, master, recurid):
+        return ComponentExpanded(master, recurid)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/definitions.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/definitions.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/definitions.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/definitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,350 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+#     5545 Components
+
+cICalComponent_VCALENDAR = "VCALENDAR"
+cICalComponent_VEVENT = "VEVENT"
+cICalComponent_VTODO = "VTODO"
+cICalComponent_VJOURNAL = "VJOURNAL"
+cICalComponent_VFREEBUSY = "VFREEBUSY"
+cICalComponent_VTIMEZONE = "VTIMEZONE"
+cICalComponent_VALARM = "VALARM"
+cICalComponent_STANDARD = "STANDARD"
+cICalComponent_DAYLIGHT = "DAYLIGHT"
+cICalComponent_UNKNOWN = "UNKNOWN"
+
+#     5545 Calendar Property Parameters
+
+#     5545 Section 3.2
+cICalParameter_ALTREP = "ALTREP"
+cICalParameter_CN = "CN"
+cICalParameter_CUTYPE = "CUTYPE"
+cICalParameter_DELEGATED_FROM = "DELEGATED-FROM"
+cICalParameter_DELEGATED_TO = "DELEGATED-TO"
+cICalParameter_DIR = "DIR"
+cICalParameter_ENCODING = "ENCODING"
+cICalParameter_FMTTYPE = "FMTTYPE"
+cICalParameter_FBTYPE = "FBTYPE"
+cICalParameter_LANGUAGE = "LANGUAGE"
+cICalParameter_MEMBER = "MEMBER"
+cICalParameter_PARTSTAT = "PARTSTAT"
+cICalParameter_RANGE = "RANGE"
+cICalParameter_RELATED = "RELATED"
+cICalParameter_RELTYPE = "RELTYPE"
+cICalParameter_ROLE = "ROLE"
+cICalParameter_RSVP = "RSVP"
+cICalParameter_RSVP_TRUE = "TRUE"
+cICalParameter_RSVP_FALSE = "FALSE"
+cICalParameter_SENT_BY = "SENT-BY"
+cICalParameter_TZID = "TZID"
+cICalParameter_VALUE = "VALUE"
+
+#     5545 Section 3.2.9
+cICalParameter_FBTYPE_FREE = "FREE"
+cICalParameter_FBTYPE_BUSY = "BUSY"
+cICalParameter_FBTYPE_BUSYUNAVAILABLE = "BUSY-UNAVAILABLE"
+cICalParameter_FBTYPE_BUSYTENTATIVE = "BUSY-TENTATIVE"
+
+#     5545 Section 3.2.12
+ePartStat_NeedsAction = 0
+ePartStat_Accepted = 1
+ePartStat_Declined = 2
+ePartStat_Tentative = 3
+ePartStat_Delegated = 4
+ePartStat_Completed = 5
+ePartStat_InProcess = 6
+
+cICalParameter_PARTSTAT_NEEDSACTION = "NEEDS-ACTION"
+cICalParameter_PARTSTAT_ACCEPTED = "ACCEPTED"
+cICalParameter_PARTSTAT_DECLINED = "DECLINED"
+cICalParameter_PARTSTAT_TENTATIVE = "TENTATIVE"
+cICalParameter_PARTSTAT_DELEGATED = "DELEGATED"
+cICalParameter_PARTSTAT_COMPLETED = "COMPLETE"
+cICalParameter_PARTSTAT_INPROCESS = "IN-PROCESS"
+
+#     5545 Section 3.2.13
+cICalParameter_RANGE_THISANDFUTURE = "THISANDFUTURE"
+cICalParameter_RANGE_THISANDPRIOR = "THISANDPRIOR"      # 2445 only
+
+#     5545 Section 3.2.14
+cICalParameter_RELATED_START = "START"
+cICalParameter_RELATED_END = "END"
+
+#     5545 Section 3.2.16
+ePartRole_Chair = 0
+ePartRole_Required = 1
+ePartRole_Optional = 2
+ePartRole_Non = 3
+
+cICalParameter_ROLE_CHAIR = "CHAIR"
+cICalParameter_ROLE_REQ_PART = "REQ-PARTICIPANT"
+cICalParameter_ROLE_OPT_PART = "OPT-PARTICIPANT"
+cICalParameter_ROLE_NON_PART = "NON-PARTICIPANT"
+
+#     5545 Section 3.2.3
+eCutype_Individual = 0
+eCutype_Group = 1
+eCutype_Resource = 2
+eCutype_Room = 3
+eCutype_Unknown = 4
+
+cICalParameter_CUTYPE_INDIVIDUAL = "INDIVIDUAL"
+cICalParameter_CUTYPE_GROUP = "GROUP"
+cICalParameter_CUTYPE_RESOURCE = "RESOURCE"
+cICalParameter_CUTYPE_ROOM = "ROOM"
+cICalParameter_CUTYPE_UNKNOWN = "UNKNOWN"
+
+#     5545 Value types
+
+#     5545 Section 3.3
+cICalValue_BINARY = "BINARY"
+cICalValue_BOOLEAN = "BOOLEAN"
+cICalValue_CAL_ADDRESS = "CAL-ADDRESS"
+cICalValue_DATE = "DATE"
+cICalValue_DATE_TIME = "DATE-TIME"
+cICalValue_DURATION = "DURATION"
+cICalValue_FLOAT = "FLOAT"
+cICalValue_INTEGER = "INTEGER"
+cICalValue_PERIOD = "PERIOD"
+cICalValue_RECUR = "RECUR"
+cICalValue_TEXT = "TEXT"
+cICalValue_TIME = "TIME"
+cICalValue_URI = "URI"
+cICalValue_UTC_OFFSET = "UTC-OFFSET"
+
+#     5545 Calendar Properties
+
+#     5545 Section  3.7
+
+cICalProperty_CALSCALE = "CALSCALE"
+cICalProperty_METHOD = "METHOD"
+cICalProperty_PRODID = "PRODID"
+cICalProperty_VERSION = "VERSION"
+
+#     Apple Extensions
+cICalProperty_XWRCALNAME = "X-WR-CALNAME"
+cICalProperty_XWRCALDESC = "X-WR-CALDESC"
+cICalProperty_XWRALARMUID = "X-WR-ALARMUID"
+
+#     5545 Component Property names
+
+#     5545 Section 3.8.1
+cICalProperty_ATTACH = "ATTACH"
+cICalProperty_CATEGORIES = "CATEGORIES"
+cICalProperty_CLASS = "CLASS"
+cICalProperty_COMMENT = "COMMENT"
+cICalProperty_DESCRIPTION = "DESCRIPTION"
+cICalProperty_GEO = "GEO"
+cICalProperty_LOCATION = "LOCATION"
+cICalProperty_PERCENT_COMPLETE = "PERCENT-COMPLETE"
+cICalProperty_PRIORITY = "PRIORITY"
+cICalProperty_RESOURCES = "RESOURCES"
+cICalProperty_STATUS = "STATUS"
+cICalProperty_SUMMARY = "SUMMARY"
+
+#     5545 Section 3.8.2
+cICalProperty_COMPLETED = "COMPLETED"
+cICalProperty_DTEND = "DTEND"
+cICalProperty_DUE = "DUE"
+cICalProperty_DTSTART = "DTSTART"
+cICalProperty_DURATION = "DURATION"
+cICalProperty_FREEBUSY = "FREEBUSY"
+cICalProperty_TRANSP = "TRANSP"
+cICalProperty_OPAQUE = "OPAQUE"
+cICalProperty_TRANSPARENT = "TRANSPARENT"
+
+#     5545 Section 3.8.3
+cICalProperty_TZID = "TZID"
+cICalProperty_TZNAME = "TZNAME"
+cICalProperty_TZOFFSETFROM = "TZOFFSETFROM"
+cICalProperty_TZOFFSETTO = "TZOFFSETTO"
+cICalProperty_TZURL = "TZURL"
+
+#     5545 Section 3.8.4
+cICalProperty_ATTENDEE = "ATTENDEE"
+cICalProperty_CONTACT = "CONTACT"
+cICalProperty_ORGANIZER = "ORGANIZER"
+cICalProperty_RECURRENCE_ID = "RECURRENCE-ID"
+cICalProperty_RELATED_TO = "RELATED-TO"
+cICalProperty_URL = "URL"
+cICalProperty_UID = "UID"
+
+#     5545 Section 3.8.5
+cICalProperty_EXDATE = "EXDATE"
+cICalProperty_EXRULE = "EXRULE"     # 2445 only
+cICalProperty_RDATE = "RDATE"
+cICalProperty_RRULE = "RRULE"
+
+#     5545 Section 3.8.6
+cICalProperty_ACTION = "ACTION"
+cICalProperty_REPEAT = "REPEAT"
+cICalProperty_TRIGGER = "TRIGGER"
+
+#     5545 Section 3.8.7
+cICalProperty_CREATED = "CREATED"
+cICalProperty_DTSTAMP = "DTSTAMP"
+cICalProperty_LAST_MODIFIED = "LAST-MODIFIED"
+cICalProperty_SEQUENCE = "SEQUENCE"
+
+#     5545 Section 3.8.8.3
+cICalProperty_REQUEST_STATUS = "REQUEST-STATUS"
+
+#     Enums
+#     Use ascending order for sensible sorting
+
+#     5545 Section 3.3.10
+
+eRecurrence_SECONDLY = 0
+eRecurrence_MINUTELY = 1
+eRecurrence_HOURLY = 2
+eRecurrence_DAILY = 3
+eRecurrence_WEEKLY = 4
+eRecurrence_MONTHLY = 5
+eRecurrence_YEARLY = 6
+
+eRecurrence_FREQ = 0
+eRecurrence_UNTIL = 1
+eRecurrence_COUNT = 2
+eRecurrence_INTERVAL = 3
+eRecurrence_BYSECOND = 4
+eRecurrence_BYMINUTE = 5
+eRecurrence_BYHOUR = 6
+eRecurrence_BYDAY = 7
+eRecurrence_BYMONTHDAY = 8
+eRecurrence_BYYEARDAY = 9
+eRecurrence_BYWEEKNO = 10
+eRecurrence_BYMONTH = 11
+eRecurrence_BYSETPOS = 12
+eRecurrence_WKST = 13
+
+cICalValue_RECUR_FREQ = "FREQ"
+cICalValue_RECUR_FREQ_LEN = 5
+
+cICalValue_RECUR_SECONDLY = "SECONDLY"
+cICalValue_RECUR_MINUTELY = "MINUTELY"
+cICalValue_RECUR_HOURLY = "HOURLY"
+cICalValue_RECUR_DAILY = "DAILY"
+cICalValue_RECUR_WEEKLY = "WEEKLY"
+cICalValue_RECUR_MONTHLY = "MONTHLY"
+cICalValue_RECUR_YEARLY = "YEARLY"
+
+cICalValue_RECUR_UNTIL = "UNTIL"
+cICalValue_RECUR_COUNT = "COUNT"
+
+cICalValue_RECUR_INTERVAL = "INTERVAL"
+cICalValue_RECUR_BYSECOND = "BYSECOND"
+cICalValue_RECUR_BYMINUTE = "BYMINUTE"
+cICalValue_RECUR_BYHOUR = "BYHOUR"
+cICalValue_RECUR_BYDAY = "BYDAY"
+cICalValue_RECUR_BYMONTHDAY = "BYMONTHDAY"
+cICalValue_RECUR_BYYEARDAY = "BYYEARDAY"
+cICalValue_RECUR_BYWEEKNO = "BYWEEKNO"
+cICalValue_RECUR_BYMONTH = "BYMONTH"
+cICalValue_RECUR_BYSETPOS = "BYSETPOS"
+cICalValue_RECUR_WKST = "WKST"
+
+eRecurrence_WEEKDAY_SU = 0
+eRecurrence_WEEKDAY_MO = 1
+eRecurrence_WEEKDAY_TU = 2
+eRecurrence_WEEKDAY_WE = 3
+eRecurrence_WEEKDAY_TH = 4
+eRecurrence_WEEKDAY_FR = 5
+eRecurrence_WEEKDAY_SA = 6
+
+cICalValue_RECUR_WEEKDAY_SU = "SU"
+cICalValue_RECUR_WEEKDAY_MO = "MO"
+cICalValue_RECUR_WEEKDAY_TU = "TU"
+cICalValue_RECUR_WEEKDAY_WE = "WE"
+cICalValue_RECUR_WEEKDAY_TH = "TH"
+cICalValue_RECUR_WEEKDAY_FR = "FR"
+cICalValue_RECUR_WEEKDAY_SA = "SA"
+
+#     5545 Section 3.8.1.11
+eStatus_VEvent_None = 0
+eStatus_VEvent_Confirmed = 1
+eStatus_VEvent_Tentative = 2
+eStatus_VEvent_Cancelled = 3
+
+eStatus_VToDo_None = 0
+eStatus_VToDo_NeedsAction = 1
+eStatus_VToDo_InProcess = 2
+eStatus_VToDo_Completed = 3
+eStatus_VToDo_Cancelled = 4
+
+eStatus_VJournal_None = 0
+eStatus_VJournal_Final = 1
+eStatus_VJournal_Draft = 2
+eStatus_VJournal_Cancelled = 3
+
+cICalProperty_STATUS_TENTATIVE = "TENTATIVE"
+cICalProperty_STATUS_CONFIRMED = "CONFIRMED"
+cICalProperty_STATUS_CANCELLED = "CANCELLED"
+cICalProperty_STATUS_NEEDS_ACTION = "NEEDS-ACTION"
+cICalProperty_STATUS_COMPLETED = "COMPLETED"
+cICalProperty_STATUS_IN_PROCESS = "IN-PROCESS"
+cICalProperty_STATUS_DRAFT = "DRAFT"
+cICalProperty_STATUS_FINAL = "FINAL"
+
+#     5545 Section 3.8.6.1
+eAction_VAlarm_Audio = 0
+eAction_VAlarm_Display = 1
+eAction_VAlarm_Email = 2
+eAction_VAlarm_Procedure = 3
+eAction_VAlarm_Unknown = 4
+
+cICalProperty_ACTION_AUDIO = "AUDIO"
+cICalProperty_ACTION_DISPLAY = "DISPLAY"
+cICalProperty_ACTION_EMAIL = "EMAIL"
+cICalProperty_ACTION_PROCEDURE = "PROCEDURE"
+
+#     Extensions: draft-daboo-calendar-availability-02
+
+#     Section 3.1
+cICalComponent_VAVAILABILITY = "VAVAILABILITY"
+cICalComponent_AVAILABLE = "AVAILABLE"
+
+#     Section 3.2
+cICalProperty_BUSYTYPE = "BUSYTYPE"
+
+#     Extensions: draft-daboo-valarm-extensions-03
+
+#     Section 5
+eAction_VAlarm_URI = 5
+cICalProperty_ACTION_URI = "URI"
+
+#     Section 7.1
+cICalProperty_ACKNOWLEDGED = "ACKNOWLEDGED"
+
+eAction_VAlarm_None = 6
+cICalProperty_ACTION_NONE = "NONE"
+
+#     Mulberry extensions
+cICalProperty_ACTION_X_SPEAKTEXT = "X-MULBERRY-SPEAK-TEXT"
+cICalProperty_ALARM_X_LASTTRIGGER = "X-MULBERRY-LAST-TRIGGER"
+
+cICalProperty_ALARM_X_ALARMSTATUS = "X-MULBERRY-ALARM-STATUS"
+
+eAlarm_Status_Pending = 0
+eAlarm_Status_Completed = 1
+eAlarm_Status_Disabled = 2
+
+cICalProperty_ALARM_X_ALARMSTATUS_PENDING = "PENDING"
+cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED = "COMPLETED"
+cICalProperty_ALARM_X_ALARMSTATUS_DISABLED = "DISABLED"
+
+cICalParameter_ORGANIZER_X_IDENTITY = "X-MULBERRY-IDENTITY"
+cICalParameter_ATTENDEE_X_NEEDS_ITIP = "X-MULBERRY-NEEDS-ITIP"

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/freebusy.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/freebusy.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/freebusy.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/freebusy.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,56 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+class FreeBusy(object):
+
+    FREE = 0
+    BUSYTENTATIVE = 1
+    BUSYUNAVAILABLE = 2
+    BUSY = 3
+
+    def __init__(self, type=None, period=None):
+
+        self.mType = type if type else FreeBusy.FREE
+        self.mPeriod = period.duplicate() if period is not None else None
+
+
+    def duplicate(self):
+        return FreeBusy(self.mType, self.mPeriod)
+
+
+    def setType(self, type):
+        self.mType = type
+
+
+    def getType(self):
+        return self.mType
+
+
+    def setPeriod(self, period):
+        self.mPeriod = period.duplicate()
+
+
+    def getPeriod(self):
+        return self.mPeriod
+
+
+    def isPeriodOverlap(self, period):
+        return self.mPeriod.isPeriodOverlap(period)
+
+
+    def resolveOverlaps(self, fb):
+        # TODO:
+        pass

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/itipdefinitions.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/itipdefinitions.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/itipdefinitions.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/itipdefinitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,42 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+#     2446 Section 3
+
+cICalMethod_PUBLISH = "PUBLISH"
+cICalMethod_REQUEST = "REQUEST"
+cICalMethod_REFRESH = "REFRESH"
+cICalMethod_CANCEL = "CANCEL"
+cICalMethod_ADD = "ADD"
+cICalMethod_REPLY = "REPLY"
+cICalMethod_COUNTER = "COUNTER"
+cICalMethod_DECLINECOUNTER = "DECLINECOUNTER"
+
+#     2447 Section 2.4
+cICalMIMEMethod_PUBLISH = "publish"
+cICalMIMEMethod_REQUEST = "request"
+cICalMIMEMethod_REFRESH = "refresh"
+cICalMIMEMethod_CANCEL = "cancel"
+cICalMIMEMethod_ADD = "add"
+cICalMIMEMethod_REPLY = "reply"
+cICalMIMEMethod_COUNTER = "counter"
+cICalMIMEMethod_DECLINECOUNTER = "declinecounter"
+
+cICalMIMEComponent_VEVENT = "vevent"
+cICalMIMEComponent_VTODO = "vtodo"
+cICalMIMEComponent_VJOURNAL = "vjournal"
+cICalMIMEComponent_VFREEBUSY = "vfreebusy"
+cICalMIMEComponent_VAVAILABILITY = "vavailability"

Added: PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,351 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.parameter import Parameter
+from pycalendar.datetime import DateTime
+from pycalendar.datetimevalue import DateTimeValue
+from pycalendar.duration import Duration
+from pycalendar.durationvalue import DurationValue
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.recurrence import Recurrence
+from pycalendar.icalendar.recurrencevalue import RecurrenceValue
+from pycalendar.icalendar.requeststatusvalue import RequestStatusValue
+from pycalendar.multivalue import MultiValue
+from pycalendar.period import Period
+from pycalendar.periodvalue import PeriodValue
+from pycalendar.property import PropertyBase
+from pycalendar.utcoffsetvalue import UTCOffsetValue
+from pycalendar.value import Value
+
+class Property(PropertyBase):
+
+    sDefaultValueTypeMap = {
+
+        # 5545 Section 3.7
+        definitions.cICalProperty_CALSCALE         : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_METHOD           : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_PRODID           : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_VERSION          : Value.VALUETYPE_TEXT,
+
+        # 5545 Section 3.8.1
+        definitions.cICalProperty_ATTACH           : Value.VALUETYPE_URI,
+        definitions.cICalProperty_CATEGORIES       : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_CLASS            : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_COMMENT          : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_DESCRIPTION      : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_GEO              : Value.VALUETYPE_GEO,
+        definitions.cICalProperty_LOCATION         : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_PERCENT_COMPLETE : Value.VALUETYPE_INTEGER,
+        definitions.cICalProperty_PRIORITY         : Value.VALUETYPE_INTEGER,
+        definitions.cICalProperty_RESOURCES        : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_STATUS           : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_SUMMARY          : Value.VALUETYPE_TEXT,
+
+        # 5545 Section 3.8.2
+        definitions.cICalProperty_COMPLETED : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_DTEND     : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_DUE       : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_DTSTART   : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_DURATION  : Value.VALUETYPE_DURATION,
+        definitions.cICalProperty_FREEBUSY  : Value.VALUETYPE_PERIOD,
+        definitions.cICalProperty_TRANSP    : Value.VALUETYPE_TEXT,
+
+        # 5545 Section 3.8.3
+        definitions.cICalProperty_TZID         : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_TZNAME       : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_TZOFFSETFROM : Value.VALUETYPE_UTC_OFFSET,
+        definitions.cICalProperty_TZOFFSETTO   : Value.VALUETYPE_UTC_OFFSET,
+        definitions.cICalProperty_TZURL        : Value.VALUETYPE_URI,
+
+        # 5545 Section 3.8.4
+        definitions.cICalProperty_ATTENDEE      : Value.VALUETYPE_CALADDRESS,
+        definitions.cICalProperty_CONTACT       : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_ORGANIZER     : Value.VALUETYPE_CALADDRESS,
+        definitions.cICalProperty_RECURRENCE_ID : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_RELATED_TO    : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_URL           : Value.VALUETYPE_URI,
+        definitions.cICalProperty_UID           : Value.VALUETYPE_TEXT,
+
+        # 5545 Section 3.8.5
+        definitions.cICalProperty_EXDATE : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_EXRULE : Value.VALUETYPE_RECUR, # 2445 only
+        definitions.cICalProperty_RDATE  : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_RRULE  : Value.VALUETYPE_RECUR,
+
+        # 5545 Section 3.8.6
+        definitions.cICalProperty_ACTION  : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_REPEAT  : Value.VALUETYPE_INTEGER,
+        definitions.cICalProperty_TRIGGER : Value.VALUETYPE_DURATION,
+
+        # 5545 Section 3.8.7
+        definitions.cICalProperty_CREATED       : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_DTSTAMP       : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_LAST_MODIFIED : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_SEQUENCE      : Value.VALUETYPE_INTEGER,
+
+        # 5545 Section 3.8.8
+        definitions.cICalProperty_REQUEST_STATUS : Value.VALUETYPE_REQUEST_STATUS,
+
+        # Extensions: draft-daboo-valarm-extensions-03
+        definitions.cICalProperty_ACKNOWLEDGED   : Value.VALUETYPE_DATETIME,
+
+        # Apple Extensions
+        definitions.cICalProperty_XWRCALNAME  : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_XWRCALDESC  : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_XWRALARMUID : Value.VALUETYPE_TEXT,
+
+        # Mulberry extensions
+        definitions.cICalProperty_ACTION_X_SPEAKTEXT  : Value.VALUETYPE_TEXT,
+        definitions.cICalProperty_ALARM_X_LASTTRIGGER : Value.VALUETYPE_DATETIME,
+        definitions.cICalProperty_ALARM_X_ALARMSTATUS : Value.VALUETYPE_TEXT,
+    }
+
+    sValueTypeMap = {
+
+        # 5545 Section 3.3
+        definitions.cICalValue_BINARY      : Value.VALUETYPE_BINARY,
+        definitions.cICalValue_BOOLEAN     : Value.VALUETYPE_BOOLEAN,
+        definitions.cICalValue_CAL_ADDRESS : Value.VALUETYPE_CALADDRESS,
+        definitions.cICalValue_DATE        : Value.VALUETYPE_DATE,
+        definitions.cICalValue_DATE_TIME   : Value.VALUETYPE_DATETIME,
+        definitions.cICalValue_DURATION    : Value.VALUETYPE_DURATION,
+        definitions.cICalValue_FLOAT       : Value.VALUETYPE_FLOAT,
+        definitions.cICalValue_INTEGER     : Value.VALUETYPE_INTEGER,
+        definitions.cICalValue_PERIOD      : Value.VALUETYPE_PERIOD,
+        definitions.cICalValue_RECUR       : Value.VALUETYPE_RECUR,
+        definitions.cICalValue_TEXT        : Value.VALUETYPE_TEXT,
+        definitions.cICalValue_TIME        : Value.VALUETYPE_TIME,
+        definitions.cICalValue_URI         : Value.VALUETYPE_URI,
+        definitions.cICalValue_UTC_OFFSET  : Value.VALUETYPE_UTC_OFFSET,
+    }
+
+    sTypeValueMap = {
+
+        # 5545 Section 3.3
+        Value.VALUETYPE_BINARY         : definitions.cICalValue_BINARY,
+        Value.VALUETYPE_BOOLEAN        : definitions.cICalValue_BOOLEAN,
+        Value.VALUETYPE_CALADDRESS     : definitions.cICalValue_CAL_ADDRESS,
+        Value.VALUETYPE_DATE           : definitions.cICalValue_DATE,
+        Value.VALUETYPE_DATETIME       : definitions.cICalValue_DATE_TIME,
+        Value.VALUETYPE_DURATION       : definitions.cICalValue_DURATION,
+        Value.VALUETYPE_FLOAT          : definitions.cICalValue_FLOAT,
+        Value.VALUETYPE_GEO            : definitions.cICalValue_FLOAT,
+        Value.VALUETYPE_INTEGER        : definitions.cICalValue_INTEGER,
+        Value.VALUETYPE_PERIOD         : definitions.cICalValue_PERIOD,
+        Value.VALUETYPE_RECUR          : definitions.cICalValue_RECUR,
+        Value.VALUETYPE_TEXT           : definitions.cICalValue_TEXT,
+        Value.VALUETYPE_REQUEST_STATUS : definitions.cICalValue_TEXT,
+        Value.VALUETYPE_TIME           : definitions.cICalValue_TIME,
+        Value.VALUETYPE_URI            : definitions.cICalValue_URI,
+        Value.VALUETYPE_UTC_OFFSET     : definitions.cICalValue_UTC_OFFSET,
+    }
+
+    sMultiValues = set((
+        definitions.cICalProperty_CATEGORIES,
+        definitions.cICalProperty_RESOURCES,
+        definitions.cICalProperty_FREEBUSY,
+        definitions.cICalProperty_EXDATE,
+        definitions.cICalProperty_RDATE,
+    ))
+
+    sVariant = "ical"
+
+    sValue = definitions.cICalParameter_VALUE
+    sText = definitions.cICalValue_TEXT
+
+    @classmethod
+    def registerDefaultValue(cls, propname, valuetype):
+        if propname not in cls.sDefaultValueTypeMap:
+            cls.sDefaultValueTypeMap[propname] = valuetype
+
+
+    def __init__(self, name=None, value=None, valuetype=None):
+
+        self.mName = name if name is not None else ""
+        self.mParameters = {}
+        self.mValue = None
+
+        # The None check speeds up .duplicate()
+        if value is None:
+            pass
+
+        # Order these based on most likely occurrence to speed up this method
+        elif isinstance(value, str):
+            self._init_attr_value_text(value, valuetype if valuetype else self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN))
+
+        elif isinstance(value, DateTime):
+            self._init_attr_value_datetime(value)
+
+        elif isinstance(value, Duration):
+            self._init_attr_value_duration(value)
+
+        elif isinstance(value, Recurrence):
+            self._init_attr_value_recur(value)
+
+        elif isinstance(value, Period):
+            self._init_attr_value_period(value)
+
+        elif isinstance(value, int):
+            self._init_attr_value_int(value)
+
+        elif isinstance(value, list):
+            if name.upper() == definitions.cICalProperty_REQUEST_STATUS:
+                self._init_attr_value_requeststatus(value)
+            else:
+                period_list = False
+                if len(value) != 0:
+                    period_list = isinstance(value[0], Period)
+                if period_list:
+                    self._init_attr_value_periodlist(value)
+                else:
+                    self._init_attr_value_datetimelist(value)
+
+        elif isinstance(value, UTCOffsetValue):
+            self._init_attr_value_utcoffset(value)
+
+
+    def duplicate(self):
+        other = Property(self.mName)
+        for attrname, attrs in self.mParameters.items():
+            other.mParameters[attrname] = [i.duplicate() for i in attrs]
+        other.mValue = self.mValue.duplicate()
+
+        return other
+
+
+    def __hash__(self):
+        return hash((
+            self.mName,
+            tuple([tuple(self.mParameters[attrname]) for attrname in sorted(self.mParameters.keys())]),
+            self.mValue,
+        ))
+
+
+    def __eq__(self, other):
+        if not isinstance(other, Property):
+            return False
+        return self.mName == other.mName and self.mValue == other.mValue and self.mParameters == other.mParameters
+
+
+    def getRecurrenceValue(self):
+
+        if isinstance(self.mValue, RecurrenceValue):
+            return self.mValue
+        else:
+            return None
+
+
+    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}
+        """
+
+        # Look for TZID parameter
+        if value_type in (Value.VALUETYPE_TIME, Value.VALUETYPE_DATETIME) and self.hasParameter(definitions.cICalParameter_TZID):
+            tzid = self.getParameterValue(definitions.cICalParameter_TZID)
+
+            if isinstance(self.mValue, DateTimeValue):
+                self.mValue.getValue().setTimezoneID(tzid)
+
+            elif isinstance(self.mValue, MultiValue):
+                for item in self.mValue.getValues():
+                    if isinstance(item, DateTimeValue):
+                        item.getValue().setTimezoneID(tzid)
+
+
+    # Creation
+    def _init_attr_value_requeststatus(self, reqstatus):
+        # Value
+        self.mValue = RequestStatusValue(reqstatus)
+
+        # Parameters
+        self.setupValueParameter()
+
+
+    def _init_attr_value_datetime(self, dt):
+        # Value
+        self.mValue = DateTimeValue(value=dt)
+
+        # Parameters
+        self.setupValueParameter()
+
+        # Look for timezone
+        if not dt.isDateOnly() and dt.local():
+            if definitions.cICalParameter_TZID in self.mParameters:
+                del self.mParameters[definitions.cICalParameter_TZID]
+            self.mParameters.setdefault(definitions.cICalParameter_TZID, []).append(
+                    Parameter(name=definitions.cICalParameter_TZID, value=dt.getTimezoneID()))
+
+
+    def _init_attr_value_datetimelist(self, dtl):
+        # Value
+        date_only = (len(dtl) > 0) and dtl[0].isDateOnly()
+        if date_only:
+            self.mValue = MultiValue(Value.VALUETYPE_DATE)
+        else:
+            self.mValue = MultiValue(Value.VALUETYPE_DATETIME)
+
+        for dt in dtl:
+            self.mValue.addValue(DateTimeValue(dt))
+
+        # Parameters
+        self.setupValueParameter()
+
+        # Look for timezone
+        if ((len(dtl) > 0)
+                and not dtl[0].isDateOnly()
+                and dtl[0].local()):
+            if definitions.cICalParameter_TZID in self.mParameters:
+                del self.mParameters[definitions.cICalParameter_TZID]
+            self.mParameters.setdefault(definitions.cICalParameter_TZID, []).append(
+                    Parameter(name=definitions.cICalParameter_TZID, value=dtl[0].getTimezoneID()))
+
+
+    def _init_attr_value_periodlist(self, periodlist):
+        # Value
+        self.mValue = MultiValue(Value.VALUETYPE_PERIOD)
+        for period in periodlist:
+            self.mValue.addValue(PeriodValue(period))
+
+        # Parameters
+        self.setupValueParameter()
+
+
+    def _init_attr_value_duration(self, du):
+        # Value
+        self.mValue = DurationValue(value=du)
+
+        # Parameters
+        self.setupValueParameter()
+
+
+    def _init_attr_value_period(self, pe):
+        # Value
+        self.mValue = PeriodValue(value=pe)
+
+        # Parameters
+        self.setupValueParameter()
+
+
+    def _init_attr_value_recur(self, recur):
+        # Value
+        self.mValue = RecurrenceValue(value=recur)
+
+        # Parameters
+        self.setupValueParameter()

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py (from rev 11594, PyCalendar/branches/json-2/src/pycalendar/recurrence.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,1637 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar import xmlutils
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions, xmldefinitions
+from pycalendar.period import Period
+from pycalendar.valueutils import ValueMixin
+import cStringIO as StringIO
+import xml.etree.cElementTree as XML
+
+def WeekDayNumCompare_compare(w1, w2):
+
+    if w1[0] < w2[0]:
+        return -1
+    elif w1[0] > w2[0]:
+        return 1
+    elif w1[1] < w2[1]:
+        return -1
+    elif w1[1] > w2[1]:
+        return 1
+    else:
+        return 0
+
+
+
+def WeekDayNumSort_less_than(w1, w2):
+
+    return (w1[0] < w2[0]) or (w1[0] == w2[0]) and (w1[1] < w2[1])
+
+
+
+class Recurrence(ValueMixin):
+
+    cFreqMap = {
+        definitions.cICalValue_RECUR_SECONDLY : definitions.eRecurrence_SECONDLY,
+        definitions.cICalValue_RECUR_MINUTELY : definitions.eRecurrence_MINUTELY,
+        definitions.cICalValue_RECUR_HOURLY   : definitions.eRecurrence_HOURLY,
+        definitions.cICalValue_RECUR_DAILY    : definitions.eRecurrence_DAILY,
+        definitions.cICalValue_RECUR_WEEKLY   : definitions.eRecurrence_WEEKLY,
+        definitions.cICalValue_RECUR_MONTHLY  : definitions.eRecurrence_MONTHLY,
+        definitions.cICalValue_RECUR_YEARLY   : definitions.eRecurrence_YEARLY,
+    }
+
+    cFreqToXMLMap = {
+        definitions.eRecurrence_SECONDLY: xmldefinitions.recur_freq_secondly,
+        definitions.eRecurrence_MINUTELY: xmldefinitions.recur_freq_minutely,
+        definitions.eRecurrence_HOURLY: xmldefinitions.recur_freq_hourly,
+        definitions.eRecurrence_DAILY: xmldefinitions.recur_freq_daily,
+        definitions.eRecurrence_WEEKLY: xmldefinitions.recur_freq_weekly,
+        definitions.eRecurrence_MONTHLY: xmldefinitions.recur_freq_monthly,
+        definitions.eRecurrence_YEARLY: xmldefinitions.recur_freq_yearly,
+    }
+
+    cRecurMap = {
+        definitions.cICalValue_RECUR_FREQ       : definitions.eRecurrence_FREQ,
+        definitions.cICalValue_RECUR_UNTIL      : definitions.eRecurrence_UNTIL,
+        definitions.cICalValue_RECUR_COUNT      : definitions.eRecurrence_COUNT,
+        definitions.cICalValue_RECUR_INTERVAL   : definitions.eRecurrence_INTERVAL,
+        definitions.cICalValue_RECUR_BYSECOND   : definitions.eRecurrence_BYSECOND,
+        definitions.cICalValue_RECUR_BYMINUTE   : definitions.eRecurrence_BYMINUTE,
+        definitions.cICalValue_RECUR_BYHOUR     : definitions.eRecurrence_BYHOUR,
+        definitions.cICalValue_RECUR_BYDAY      : definitions.eRecurrence_BYDAY,
+        definitions.cICalValue_RECUR_BYMONTHDAY : definitions.eRecurrence_BYMONTHDAY,
+        definitions.cICalValue_RECUR_BYYEARDAY  : definitions.eRecurrence_BYYEARDAY,
+        definitions.cICalValue_RECUR_BYWEEKNO   : definitions.eRecurrence_BYWEEKNO,
+        definitions.cICalValue_RECUR_BYMONTH    : definitions.eRecurrence_BYMONTH,
+        definitions.cICalValue_RECUR_BYSETPOS   : definitions.eRecurrence_BYSETPOS,
+        definitions.cICalValue_RECUR_WKST       : definitions.eRecurrence_WKST,
+    }
+
+    cWeekdayMap = {
+        definitions.cICalValue_RECUR_WEEKDAY_SU : definitions.eRecurrence_WEEKDAY_SU,
+        definitions.cICalValue_RECUR_WEEKDAY_MO : definitions.eRecurrence_WEEKDAY_MO,
+        definitions.cICalValue_RECUR_WEEKDAY_TU : definitions.eRecurrence_WEEKDAY_TU,
+        definitions.cICalValue_RECUR_WEEKDAY_WE : definitions.eRecurrence_WEEKDAY_WE,
+        definitions.cICalValue_RECUR_WEEKDAY_TH : definitions.eRecurrence_WEEKDAY_TH,
+        definitions.cICalValue_RECUR_WEEKDAY_FR : definitions.eRecurrence_WEEKDAY_FR,
+        definitions.cICalValue_RECUR_WEEKDAY_SA : definitions.eRecurrence_WEEKDAY_SA,
+    }
+
+    cWeekdayRecurMap = dict([(v, k) for k, v in cWeekdayMap.items()])
+
+    cUnknownIndex = -1
+
+    def __init__(self):
+        self.init_Recurrence()
+
+
+    def duplicate(self):
+        other = Recurrence()
+
+        other.mFreq = self.mFreq
+
+        other.mUseCount = self.mUseCount
+        other.mCount = self.mCount
+        other.mUseUntil = self.mUseUntil
+        if other.mUseUntil:
+            other.mUntil = self.mUntil.duplicate()
+
+        other.mInterval = self.mInterval
+        if self.mBySeconds is not None:
+            other.mBySeconds = self.mBySeconds[:]
+        if self.mByMinutes is not None:
+            other.mByMinutes = self.mByMinutes[:]
+        if self.mByHours is not None:
+            other.mByHours = self.mByHours[:]
+        if self.mByDay is not None:
+            other.mByDay = self.mByDay[:]
+        if self.mByMonthDay is not None:
+            other.mByMonthDay = self.mByMonthDay[:]
+        if self.mByYearDay is not None:
+            other.mByYearDay = self.mByYearDay[:]
+        if self.mByWeekNo is not None:
+            other.mByWeekNo = self.mByWeekNo[:]
+        if self.mByMonth is not None:
+            other.mByMonth = self.mByMonth[:]
+        if self.mBySetPos is not None:
+            other.mBySetPos = self.mBySetPos[:]
+        other.mWeekstart = self.mWeekstart
+
+        other.mCached = self.mCached
+        if self.mCacheStart:
+            other.mCacheStart = self.mCacheStart.duplicate()
+        if self.mCacheUpto:
+            other.mCacheUpto = self.mCacheUpto.duplicate()
+        other.mFullyCached = self.mFullyCached
+        if self.mRecurrences:
+            other.mRecurrences = self.mRecurrences[:]
+
+        return other
+
+
+    def init_Recurrence(self):
+        self.mFreq = definitions.eRecurrence_YEARLY
+
+        self.mUseCount = False
+        self.mCount = 0
+
+        self.mUseUntil = False
+        self.mUntil = None
+
+        self.mInterval = 1
+        self.mBySeconds = None
+        self.mByMinutes = None
+        self.mByHours = None
+        self.mByDay = None
+        self.mByMonthDay = None
+        self.mByYearDay = None
+        self.mByWeekNo = None
+        self.mByMonth = None
+        self.mBySetPos = None
+        self.mWeekstart = definitions.eRecurrence_WEEKDAY_MO
+
+        self.mCached = False
+        self.mCacheStart = None
+        self.mCacheUpto = None
+        self.mFullyCached = False
+        self.mRecurrences = None
+
+
+    def __hash__(self):
+        return hash((
+            self.mFreq,
+            self.mUseCount,
+            self.mCount,
+            self.mUseUntil,
+            self.mUntil,
+            self.mInterval,
+            tuple(self.mBySeconds) if self.mBySeconds else None,
+            tuple(self.mByMinutes) if self.mByMinutes else None,
+            tuple(self.mByHours) if self.mByHours else None,
+            tuple(self.mByDay) if self.mByDay else None,
+            tuple(self.mByMonthDay) if self.mByMonthDay else None,
+            tuple(self.mByYearDay) if self.mByYearDay else None,
+            tuple(self.mByWeekNo) if self.mByWeekNo else None,
+            tuple(self.mByMonth) if self.mByMonth else None,
+            tuple(self.mBySetPos) if self.mBySetPos else None,
+            self.mWeekstart,
+        ))
+
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+
+    def __eq__(self, other):
+        if not isinstance(other, Recurrence):
+            return False
+        return self.equals(other)
+
+
+    def equals(self, comp):
+        return (self.mFreq == comp.mFreq) \
+                and (self.mUseCount == comp.mUseCount) and (self.mCount == comp.mCount) \
+                and (self.mUseUntil == comp.mUseUntil) and (self.mUntil == comp.mUntil) \
+                and (self.mInterval == comp.mInterval) \
+                and self.equalsNum(self.mBySeconds, comp.mBySeconds) \
+                and self.equalsNum(self.mByMinutes, comp.mByMinutes) \
+                and self.equalsNum(self.mByHours, comp.mByHours) \
+                and self.equalsDayNum(self.mByDay, comp.mByDay) \
+                and self.equalsNum(self.mByMonthDay, comp.mByMonthDay) \
+                and self.equalsNum(self.mByYearDay, comp.mByYearDay) \
+                and self.equalsNum(self.mByWeekNo, comp.mByWeekNo) \
+                and self.equalsNum(self.mByMonth, comp.mByMonth) \
+                and self.equalsNum(self.mBySetPos, comp.mBySetPos) \
+                and (self.mWeekstart == comp.mWeekstart)
+
+
+    def equalsNum(self, items1, items2):
+        # Check sizes first
+        if items1 is None:
+            items1 = []
+        if items2 is None:
+            items2 = []
+        if len(items1) != len(items2):
+            return False
+        elif len(items1) == 0:
+            return True
+
+        # Copy and sort each one for comparison
+        temp1 = items1[:]
+        temp2 = items2[:]
+        temp1.sort()
+        temp2.sort()
+
+        for i in range(0, len(temp1)):
+            if temp1[i] != temp2[i]:
+                return False
+        return True
+
+
+    def equalsDayNum(self, items1, items2):
+        # Check sizes first
+        if items1 is None:
+            items1 = []
+        if items2 is None:
+            items2 = []
+        if len(items1) != len(items2):
+            return False
+        elif len(items1) == 0:
+            return True
+
+        # Copy and sort each one for comparison
+        temp1 = items1[:]
+        temp2 = items2[:]
+        temp1.sort()
+        temp2.sort()
+
+        for i in range(0, len(temp1)):
+            if temp1[i] != temp2[i]:
+                return False
+        return True
+
+
+    def _setAndclearIfChanged(self, attr, value):
+        if getattr(self, attr) != value:
+            self.clear()
+            setattr(self, attr, value)
+
+
+    def getFreq(self):
+        return self.mFreq
+
+
+    def setFreq(self, freq):
+        self._setAndclearIfChanged("mFreq", freq)
+
+
+    def getUseUntil(self):
+        return self.mUseUntil
+
+
+    def setUseUntil(self, use_until):
+        self._setAndclearIfChanged("mUseUntil", use_until)
+
+
+    def getUntil(self):
+        return self.mUntil
+
+
+    def setUntil(self, until):
+        self._setAndclearIfChanged("mUntil", until)
+
+
+    def getUseCount(self):
+        return self.mUseCount
+
+
+    def setUseCount(self, use_count):
+        self._setAndclearIfChanged("mUseCount", use_count)
+
+
+    def getCount(self):
+        return self.mCount
+
+
+    def setCount(self, count):
+        self._setAndclearIfChanged("mCount", count)
+
+
+    def getInterval(self):
+        return self.mInterval
+
+
+    def setInterval(self, interval):
+        self._setAndclearIfChanged("mInterval", interval)
+
+
+    def getByMonth(self):
+        return self.mByMonth
+
+
+    def setByMonth(self, by):
+        self._setAndclearIfChanged("mByMonth", by[:])
+
+
+    def getByMonthDay(self):
+        return self.mByMonthDay
+
+
+    def setByMonthDay(self, by):
+        self._setAndclearIfChanged("mByMonthDay", by[:])
+
+
+    def getByYearDay(self):
+        return self.mByYearDay
+
+
+    def setByYearDay(self, by):
+        self._setAndclearIfChanged("mByYearDay", by[:])
+
+
+    def getByDay(self):
+        return self.mByDay
+
+
+    def setByDay(self, by):
+        self._setAndclearIfChanged("mByDay", by[:])
+
+
+    def getBySetPos(self):
+        return self.mBySetPos
+
+
+    def setBySetPos(self, by):
+        self._setAndclearIfChanged("mBySetPos", by[:])
+
+
+    def parse(self, data):
+        self.init_Recurrence()
+
+        # Tokenise using ''
+        tokens = data.split(";")
+        tokens.reverse()
+
+        if len(tokens) == 0:
+            raise ValueError("Recurrence: Invalid recurrence rule value")
+
+        while len(tokens) != 0:
+            # Get next token
+            token = tokens.pop()
+            try:
+                tname, tvalue = token.split("=")
+            except ValueError:
+                raise ValueError("Recurrence: Invalid token '%s'" % (token,))
+
+            # Determine token type
+            index = Recurrence.cRecurMap.get(tname, Recurrence.cUnknownIndex)
+            if index == Recurrence.cUnknownIndex:
+                raise ValueError("Recurrence: Invalid token '%s'" % (tname,))
+
+            # Parse remainder based on index
+            if index == definitions.eRecurrence_FREQ:
+                # Get the FREQ value
+                index = Recurrence.cFreqMap.get(tvalue, Recurrence.cUnknownIndex)
+                if index == Recurrence.cUnknownIndex:
+                    raise ValueError("Recurrence: Invalid FREQ value")
+                self.mFreq = index
+
+            elif index == definitions.eRecurrence_UNTIL:
+                if self.mUseCount:
+                    raise ValueError("Recurrence: Can't have both UNTIL and COUNT")
+                self.mUseUntil = True
+                if self.mUntil is None:
+                    self.mUntil = DateTime()
+                try:
+                    self.mUntil.parse(tvalue)
+                except ValueError:
+                    raise ValueError("Recurrence: Invalid UNTIL value")
+
+            elif index == definitions.eRecurrence_COUNT:
+                if self.mUseUntil:
+                    raise ValueError("Recurrence: Can't have both UNTIL and COUNT")
+                self.mUseCount = True
+                try:
+                    self.mCount = int(tvalue)
+                except ValueError:
+                    raise ValueError("Recurrence: Invalid COUNT value")
+
+                # Must not be less than one
+                if self.mCount < 1:
+                    raise ValueError("Recurrence: Invalid COUNT value")
+
+            elif index == definitions.eRecurrence_INTERVAL:
+                try:
+                    self.mInterval = int(tvalue)
+                except ValueError:
+                    raise ValueError("Recurrence: Invalid INTERVAL value")
+
+                # Must NOT be less than one
+                if self.mInterval < 1:
+                    raise ValueError("Recurrence: Invalid INTERVAL value")
+
+            elif index == definitions.eRecurrence_BYSECOND:
+                if self.mBySeconds is not None:
+                    raise ValueError("Recurrence: Only one BYSECOND allowed")
+                self.mBySeconds = []
+                self.parseList(tvalue, self.mBySeconds, 0, 60, errmsg="Recurrence: Invalid BYSECOND value")
+
+            elif index == definitions.eRecurrence_BYMINUTE:
+                if self.mByMinutes is not None:
+                    raise ValueError("Recurrence: Only one BYMINUTE allowed")
+                self.mByMinutes = []
+                self.parseList(tvalue, self.mByMinutes, 0, 59, errmsg="Recurrence: Invalid BYMINUTE value")
+
+            elif index == definitions.eRecurrence_BYHOUR:
+                if self.mByHours is not None:
+                    raise ValueError("Recurrence: Only one BYHOUR allowed")
+                self.mByHours = []
+                self.parseList(tvalue, self.mByHours, 0, 23, errmsg="Recurrence: Invalid BYHOUR value")
+
+            elif index == definitions.eRecurrence_BYDAY:
+                if self.mByDay is not None:
+                    raise ValueError("Recurrence: Only one BYDAY allowed")
+                self.mByDay = []
+                self.parseListDW(tvalue, self.mByDay, errmsg="Recurrence: Invalid BYDAY value")
+
+            elif index == definitions.eRecurrence_BYMONTHDAY:
+                if self.mByMonthDay is not None:
+                    raise ValueError("Recurrence: Only one BYMONTHDAY allowed")
+                self.mByMonthDay = []
+                self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg="Recurrence: Invalid BYMONTHDAY value")
+
+            elif index == definitions.eRecurrence_BYYEARDAY:
+                if self.mByYearDay is not None:
+                    raise ValueError("Recurrence: Only one BYYEARDAY allowed")
+                self.mByYearDay = []
+                self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg="Recurrence: Invalid BYYEARDAY value")
+
+            elif index == definitions.eRecurrence_BYWEEKNO:
+                if self.mByWeekNo is not None:
+                    raise ValueError("Recurrence: Only one BYWEEKNO allowed")
+                self.mByWeekNo = []
+                self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg="Recurrence: Invalid BYWEEKNO value")
+
+            elif index == definitions.eRecurrence_BYMONTH:
+                if self.mByMonth is not None:
+                    raise ValueError("Recurrence: Only one BYMONTH allowed")
+                self.mByMonth = []
+                self.parseList(tvalue, self.mByMonth, 1, 12, errmsg="Recurrence: Invalid BYMONTH value")
+
+            elif index == definitions.eRecurrence_BYSETPOS:
+                if self.mBySetPos is not None:
+                    raise ValueError("Recurrence: Only one BYSETPOS allowed")
+                self.mBySetPos = []
+                self.parseList(tvalue, self.mBySetPos, allowNegative=True, errmsg="Recurrence: Invalid BYSETPOS value")
+
+            elif index == definitions.eRecurrence_WKST:
+                index = Recurrence.cWeekdayMap.get(tvalue, Recurrence.cUnknownIndex)
+                if (index == Recurrence.cUnknownIndex):
+                    raise ValueError("Recurrence: Invalid WKST value")
+                self.mWeekstart = index
+
+
+    def parseList(self, txt, list, min=None, max=None, allowNegative=False, errmsg=""):
+
+        if "," in txt:
+            tokens = txt.split(",")
+        else:
+            tokens = (txt,)
+
+        for token in tokens:
+            value = int(token)
+            if not allowNegative and value < 0:
+                raise ValueError(errmsg)
+            avalue = abs(value)
+            if min is not None and avalue < min:
+                raise ValueError(errmsg)
+            if max is not None  and avalue > max:
+                raise ValueError(errmsg)
+            list.append(value)
+
+
+    def parseListDW(self, txt, list, errmsg=""):
+
+        if "," in txt:
+            tokens = txt.split(",")
+        else:
+            tokens = (txt,)
+
+        for token in tokens:
+            # Get number if present
+            num = 0
+            if (len(token) > 0) and token[0] in "+-1234567890":
+                offset = 0
+                while (offset < len(token)) and token[offset] in "+-1234567890":
+                    offset += 1
+
+                num = int(token[0:offset])
+                token = token[offset:]
+
+                anum = abs(num)
+                if anum < 1:
+                    raise ValueError(errmsg)
+                if anum > 53:
+                    raise ValueError(errmsg)
+
+            # Get day
+            index = Recurrence.cWeekdayMap.get(token, Recurrence.cUnknownIndex)
+            if (index == Recurrence.cUnknownIndex):
+                raise ValueError(errmsg)
+            wday = index
+
+            list.append((num, wday))
+
+
+    def generate(self, os):
+        try:
+            os.write(definitions.cICalValue_RECUR_FREQ)
+            os.write("=")
+
+            if self.mFreq == definitions.eRecurrence_SECONDLY:
+                os.write(definitions.cICalValue_RECUR_SECONDLY)
+
+            elif self.mFreq == definitions.eRecurrence_MINUTELY:
+                os.write(definitions.cICalValue_RECUR_MINUTELY)
+
+            elif self.mFreq == definitions.eRecurrence_HOURLY:
+                os.write(definitions.cICalValue_RECUR_HOURLY)
+
+            elif self.mFreq == definitions.eRecurrence_DAILY:
+                os.write(definitions.cICalValue_RECUR_DAILY)
+
+            elif self.mFreq == definitions.eRecurrence_WEEKLY:
+                os.write(definitions.cICalValue_RECUR_WEEKLY)
+
+            elif self.mFreq == definitions.eRecurrence_MONTHLY:
+                os.write(definitions.cICalValue_RECUR_MONTHLY)
+
+            elif self.mFreq == definitions.eRecurrence_YEARLY:
+                os.write(definitions.cICalValue_RECUR_YEARLY)
+
+            if self.mUseCount:
+                os.write(";")
+                os.write(definitions.cICalValue_RECUR_COUNT)
+                os.write("=")
+                os.write(str(self.mCount))
+            elif self.mUseUntil:
+                os.write(";")
+                os.write(definitions.cICalValue_RECUR_UNTIL)
+                os.write("=")
+                self.mUntil.generate(os)
+
+            if self.mInterval > 1:
+                os.write(";")
+                os.write(definitions.cICalValue_RECUR_INTERVAL)
+                os.write("=")
+                os.write(str(self.mInterval))
+
+            self.generateList(os, definitions.cICalValue_RECUR_BYSECOND, self.mBySeconds)
+            self.generateList(os, definitions.cICalValue_RECUR_BYMINUTE, self.mByMinutes)
+            self.generateList(os, definitions.cICalValue_RECUR_BYHOUR, self.mByHours)
+
+            if (self.mByDay is not None) and (len(self.mByDay) != 0):
+                os.write(";")
+                os.write(definitions.cICalValue_RECUR_BYDAY)
+                os.write("=")
+                comma = False
+                for iter in self.mByDay:
+                    if comma:
+                        os.write(",")
+                    comma = True
+
+                    if iter[0] != 0:
+                        os.write(str(iter[0]))
+
+                    if iter[1] == definitions.eRecurrence_WEEKDAY_SU:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_MO:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_TU:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_WE:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_TH:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_FR:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
+
+                    elif iter[1] == definitions.eRecurrence_WEEKDAY_SA:
+                        os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
+
+            self.generateList(os, definitions.cICalValue_RECUR_BYMONTHDAY, self.mByMonthDay)
+            self.generateList(os, definitions.cICalValue_RECUR_BYYEARDAY, self.mByYearDay)
+            self.generateList(os, definitions.cICalValue_RECUR_BYWEEKNO, self.mByWeekNo)
+            self.generateList(os, definitions.cICalValue_RECUR_BYMONTH, self.mByMonth)
+            self.generateList(os, definitions.cICalValue_RECUR_BYSETPOS, self.mBySetPos)
+
+            # MO is the default so we do not need it
+            if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
+                os.write(";")
+                os.write(definitions.cICalValue_RECUR_WKST)
+                os.write("=")
+
+                if self.mWeekstart == definitions.eRecurrence_WEEKDAY_SU:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_MO:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TU:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_WE:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TH:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_FR:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
+
+                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_SA:
+                    os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
+
+        except:
+            pass
+
+
+    def generateList(self, os, title, items):
+
+        if (items is not None) and (len(items) != 0):
+            os.write(";")
+            os.write(title)
+            os.write("=")
+            comma = False
+            for e in items:
+                if comma:
+                    os.write(",")
+                comma = True
+                os.write(str(e))
+
+
+    def writeXML(self, node, namespace):
+
+        recur = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.value_recur))
+
+        freq = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_freq))
+        freq.text = self.cFreqToXMLMap[self.mFreq]
+
+        if self.mUseCount:
+            count = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_count))
+            count.text = str(self.mCount)
+        elif self.mUseUntil:
+            until = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_until))
+            self.mUntil.writeXML(until, namespace)
+
+        if self.mInterval > 1:
+            interval = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_interval))
+            interval.text = str(self.mInterval)
+
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_bysecond, self.mBySeconds)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_byminute, self.mByMinutes)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_byhour, self.mByHours)
+
+        if self.mByDay is not None and len(self.mByDay) != 0:
+            for iter in self.mByDay:
+                byday = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_byday))
+                data = ""
+                if iter[0] != 0:
+                    data = str(iter[0])
+                data += self.cWeekdayRecurMap.get(iter[1], "")
+                byday.text = data
+
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_bymonthday, self.mByMonthDay)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_byyearday, self.mByYearDay)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_byweekno, self.mByWeekNo)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_bymonth, self.mByMonth)
+        self.writeXMLList(recur, namespace, xmldefinitions.recur_bysetpos, self.mBySetPos)
+
+        # MO is the default so we do not need it
+        if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
+            wkst = XML.SubElement(recur, xmlutils.makeTag(namespace, xmldefinitions.recur_wkst))
+            wkst.text = self.cWeekdayRecurMap.get(self.mWeekstart, definitions.cICalValue_RECUR_WEEKDAY_MO)
+
+
+    def writeXMLList(self, node, namespace, name, items):
+        if items is not None and len(items) != 0:
+            for item in items:
+                child = XML.SubElement(node, xmlutils.makeTag(namespace, name))
+                child.text = str(item)
+
+
+    def hasBy(self):
+        return (self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
+                or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
+                or (self.mByHours is not None) and (len(self.mByHours) != 0) \
+                or (self.mByDay is not None) and (len(self.mByDay) != 0) \
+                or (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0) \
+                or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
+                or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0) \
+                or (self.mByMonth is not None) and (len(self.mByMonth) != 0) \
+                or (self.mBySetPos is not None) and (len(self.mBySetPos) != 0)
+
+
+    def isSimpleRule(self):
+        # One that has no BYxxx rules
+        return not self.hasBy()
+
+
+    def isAdvancedRule(self):
+        # One that has BYMONTH,
+        # BYMONTHDAY (with no negative value),
+        # BYDAY (with multiple unnumbered, or numbered with all the same number
+        # (1..4, -2, -1)
+        # BYSETPOS with +1, or -1 only
+        # no others
+
+        # First checks the ones we do not handle at all
+        if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
+                or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
+                or (self.mByHours is not None) and (len(self.mByHours) != 0) \
+                or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
+                or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0)):
+            return False
+
+        # Check BYMONTHDAY numbers (we can handle -7...-1, 1..31)
+        if self.mByMonthDay is not None:
+            for iter in self.mByMonthDay:
+                if (iter < -7) or (iter > 31) or (iter == 0):
+                    return False
+
+        # Check BYDAY numbers
+        if self.mByDay is not None:
+            number = 0
+            first = True
+            for iter in self.mByDay:
+
+                # Get the first number
+                if (first):
+                    number = iter[0]
+                    first = False
+
+                    # Check number range
+                    if (number > 4) or (number < -2):
+                        return False
+
+                # If current differs from last, then we have an error
+                elif number != iter[0]:
+                    return False
+
+        # Check BYSETPOS numbers
+        if self.mBySetPos is not None:
+            if len(self.mBySetPos) > 1:
+                return False
+            if (len(self.mBySetPos) == 1) and (self.mBySetPos[0] != -1) and (self.mBySetPos[0] != 1):
+                return False
+
+        # If we get here it must be OK
+        return True
+
+
+    def getUIDescription(self):
+        try:
+            # For now just use iCal item
+            sout = StringIO()
+            self.generate(sout)
+            result = sout.getvalue()
+        except:
+            result = ""
+
+        return result
+
+
+    def expand(self, start, range, items, float_offset=0):
+
+        # Must have recurrence list at this point
+        if self.mRecurrences is None:
+            self.mRecurrences = []
+
+        # Wipe cache if start is different
+        if self.mCached and (start != self.mCacheStart):
+            self.mCached = False
+            self.mFullyCached = False
+            self.mRecurrences = []
+
+        # Is the current cache complete or does it extend past the requested
+        # range end
+        if not self.mCached or not self.mFullyCached \
+                and (self.mCacheUpto is None or self.mCacheUpto < range.getEnd()):
+            cache_range = range.duplicate()
+
+            # If partially cached just cache from previous cache end up to new
+            # end
+            if self.mCached:
+                cache_range = Period(self.mCacheUpto, range.getEnd())
+
+            # Simple expansion is one where there is no BYXXX rule part
+            if not self.hasBy():
+                self.mFullyCached = self.simpleExpand(start, cache_range, self.mRecurrences, float_offset)
+            else:
+                self.mFullyCached = self.complexExpand(start, cache_range, self.mRecurrences, float_offset)
+
+            # Set cache values
+            self.mCached = True
+            self.mCacheStart = start
+            self.mCacheUpto = range.getEnd()
+
+        # Just return the cached items in the requested range
+        limited = not self.mFullyCached
+        for iter in self.mRecurrences:
+            if range.isDateWithinPeriod(iter):
+                items.append(iter)
+            else:
+                limited = True
+        return limited
+
+
+    def simpleExpand(self, start, range, items, float_offset):
+        start_iter = start.duplicate()
+        ctr = 0
+
+        if self.mUseUntil:
+            float_until = self.mUntil.duplicate()
+            if start.floating():
+                float_until.setTimezoneID(0)
+                float_until.offsetSeconds(float_offset)
+
+        while True:
+            # Exit if after period we want
+            if range.isDateAfterPeriod(start_iter):
+                return False
+
+            # Add current one to list
+            items.append(start_iter.duplicate())
+
+            # Get next item
+            start_iter.recur(self.mFreq, self.mInterval)
+
+            # Check limits
+            if self.mUseCount:
+                # Bump counter and exit if over
+                ctr += 1
+                if ctr >= self.mCount:
+                    return True
+            elif self.mUseUntil:
+                # Exit if next item is after until (its OK if its the same as
+                # UNTIL as UNTIL is inclusive)
+                if start_iter > float_until:
+                    return True
+
+
+    def complexExpand(self, start, range, items, float_offset):
+        start_iter = start.duplicate()
+        ctr = 0
+
+        if self.mUseUntil:
+            float_until = self.mUntil.duplicate()
+            if start.floating():
+                float_until.setTimezoneID(None)
+                float_until.offsetSeconds(float_offset)
+
+        # Always add the initial instance DTSTART
+        if self.mUseCount:
+            # Bump counter and exit if over
+            ctr += 1
+            if ctr >= self.mCount:
+                return True
+
+        # Need to re-initialise start based on BYxxx rules
+        while True:
+            # Behaviour is based on frequency
+            set_items = []
+
+            if self.mFreq == definitions.eRecurrence_SECONDLY:
+                self.generateSecondlySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_MINUTELY:
+                self.generateMinutelySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_HOURLY:
+                self.generateHourlySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_DAILY:
+                self.generateDailySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_WEEKLY:
+                self.generateWeeklySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_MONTHLY:
+                self.generateMonthlySet(start_iter, set_items)
+
+            elif self.mFreq == definitions.eRecurrence_YEARLY:
+                self.generateYearlySet(start_iter, set_items)
+
+            # Always sort the set as BYxxx rules may not be sorted
+            #set_items.sort(cmp=DateTime.sort)
+            set_items.sort(key=lambda x: x.getPosixTime())
+
+            # Process each one in the generated set
+            for iter in set_items:
+
+                # Ignore if it is before the actual start - we need this
+                # because the expansion
+                # can go back in time from the real start, but we must exclude
+                # those when counting
+                # even if they are not within the requested range
+                if iter < start:
+                    continue
+
+                # Exit if after period we want
+                if range.isDateAfterPeriod(iter):
+                    return False
+
+                # Exit if beyond the UNTIL limit
+                if self.mUseUntil:
+                    # Exit if next item is after until (its OK if its the same
+                    # as UNTIL as UNTIL is inclusive)
+                    if iter > float_until:
+                        return True
+
+                # Special for start instance
+                if (ctr == 1) and (start == iter):
+                    continue
+
+                # Add current one to list
+                items.append(iter)
+
+                # Check limits
+                if self.mUseCount:
+                    # Bump counter and exit if over
+                    ctr += 1
+                    if ctr >= self.mCount:
+                        return True
+
+            # Exit if after period we want
+            if range.isDateAfterPeriod(start_iter):
+                return False
+
+            # Get next item
+            start_iter.recur(self.mFreq, self.mInterval)
+
+
+    def clear(self):
+        self.mCached = False
+        self.mFullyCached = False
+        if self.mRecurrences is not None:
+            self.mRecurrences = []
+
+
+    # IMPORTANT ExcludeFutureRecurrence assumes mCacheStart is setup with the
+    # owning VEVENT's DTSTART
+    # Currently this method is only called when a recurrence is being removed
+    # so
+    # the recurrence data should be cached
+
+    # Exclude dates on or after the chosen one
+    def excludeFutureRecurrence(self, exclude):
+        # Expand the rule up to the exclude date
+        items = []
+        period = Period()
+        period.init(self.mCacheStart, exclude)
+        self.expand(self.mCacheStart, period, items)
+
+        # Adjust UNTIL or add one if no COUNT
+        if self.getUseUntil() or not self.getUseCount():
+            # The last one is just less than the exclude date
+            if len(items) != 0:
+                # Now use the data as the UNTIL
+                self.mUseUntil = True
+                self.mUntil = items[-1]
+
+        # Adjust COUNT
+        elif self.getUseCount():
+            # The last one is just less than the exclude date
+            self.mUseCount = True
+            self.mCount = len(items)
+
+        # Now clear out the cached set after making changes
+        self.clear()
+
+
+    def generateYearlySet(self, start, items):
+        # All possible BYxxx are valid, though some combinations are not
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            items[:] = self.byMonthExpand(items)
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoExpand(items)
+
+        if (self.mByYearDay is not None) and (len(self.mByYearDay) != 0):
+            items[:] = self.byYearDayExpand(items)
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayExpand(items)
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            # BYDAY is complicated:
+            # if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
+            # contracts the recurrence set
+            # else it expands it, but the expansion depends on the frequency
+            # and other BYxxx periodicities
+
+            if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
+                    or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
+                items[:] = self.byDayLimit(items)
+            elif (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+                items[:] = self.byDayExpandWeekly(items)
+            elif (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+                items[:] = self.byDayExpandMonthly(items)
+            else:
+                items[:] = self.byDayExpandYearly(items)
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourExpand(items)
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteExpand(items)
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondExpand(items)
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateMonthlySet(self, start, items):
+        # Cannot have BYYEARDAY and BYWEEKNO
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYWEEKNO
+
+        # No BYYEARDAY
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayExpand(items)
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            # BYDAY is complicated:
+            # if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
+            # contracts the recurrence set
+            # else it expands it, but the expansion depends on the frequency
+            # and other BYxxx periodicities
+
+            if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
+                    or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
+                items[:] = self.byDayLimit(items)
+            else:
+                items[:] = self.byDayExpandMonthly(items)
+
+        if ((self.mByHours is not None) and (len(self.mByHours) != 0)):
+            items[:] = self.byHourExpand(items)
+
+        if ((self.mByMinutes is not None) and (len(self.mByMinutes) != 0)):
+            items[:] = self.byMinuteExpand(items)
+
+        if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0)):
+            items[:] = self.bySecondExpand(items)
+
+        if ((self.mBySetPos is not None) and (len(self.mBySetPos) != 0)):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateWeeklySet(self, start, items):
+        # Cannot have BYYEARDAY and BYMONTHDAY
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYYEARDAY
+
+        # No BYMONTHDAY
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            items[:] = self.byDayExpandWeekly(items)
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourExpand(items)
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteExpand(items)
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondExpand(items)
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateDailySet(self, start, items):
+        # Cannot have BYYEARDAY
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYYEARDAY
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            items[:] = self.byDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourExpand(items)
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteExpand(items)
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondExpand(items)
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateHourlySet(self, start, items):
+        # Cannot have BYYEARDAY
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYYEARDAY
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            items[:] = self.byDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteExpand(items)
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondExpand(items)
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateMinutelySet(self, start, items):
+        # Cannot have BYYEARDAY
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYYEARDAY
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            items[:] = self.byDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondExpand(items)
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def generateSecondlySet(self, start, items):
+        # Cannot have BYYEARDAY
+
+        # Start with initial date-time
+        items.append(start.duplicate())
+
+        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
+            # BYMONTH limits the range of possible values
+            items[:] = self.byMonthLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
+            items[:] = self.byWeekNoLimit(items)
+            if (len(items) == 0):
+                return
+
+        # No BYYEARDAY
+
+        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
+            items[:] = self.byMonthDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByDay is not None) and (len(self.mByDay) != 0):
+            items[:] = self.byDayLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByHours is not None) and (len(self.mByHours) != 0):
+            items[:] = self.byHourLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
+            items[:] = self.byMinuteLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
+            items[:] = self.bySecondLimit(items)
+            if (len(items) == 0):
+                return
+
+        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
+            items[:] = self.bySetPosLimit(items)
+
+
+    def byMonthExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMONTH and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByMonth:
+                temp = iter1.duplicate()
+                temp.setMonth(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byWeekNoExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYWEEKNO and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByWeekNo:
+                temp = iter1.duplicate()
+                temp.setWeekNo(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byYearDayExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYYEARDAY and generating a new date-time for it
+            # and insert into output
+            for iter2 in self.mByYearDay:
+                temp = iter1.duplicate()
+                temp.setYearDay(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byMonthDayExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMONTHDAY and generating a new date-time for it
+            # and insert into output
+            for iter2 in self.mByMonthDay:
+                temp = iter1.duplicate()
+                temp.setMonthDay(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byDayExpandYearly(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYDAY and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByDay:
+                # Numeric value means specific instance
+                if iter2[0] != 0:
+                    temp = iter1.duplicate()
+                    temp.setDayOfWeekInYear(iter2[0], iter2[1])
+                    output.append(temp)
+                else:
+                    # Every matching day in the year
+                    for  i in range(1, 54):
+                        temp = iter1.duplicate()
+                        temp.setDayOfWeekInYear(i, iter2[1])
+                        if temp.getYear() == (iter1).getYear():
+                            output.append(temp)
+
+        return output
+
+
+    def byDayExpandMonthly(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYDAY and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByDay:
+                # Numeric value means specific instance
+                if iter2[0] != 0:
+                    temp = iter1.duplicate()
+                    temp.setDayOfWeekInMonth(iter2[0], iter2[1])
+                    output.append(temp)
+                else:
+                    # Every matching day in the month
+                    for i in range(1, 7):
+                        temp = iter1.duplicate()
+                        temp.setDayOfWeekInMonth(i, iter2[1])
+                        if temp.getMonth() == iter1.getMonth():
+                            output.append(temp)
+
+        return output
+
+
+    def byDayExpandWeekly(self, dates):
+        # Must take into account the WKST value
+
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYDAY and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByDay:
+                # Numeric values are meaningless so ignore them
+                if iter2[0] == 0:
+                    temp = iter1.duplicate()
+
+                    # Determine amount of offset to apply to temp to shift it
+                    # to the start of the week (backwards)
+                    week_start_offset = self.mWeekstart - temp.getDayOfWeek()
+                    if week_start_offset > 0:
+                        week_start_offset -= 7
+
+                    # Determine amount of offset from the start of the week to
+                    # the day we want (forwards)
+                    day_in_week_offset = iter2[1] - self.mWeekstart
+                    if day_in_week_offset < 0:
+                        day_in_week_offset += 7
+
+                    # Apply offsets
+                    temp.offsetDay(week_start_offset + day_in_week_offset)
+                    output.append(temp)
+
+        return output
+
+
+    def byHourExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYHOUR and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByHours:
+                temp = iter1.duplicate()
+                temp.setHours(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byMinuteExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMINUTE and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mByMinutes:
+                temp = iter1.duplicate()
+                temp.setMinutes(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def bySecondExpand(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYSECOND and generating a new date-time for it and
+            # insert into output
+            for iter2 in self.mBySeconds:
+                temp = iter1.duplicate()
+                temp.setSeconds(iter2)
+                output.append(temp)
+
+        return output
+
+
+    def byMonthLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMONTH and indicate keep if input month matches
+            keep = False
+            for iter2 in self.mByMonth:
+                keep = (iter1.getMonth() == iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def byWeekNoLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYWEEKNO and indicate keep if input month matches
+            keep = False
+            for iter2 in self.mByWeekNo:
+                keep = iter1.isWeekNo(iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def byMonthDayLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMONTHDAY and indicate keep if input month
+            # matches
+            keep = False
+            for iter2 in self.mByMonthDay:
+                keep = iter1.isMonthDay(iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def byDayLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYDAY and indicate keep if input month matches
+            keep = False
+            for iter2 in self.mByDay:
+                keep = iter1.isDayOfWeekInMonth(iter2[0], iter2[1])
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def byHourLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYHOUR and indicate keep if input hour matches
+            keep = False
+            for iter2 in self.mByHours:
+                keep = (iter1.getHours() == iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def byMinuteLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYMINUTE and indicate keep if input minute matches
+            keep = False
+            for iter2 in self.mByMinutes:
+                keep = (iter1.getMinutes() == iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def bySecondLimit(self, dates):
+        # Loop over all input items
+        output = []
+        for iter1 in dates:
+            # Loop over each BYSECOND and indicate keep if input second matches
+            keep = False
+            for iter2 in self.mBySeconds:
+                keep = (iter1.getSeconds() == iter2)
+                if keep:
+                    break
+
+            if keep:
+                output.append(iter1)
+
+        return output
+
+
+    def bySetPosLimit(self, dates):
+        # The input dates MUST be sorted in order for this to work properly
+        #dates.sort(cmp=DateTime.sort)
+        dates.sort(key=lambda x: x.getPosixTime())
+
+        # Loop over each BYSETPOS and extract the relevant component from the
+        # input array and add to the output
+        output = []
+        input_size = len(dates)
+        for iter in self.mBySetPos:
+            if iter > 0:
+                # Positive values are offset from the start
+                if iter <= input_size:
+                    output.append(dates[iter - 1])
+            elif iter < 0:
+                # Negative values are offset from the end
+                if -iter <= input_size:
+                    output.append(dates[input_size + iter])
+
+        return output

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrenceset.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/recurrenceset.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrenceset.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrenceset.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,312 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.utils import set_difference
+
+class RecurrenceSet(object):
+
+    def __init__(self):
+        self.mRrules = []
+        self.mExrules = []
+        self.mRdates = []
+        self.mExdates = []
+        self.mRperiods = []
+        self.mExperiods = []
+
+
+    def duplicate(self):
+        other = RecurrenceSet()
+        other.mRrules = [i.duplicate() for i in self.mRrules]
+        other.mExrules = [i.duplicate() for i in self.mExrules]
+        other.mRdates = [i.duplicate() for i in self.mRdates]
+        other.mExdates = [i.duplicate() for i in self.mExdates]
+        other.mRperiods = [i.duplicate() for i in self.mRperiods]
+        other.mExperiods = [i.duplicate() for i in self.mExperiods]
+        return other
+
+
+    def hasRecurrence(self):
+        return ((len(self.mRrules) != 0) or (len(self.mRdates) != 0) or (len(self.mRperiods) != 0)
+                    or (len(self.mExrules) != 0) or (len(self.mExdates) != 0)
+                    or (len(self.mExperiods) != 0))
+
+
+    def equals(self, comp):
+        # Look at RRULEs
+        if not self.equalsRules(self.mRrules, comp.self.mRrules):
+            return False
+
+        # Look at EXRULEs
+        if not self.equalsRules(self.mExrules, comp.self.mExrules):
+            return False
+
+        # Look at RDATEs
+        if not self.equalsDates(self.mRdates, comp.self.mRdates):
+            return False
+        if not self.equalsPeriods(self.mRperiods, comp.self.mRperiods):
+            return False
+
+        # Look at EXDATEs
+        if not self.equalsDates(self.mExdates, comp.self.mExdates):
+            return False
+        if not self.equalsPeriods(self.mExperiods, comp.self.mExperiods):
+            return False
+
+        # If we get here they match
+        return True
+
+
+    def equalsRules(self, rules1, rules2):
+        # Check sizes first
+        if len(rules1) != len(rules2):
+            return False
+        elif len(rules1) == 0:
+            return True
+
+        # Do sledge hammer O(n^2) approach as its not easy to sort these things
+        # for a smarter test.
+        # In most cases there will only be one rule anyway, so this should not
+        # be too painful.
+
+        temp2 = rules2[:]
+
+        for r1 in rules1:
+            found = False
+            for r2 in temp2:
+                if r1.equals(r2):
+                    # Remove the one found so it is not tested again
+                    temp2.remove(r2)
+                    found = True
+                    break
+
+            if not found:
+                return False
+
+        return True
+
+
+    def equalsDates(self, dates1, dates2):
+        # Check sizes first
+        if len(dates1) != len(dates2):
+            return False
+        elif len(dates1) == 0:
+            return True
+
+        # Copy each and sort for comparison
+        dt1 = dates1[:]
+        dt2 = dates2[:]
+
+        dt1.sort(key=lambda x: x.getPosixTime())
+        dt2.sort(key=lambda x: x.getPosixTime())
+
+        return dt1.equal(dt2)
+
+
+    def equalsPeriods(self, periods1, periods2):
+        # Check sizes first
+        if len(periods1) != len(periods2):
+            return False
+        elif len(periods1) == 0:
+            return True
+
+        # Copy each and sort for comparison
+        p1 = periods1[:]
+        p2 = periods2[:]
+
+        p1.sort()
+        p2.sort()
+
+        return p1.equal(p2)
+
+
+    def addRule(self, rule):
+        self.mRrules.append(rule)
+
+
+    def subtractRule(self, rule):
+        self.mExrules.append(rule)
+
+
+    def addDT(self, dt):
+        self.mRdates.append(dt)
+
+
+    def subtractDT(self, dt):
+        self.mExdates.append(dt)
+
+
+    def addPeriod(self, p):
+        self.mRperiods.append(p)
+
+
+    def subtractPeriod(self, p):
+        self.mExperiods.append(p)
+
+
+    def getRules(self):
+        return self.mRrules
+
+
+    def getExrules(self):
+        return self.mExrules
+
+
+    def getDates(self):
+        return self.mRdates
+
+
+    def getExdates(self):
+        return self.mExdates
+
+
+    def getPeriods(self):
+        return self.mRperiods
+
+
+    def getExperiods(self):
+        return self.mExperiods
+
+
+    def expand(self, start, range, items, float_offset=0):
+        # Need to return whether the limit was applied or not
+        limited = False
+
+        # Now create list of items to include
+        include = []
+
+        # Always include the initial DTSTART if within the range
+        if range.isDateWithinPeriod(start):
+            include.append(start)
+        else:
+            limited = True
+
+        # RRULES
+        for iter in self.mRrules:
+            if iter.expand(start, range, include, float_offset=float_offset):
+                limited = True
+
+        # RDATES
+        for iter in self.mRdates:
+            if range.isDateWithinPeriod(iter):
+                include.append(iter)
+            else:
+                limited = True
+        for iter in self.mRperiods:
+            if range.isPeriodOverlap(iter):
+                include.append(iter.getStart())
+            else:
+                limited = True
+
+        # Make sure the list is unique
+        include = [x for x in set(include)]
+        include.sort(key=lambda x: x.getPosixTime())
+
+        # Now create list of items to exclude
+        exclude = []
+
+        # EXRULES
+        for iter in self.mExrules:
+            iter.expand(start, range, exclude, float_offset=float_offset)
+
+        # EXDATES
+        for iter in self.mExdates:
+            if range.isDateWithinPeriod(iter):
+                exclude.append(iter)
+        for iter in self.mExperiods:
+            if range.isPeriodOverlap(iter):
+                exclude.append(iter.getStart())
+
+        # Make sure the list is unique
+        exclude = [x for x in set(exclude)]
+        exclude.sort(key=lambda x: x.getPosixTime())
+
+        # Add difference between to the two sets (include - exclude) to the
+        # results
+        items.extend(set_difference(include, exclude))
+        return limited
+
+
+    def changed(self):
+        # RRULES
+        for iter in self.mRrules:
+            iter.clear()
+
+        # EXRULES
+        for iter in self.mExrules:
+            iter.clear()
+
+
+    def excludeFutureRecurrence(self, exclude):
+        # Adjust RRULES to end before start
+        for iter in self.mRrules:
+            iter.excludeFutureRecurrence(exclude)
+
+        # Remove RDATES on or after start
+        self.mRdates.removeOnOrAfter(exclude)
+        for iter in self.mRperiods:
+            if iter > exclude:
+                self.mRperiods.remove(iter)
+
+
+    # UI operations
+    def isSimpleUI(self):
+        # Right now the Event dialog only handles a single RRULE (but we allow
+        # any number of EXDATES as deleted
+        # instances will appear as EXDATES)
+        if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
+                or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
+            return False
+
+        # Also, check the rule iteself
+        elif len(self.mRrules) == 1:
+            return self.mRrules.firstElement().isSimpleRule()
+        else:
+            return True
+
+
+    def isAdvancedUI(self):
+        # Right now the Event dialog only handles a single RRULE
+        if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
+                or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
+            return False
+
+        # Also, check the rule iteself
+        elif len(self.mRrules) == 1:
+            return self.mRrules.firstElement().isAdvancedRule()
+        else:
+            return True
+
+
+    def getUIRecurrence(self):
+        if len(self.mRrules) == 1:
+            return self.mRrules[0]
+        else:
+            return None
+
+
+    def getUIDescription(self):
+        # Check for anything
+        if not self.hasRecurrence():
+            return "No Recurrence"
+
+        # Look for a single RRULE and return its descriptor
+        if ((len(self.mRrules) == 1) and (len(self.mExrules) == 0) and (len(self.mRdates) == 0)
+                and (len(self.mExdates) == 0) and (len(self.mRperiods) == 0)
+                and (len(self.mExperiods) == 0)):
+            return self.mRrules.firstElement().getUIDescription()
+
+        # Indicate some form of complex recurrence
+        return "Multiple recurrence rules, dates or exclusions"

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/recurrencevalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,35 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import xmldefinitions
+from pycalendar.icalendar.recurrence import Recurrence
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
+
+class RecurrenceValue(WrapperValue, Value):
+
+    def __init__(self, value=None):
+        self.mValue = value if value is not None else Recurrence()
+
+
+    def getType(self):
+        return Value.VALUETYPE_RECUR
+
+
+    def writeXML(self, node, namespace):
+        self.mValue.writeXML(node, namespace)
+
+Value.registerType(Value.VALUETYPE_RECUR, RecurrenceValue, xmldefinitions.value_recur)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/requeststatusvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,103 @@
+##
+#    Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# iCalendar REQUEST-STATUS value
+
+from pycalendar import utils, xmlutils
+from pycalendar.icalendar import xmldefinitions
+from pycalendar.parser import ParserContext
+from pycalendar.value import Value
+import xml.etree.cElementTree as XML
+
+class RequestStatusValue(Value):
+    """
+    The value is a list of strings (either 2 or 3 items)
+    """
+
+    def __init__(self, value=None):
+        self.mValue = value if value is not None else ["2.0", "Success"]
+
+
+    def __hash__(self):
+        return hash(tuple(self.mValue))
+
+
+    def duplicate(self):
+        return RequestStatusValue(self.mValue[:])
+
+
+    def getType(self):
+        return Value.VALUETYPE_REQUEST_STATUS
+
+
+    def parse(self, data, variant="icalendar"):
+
+        result = utils.parseTextList(data, always_list=True)
+        if len(result) == 1:
+            if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
+                if ";" in result[0]:
+                    code, desc = result[0].split(";", 1)
+                else:
+                    code = result[0]
+                    desc = ""
+                rest = None
+            else:
+                raise ValueError
+        elif len(result) == 2:
+            code, desc = result
+            rest = None
+        elif len(result) == 3:
+            code, desc, rest = result
+        else:
+            if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
+                code, desc, rest = result[:3]
+            else:
+                raise ValueError
+
+        if "\\" in code and ParserContext.INVALID_REQUEST_STATUS_VALUE in (ParserContext.PARSER_IGNORE, ParserContext.PARSER_FIX):
+            code = code.replace("\\", "")
+        elif ParserContext.INVALID_REQUEST_STATUS_VALUE == ParserContext.PARSER_RAISE:
+            raise ValueError
+
+        # Decoding required
+        self.mValue = [code, desc, rest, ] if rest else [code, desc, ]
+
+
+    # os - StringIO object
+    def generate(self, os):
+        utils.generateTextList(os, self.mValue if len(self.mValue) < 3 or self.mValue[2] else self.mValue[:2])
+
+
+    def writeXML(self, node, namespace):
+        code = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_code))
+        code.text = self.mValue[0]
+
+        description = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_description))
+        description.text = self.mValue[1]
+
+        if len(self.mValue) == 3 and self.mValue[2]:
+            data = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_data))
+            data.text = self.mValue[1]
+
+
+    def getValue(self):
+        return self.mValue
+
+
+    def setValue(self, value):
+        self.mValue = value
+
+Value.registerType(Value.VALUETYPE_REQUEST_STATUS, RequestStatusValue, None)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_calendar.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_calendar.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_calendar.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_calendar.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,854 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.icalendar.property import Property
+from pycalendar.parser import ParserContext
+from pycalendar.period import Period
+import cStringIO as StringIO
+import difflib
+import unittest
+
+class TestCalendar(unittest.TestCase):
+
+    data = (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+X-WR-CALNAME:PayDay
+BEGIN:VTIMEZONE
+TZID:US/Eastern
+LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:DC3D0301C7790B38631F1FBB at ninevah.local
+DTSTART;VALUE=DATE:20040227
+DTSTAMP:20050211T173501Z
+RRULE:FREQ=MONTHLY;BYDAY=-1MO,-1TU,-1WE,-1TH,-1FR;BYSETPOS=-1
+SUMMARY:PAY DAY
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Alarm for Organizer!
+TRIGGER;RELATED=START:-PT15M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+ATTACH:http://example.com/test.jpg
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+ATTACH;ENCODING=BASE64;VALUE=BINARY:dGVzdA==
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VTIMEZONE
+TZID:America/Montreal
+LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VAVAILABILITY
+UID:20061005T133225Z-00001-availability at example.com
+DTSTART;TZID=America/Montreal:20060101T000000
+DTEND;TZID=America/Montreal:20060108T000000
+DTSTAMP:20061005T133225Z
+ORGANIZER:mailto:bernard at example.com
+BEGIN:AVAILABLE
+UID:20061005T133225Z-00001-A-availability at example.com
+DTSTART;TZID=America/Montreal:20060102T090000
+DTEND;TZID=America/Montreal:20060102T120000
+DTSTAMP:20061005T133225Z
+RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR
+SUMMARY:Monday\\, Wednesday and Friday from 9:00 to 12:00
+END:AVAILABLE
+BEGIN:AVAILABLE
+UID:20061005T133225Z-00001-A-availability at example.com
+RECURRENCE-ID;TZID=America/Montreal:20060106T090000
+DTSTART;TZID=America/Montreal:20060106T120000
+DTEND;TZID=America/Montreal:20060106T170000
+DTSTAMP:20061005T133225Z
+SUMMARY:Friday override from 12:00 to 17:00
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VTODO
+UID:event1 at ninevah.local
+CREATED:20060101T150000Z
+DTSTAMP:20051222T205953Z
+SUMMARY:event 1
+END:VTODO
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+BEGIN:X-COMPONENT
+UID:1234
+END:X-COMPONENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:PDT
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:PST
+TZOFFSETFROM:-0700
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:uid4
+DTSTART;TZID=US/Pacific:20100207T170000
+DTEND;TZID=US/Pacific:20100207T173000
+CREATED:20100203T013849Z
+DTSTAMP:20100203T013909Z
+SEQUENCE:3
+SUMMARY:New Event
+TRANSP:OPAQUE
+BEGIN:VALARM
+ACTION:AUDIO
+ATTACH:Basso
+TRIGGER:-PT20M
+X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+ATTACH:http://example.com/test.jpg
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+X-APPLE-STRUCTURED-LOCATION:geo:123.123,123.123
+X-Test:Some\, text.
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+ATTACH:http://example.com/test.jpg
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123
+X-Test:Some\, text.
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+)
+    data2 = (
+                (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+X-TEST:Testing
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+X-TEST:Testing
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                ),
+)
+
+
+    def testRoundtrip(self):
+
+
+        def _doRoundtrip(caldata, resultdata=None):
+            test1 = resultdata if resultdata is not None else caldata
+
+            cal = Calendar()
+            cal.parse(StringIO.StringIO(caldata))
+
+            s = StringIO.StringIO()
+            cal.generate(s)
+            test2 = s.getvalue()
+
+            self.assertEqual(
+                test1,
+                test2,
+                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+            )
+
+        for item in self.data:
+            _doRoundtrip(item)
+
+        for item1, item2 in self.data2:
+            _doRoundtrip(item1, item2)
+
+
+    def testRoundtripDuplicate(self):
+
+
+        def _doDuplicateRoundtrip(caldata):
+            cal = Calendar()
+            cal.parse(StringIO.StringIO(caldata))
+            cal = cal.duplicate()
+
+            s = StringIO.StringIO()
+            cal.generate(s)
+            self.assertEqual(caldata, s.getvalue())
+
+        for item in self.data:
+            _doDuplicateRoundtrip(item)
+
+
+    def testEquality(self):
+
+
+        def _doEquality(caldata):
+            cal1 = Calendar()
+            cal1.parse(StringIO.StringIO(caldata))
+
+            cal2 = Calendar()
+            cal2.parse(StringIO.StringIO(caldata))
+
+            self.assertEqual(cal1, cal2, "%s\n\n%s" % (cal1, cal2,))
+
+
+        def _doNonEquality(caldata):
+            cal1 = Calendar()
+            cal1.parse(StringIO.StringIO(caldata))
+
+            cal2 = Calendar()
+            cal2.parse(StringIO.StringIO(caldata))
+            cal2.addProperty(Property("X-FOO", "BAR"))
+
+            self.assertNotEqual(cal1, cal2)
+
+        for item in self.data:
+            _doEquality(item)
+            _doNonEquality(item)
+
+
+    def testParseComponent(self):
+
+        data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+        data2 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VTIMEZONE
+TZID:America/Montreal
+LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+        result = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VTIMEZONE
+TZID:America/Montreal
+LAST-MODIFIED:20040110T032845Z
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+        cal = Calendar()
+        cal.parse(StringIO.StringIO(data1))
+        cal.parseComponent(StringIO.StringIO(data2))
+        self.assertEqual(str(cal), result)
+
+
+    def testParseFail(self):
+
+        data = (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCARD
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+VERSION:2.0
+END:VCARD
+""".replace("\n", "\r\n"),
+
+"""BOGUS
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BOGUS
+
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+BOGUS
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+
+BOGUS
+""".replace("\n", "\r\n"),
+
+        )
+
+        for item in data:
+            self.assertRaises(InvalidData, Calendar.parseText, item)
+
+
+    def testParseBlank(self):
+
+        data = (
+"""
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""
+
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+
+
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+
+
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+        )
+
+        save = ParserContext.BLANK_LINES_IN_DATA
+        for item in data:
+            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
+            self.assertRaises(InvalidData, Calendar.parseText, item)
+
+            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
+            lines = item.split("\r\n")
+            result = "\r\n".join([line for line in lines if line]) + "\r\n"
+            self.assertEqual(str(Calendar.parseText(item)), result)
+
+        ParserContext.BLANK_LINES_IN_DATA = save
+
+
+    def testGetVEvents(self):
+
+        data = (
+            (
+                "Non-recurring match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20110601
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (DateTime(2011, 6, 1),),
+            ),
+            (
+                "Non-recurring no-match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20110501
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (),
+            ),
+            (
+                "Recurring match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20110601
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (
+                    DateTime(2011, 6, 1),
+                    DateTime(2011, 6, 2),
+                ),
+            ),
+            (
+                "Recurring no match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20110501
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (),
+            ),
+            (
+                "Recurring with override match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART:20110601T120000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+RECURRENCE-ID;VALUE=DATE:20110602T120000
+DTSTART;VALUE=DATE:20110602T130000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (
+                    DateTime(2011, 6, 1, 12, 0, 0),
+                    DateTime(2011, 6, 2, 13, 0, 0),
+                ),
+            ),
+            (
+                "Recurring with override no match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART:20110501T120000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+RECURRENCE-ID;VALUE=DATE:20110502T120000
+DTSTART;VALUE=DATE:20110502T130000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (),
+            ),
+            (
+                "Recurring partial match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20110531
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (
+                    DateTime(2011, 6, 1),
+                ),
+            ),
+            (
+                "Recurring with override partial match",
+                """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART:20110531T120000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=DAILY;COUNT=2
+SUMMARY:New Year's Day
+END:VEVENT
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+RECURRENCE-ID;VALUE=DATE:20110601T120000
+DTSTART;VALUE=DATE:20110601T130000
+DURATION:P1D
+DTSTAMP:20020101T000000Z
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+                (
+                    DateTime(2011, 6, 1, 13, 0, 0),
+                ),
+            ),
+        )
+
+        for title, caldata, result in data:
+            calendar = Calendar.parseText(caldata)
+            instances = []
+            calendar.getVEvents(
+                Period(
+                    start=DateTime(2011, 6, 1),
+                    end=DateTime(2011, 7, 1),
+                ),
+                instances
+            )
+            instances = tuple([instance.getInstanceStart() for instance in instances])
+            self.assertEqual(instances, result, "Failed in %s: got %s, expected %s" % (title, instances, result))

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_componentrecur.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_componentrecur.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_componentrecur.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_componentrecur.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,66 @@
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar.calendar import Calendar
+import cStringIO as StringIO
+import unittest
+
+class TestCalendar(unittest.TestCase):
+
+    def testDuplicateWithRecurrenceChange(self):
+
+        data = (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;COUNT=400
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+)
+
+        cal1 = Calendar()
+        cal1.parse(StringIO.StringIO(data[0]))
+        cal2 = cal1.duplicate()
+        vevent = cal2.getComponents()[0]
+        rrules = vevent.getRecurrenceSet()
+        for rrule in rrules.getRules():
+            rrule.setUseCount(True)
+            rrule.setCount(400)
+            rrules.changed()
+
+        self.assertEqual(data[0], str(cal1))
+        self.assertEqual(data[1], str(cal2))

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_i18n.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_i18n.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_i18n.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_i18n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,74 @@
+# coding: utf-8
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.parameter import Parameter
+from pycalendar.icalendar.calendar import Calendar
+import cStringIO as StringIO
+import unittest
+
+class TestCalendar(unittest.TestCase):
+
+    def testAddCN(self):
+
+        data = (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+ORGANIZER:user01 at example.com
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+    "まだ",
+
+"""BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
+DTSTART;VALUE=DATE:20020101
+DTEND;VALUE=DATE:20020102
+DTSTAMP:20020101T000000Z
+RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
+ORGANIZER;CN=まだ:user01 at example.com
+SUMMARY:New Year's Day
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+        )
+
+        cal1 = Calendar()
+        cal1.parse(StringIO.StringIO(data[0]))
+
+        vevent = cal1.getComponents("VEVENT")[0]
+        organizer = vevent.getProperties("ORGANIZER")[0]
+        organizer.addParameter(Parameter("CN", data[1]))
+
+        cal2 = Calendar()
+        cal2.parse(StringIO.StringIO(data[2]))
+
+        self.assertEqual(str(cal1), str(cal2))

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_property.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,189 @@
+##
+#    Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.parameter import Parameter
+from pycalendar.exceptions import InvalidProperty
+from pycalendar.icalendar.property import Property
+from pycalendar.parser import ParserContext
+from pycalendar.value import Value
+import unittest
+
+class TestProperty(unittest.TestCase):
+
+    test_data = (
+        # Different value types
+        "ATTACH;VALUE=BINARY:VGVzdA==",
+        "attach;VALUE=BINARY:VGVzdA==",
+        "ORGANIZER:mailto:jdoe at example.com",
+        "DTSTART;TZID=US/Eastern:20060226T120000",
+        "DTSTART;VALUE=DATE:20060226",
+        "DTSTART:20060226T130000Z",
+        "X-FOO:BAR",
+        "DURATION:PT10M",
+        "duraTION:PT10M",
+        "SEQUENCE:1",
+        "RDATE:20060226T120000Z,20060227T120000Z",
+        "FREEBUSY:20060226T120000Z/20060227T120000Z",
+        "SUMMARY:Some \\ntext",
+        "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
+        "REQUEST-STATUS:2.0;Success",
+        "URI:http://www.example.com",
+        "TZOFFSETFROM:-0500",
+        "X-Test:Some\, text.",
+        "X-Test:Some:, text.",
+        "X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123",
+        "X-CALENDARSERVER-PRIVATE-COMMENT:This\\ntest\\nis\\, here.\\n",
+
+        # Various parameters
+        "DTSTART;TZID=\"Somewhere, else\":20060226T120000",
+        "ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:jdoe at example.com",
+        "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-APPLE-ABUID=ab\\://Work;X-TITLE=\"10\\n XX S. XXX Dr.\\nSuite XXX\\nXX XX XXXXX\\nUnited States\":\"geo:11.111111,-11.111111\"",
+
+        # Parameter escaping
+        "ATTENDEE;CN=My ^'Test^' Name;ROLE=CHAIR:mailto:jdoe at example.com",
+    )
+
+
+    def testParseGenerate(self):
+
+        for data in TestProperty.test_data:
+            prop = Property()
+            prop.parse(data)
+            propstr = str(prop).replace("\r\n ", "")
+            self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
+
+
+    def testEquality(self):
+
+        for data in TestProperty.test_data:
+            prop1 = Property()
+            prop1.parse(data)
+            prop2 = Property()
+            prop2.parse(data)
+            self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
+
+
+    def testParseBad(self):
+
+        test_bad_data = (
+            "DTSTART;TZID=US/Eastern:abc",
+            "DTSTART;VALUE=DATE:20060226T",
+            "DTSTART:20060226T120000A",
+            "X-FOO;:BAR",
+            "DURATION:A",
+            "SEQUENCE:b",
+            "RDATE:20060226T120000Z;20060227T120000Z",
+            "FREEBUSY:20060226T120000Z/ABC",
+            "SUMMARY:Some \\qtext",
+            "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,VE;BYSETPOS=-1",
+            "TZOFFSETFROM:-050",
+            """ATTENDEE;CN="\\";CUTYPE=INDIVIDUAL;PARTSTAT=X-UNDELIVERABLE:invalid:nomai
+ l""",
+        )
+        save = ParserContext.INVALID_ESCAPE_SEQUENCES
+        for data in test_bad_data:
+            ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
+            prop = Property()
+            self.assertRaises(InvalidProperty, prop.parse, data)
+        ParserContext.INVALID_ESCAPE_SEQUENCES = save
+
+
+    def testHash(self):
+
+        hashes = []
+        for item in TestProperty.test_data:
+            prop = Property()
+            prop.parse(item)
+            hashes.append(hash(prop))
+        hashes.sort()
+        for i in range(1, len(hashes)):
+            self.assertNotEqual(hashes[i - 1], hashes[i])
+
+
+    def testDefaultValueCreate(self):
+
+        test_data = (
+            ("ATTENDEE", "mailto:attendee at example.com", "ATTENDEE:mailto:attendee at example.com\r\n"),
+            ("attendee", "mailto:attendee at example.com", "attendee:mailto:attendee at example.com\r\n"),
+            ("ORGANIZER", "mailto:organizer at example.com", "ORGANIZER:mailto:organizer at example.com\r\n"),
+            ("ORGANizer", "mailto:organizer at example.com", "ORGANizer:mailto:organizer at example.com\r\n"),
+            ("URL", "http://example.com/tz1", "URL:http://example.com/tz1\r\n"),
+            ("TZURL", "http://example.com/tz2", "TZURL:http://example.com/tz2\r\n"),
+        )
+        for propname, propvalue, result in test_data:
+            prop = Property(name=propname, value=propvalue)
+            self.assertEqual(str(prop), result)
+
+
+    def testGEOValueRoundtrip(self):
+
+        data = "GEO:123.456,789.101"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(str(prop), data + "\r\n")
+
+
+    def testUnknownValueRoundtrip(self):
+
+        data = "X-FOO:Text, not escaped"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(str(prop), data + "\r\n")
+
+        prop = Property("X-FOO", "Text, not escaped")
+        self.assertEqual(str(prop), data + "\r\n")
+
+        data = "X-FOO:Text\\, escaped\\n"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(str(prop), data + "\r\n")
+
+        prop = Property("X-FOO", "Text\\, escaped\\n")
+        self.assertEqual(str(prop), data + "\r\n")
+
+
+    def testNewRegistrationValueRoundtrip(self):
+
+        Property.registerDefaultValue("X-SPECIAL-REGISTRATION", Value.VALUETYPE_TEXT)
+
+        data = "X-SPECIAL-REGISTRATION:Text\\, escaped\\n"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
+
+        prop = Property("X-SPECIAL-REGISTRATION", "Text, escaped\n")
+        self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
+
+
+    def testParameterEncodingDecoding(self):
+
+        prop = Property("X-FOO", "Test")
+        prop.addParameter(Parameter("X-BAR", "\"Check\""))
+        self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^':Test\r\n")
+
+        prop.addParameter(Parameter("X-BAR2", "Check\nThis\tOut\n"))
+        self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
+
+        data = "X-FOO;X-BAR=^'Check^':Test"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
+
+        data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
+        prop = Property()
+        prop.parse(data)
+        self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
+        self.assertEqual(prop.getParameterValue("X-BAR2"), "Check\nThis\tOut\n")

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py (from rev 11594, PyCalendar/branches/json-2/src/pycalendar/tests/test_recurrence.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,161 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.period import Period
+from pycalendar.icalendar.recurrence import Recurrence
+import unittest
+
+class TestRecurrence(unittest.TestCase):
+
+    items = (
+        "FREQ=DAILY",
+        "FREQ=YEARLY;COUNT=400",
+        "FREQ=MONTHLY;UNTIL=20110102",
+        "FREQ=MONTHLY;UNTIL=20110102T090000",
+        "FREQ=MONTHLY;UNTIL=20110102T100000Z",
+        "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
+
+        # These are from RFC5545 examples
+        "FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
+        "FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4",
+        "FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4",
+        "FREQ=YEARLY;BYDAY=2SU;BYMONTH=3",
+        "FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
+        "FREQ=DAILY;INTERVAL=2",
+        "FREQ=DAILY;COUNT=5;INTERVAL=10",
+        "FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1",
+        "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
+        "FREQ=WEEKLY;UNTIL=19971007T000000Z;BYDAY=TU,TH;WKST=SU",
+        "FREQ=WEEKLY;COUNT=10;BYDAY=TU,TH;WKST=SU",
+        "FREQ=WEEKLY;UNTIL=19971224T000000Z;INTERVAL=2;BYDAY=MO,WE,FR;WKST=SU",
+        "FREQ=WEEKLY;COUNT=8;INTERVAL=2;BYDAY=TU,TH;WKST=SU",
+        "FREQ=MONTHLY;BYMONTHDAY=-3",
+        "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
+        "FREQ=MONTHLY;COUNT=10;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,15",
+        "FREQ=YEARLY;COUNT=10;INTERVAL=3;BYYEARDAY=1,100,200",
+        "FREQ=YEARLY;BYDAY=MO;BYWEEKNO=20",
+        "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
+        "FREQ=DAILY;BYMINUTE=0,20,40;BYHOUR=9,10,11,12,13,14,15,16",
+    )
+
+
+    def testParse(self):
+
+        for item in TestRecurrence.items:
+            recur = Recurrence()
+            recur.parse(item)
+            self.assertEqual(recur.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
+
+
+    def testParseInvalid(self):
+
+        items = (
+            "",
+            "FREQ=",
+            "FREQ=MICROSECONDLY",
+            "FREQ=YEARLY;COUNT=ABC",
+            "FREQ=YEARLY;COUNT=123;UNTIL=20110102",
+            "FREQ=MONTHLY;UNTIL=20110102T",
+            "FREQ=MONTHLY;UNTIL=20110102t090000",
+            "FREQ=MONTHLY;UNTIL=20110102T100000z",
+            "FREQ=MONTHLY;UNTIL=20110102TAABBCCz",
+            "FREQ=MONTHLY;BYDAY=A",
+            "FREQ=MONTHLY;BYDAY=+1,3MO",
+            "FREQ=MONTHLY;BYHOUR=A",
+            "FREQ=MONTHLY;BYHOUR=54",
+        )
+
+        for item in items:
+            self.assertRaises(ValueError, Recurrence().parse, item)
+
+
+    def testEquality(self):
+
+        recur1 = Recurrence()
+        recur1.parse("FREQ=YEARLY;COUNT=400")
+        recur2 = Recurrence()
+        recur2.parse("COUNT=400;FREQ=YEARLY")
+
+        self.assertEqual(recur1, recur2)
+
+
+    def testInequality(self):
+
+        recur1 = Recurrence()
+        recur1.parse("FREQ=YEARLY;COUNT=400")
+        recur2 = Recurrence()
+        recur2.parse("COUNT=400;FREQ=YEARLY;BYMONTH=1")
+
+        self.assertNotEqual(recur1, recur2)
+
+
+    def testHash(self):
+
+        hashes = []
+        for item in TestRecurrence.items:
+            recur = Recurrence()
+            recur.parse(item)
+            hashes.append(hash(recur))
+        hashes.sort()
+        for i in range(1, len(hashes)):
+            self.assertNotEqual(hashes[i - 1], hashes[i])
+
+
+    def testByWeekNoExpand(self):
+
+        recur = Recurrence()
+        recur.parse("FREQ=YEARLY;BYWEEKNO=1,2")
+        start = DateTime(2013, 1, 1, 0, 0, 0)
+        end = DateTime(2017, 1, 1, 0, 0, 0)
+        items = []
+        range = Period(start, end)
+        recur.expand(start, range, items)
+        self.assertEqual(
+            items,
+            [
+                DateTime(2013, 1, 1, 0, 0, 0),
+                DateTime(2013, 1, 8, 0, 0, 0),
+                DateTime(2014, 1, 1, 0, 0, 0),
+                DateTime(2014, 1, 8, 0, 0, 0),
+                DateTime(2015, 1, 1, 0, 0, 0),
+                DateTime(2015, 1, 8, 0, 0, 0),
+                DateTime(2016, 1, 8, 0, 0, 0),
+                DateTime(2016, 1, 15, 0, 0, 0),
+            ],
+        )
+        print items
+
+
+    def testClearOnChange(self):
+
+        recur = Recurrence()
+        recur.parse("FREQ=DAILY")
+
+        start = DateTime(2013, 1, 1, 0, 0, 0)
+        end = DateTime(2017, 1, 1, 0, 0, 0)
+        range = Period(start, end)
+        items = []
+        recur.expand(start, range, items)
+        self.assertTrue(recur.mCached)
+        self.assertTrue(len(items) > 100)
+
+        recur.setUseCount(True)
+        recur.setCount(10)
+        self.assertFalse(recur.mCached)
+        items = []
+        recur.expand(start, range, items)
+        self.assertEqual(len(items), 10)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_requeststatus.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_requeststatus.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,95 @@
+##
+#    Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.parser import ParserContext
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.requeststatusvalue import RequestStatusValue
+import unittest
+
+class TestRequestStatus(unittest.TestCase):
+
+    def testParseValue(self):
+
+        items = (
+            "2.0;Success",
+            "2.0;Success\;here",
+            "2.0;Success;Extra",
+            "2.0;Success\;here;Extra",
+            "2.0;Success;Extra\;here",
+            "2.0;Success\;here;Extra\;here too",
+        )
+
+        for item in items:
+            req = RequestStatusValue()
+            req.parse(item, "icalendar")
+            self.assertEqual(req.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
+
+
+    def testBadValue(self):
+
+        bad_value = "2.0\;Success"
+        ok_value = "2.0;Success"
+
+        # Fix the value
+        oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
+        req = RequestStatusValue()
+        req.parse(bad_value, "icalendar")
+        self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
+
+        # Raise the value
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
+        req = RequestStatusValue()
+        self.assertRaises(ValueError, req.parse, bad_value)
+
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
+
+
+    def testTruncatedValue(self):
+
+        bad_value = "2.0"
+        ok_value = "2.0;"
+
+        # Fix the value
+        oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
+        req = RequestStatusValue()
+        req.parse(bad_value, "icalendar")
+        self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
+
+        # Raise the value
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
+        req = RequestStatusValue()
+        self.assertRaises(ValueError, req.parse, bad_value)
+
+        ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
+
+
+    def testParseProperty(self):
+
+        items = (
+            "REQUEST-STATUS:2.0;Success",
+            "REQUEST-STATUS:2.0;Success\;here",
+            "REQUEST-STATUS:2.0;Success;Extra",
+            "REQUEST-STATUS:2.0;Success\;here;Extra",
+            "REQUEST-STATUS:2.0;Success;Extra\;here",
+            "REQUEST-STATUS:2.0;Success\;here;Extra\;here too",
+        )
+
+        for item in items:
+            req = Property()
+            req.parse(item)
+            self.assertEqual(req.getText(), item + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_timezone.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_timezone.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_timezone.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_timezone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,235 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar.calendar import Calendar
+import unittest
+
+class TestCalendar(unittest.TestCase):
+
+    def testOffsets(self):
+
+        data = (
+                    ("""BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:America/New_York
+X-LIC-LOCATION:America/New_York
+BEGIN:STANDARD
+DTSTART:18831118T120358
+RDATE:18831118T120358
+TZNAME:EST
+TZOFFSETFROM:-045602
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19180331T020000
+RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19181027T020000
+RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19200101T000000
+RDATE:19200101T000000
+RDATE:19420101T000000
+RDATE:19460101T000000
+RDATE:19670101T000000
+TZNAME:EST
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19200328T020000
+RDATE:19200328T020000
+RDATE:19740106T020000
+RDATE:19750223T020000
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19201031T020000
+RDATE:19201031T020000
+RDATE:19450930T020000
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19210424T020000
+RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19210925T020000
+RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19420209T020000
+RDATE:19420209T020000
+TZNAME:EWT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19450814T190000
+RDATE:19450814T190000
+TZNAME:EPT
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19460428T020000
+RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19460929T020000
+RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:STANDARD
+DTSTART:19551030T020000
+RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19670430T020000
+RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19671029T020000
+RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+BEGIN:DAYLIGHT
+DTSTART:19760425T020000
+RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:19870405T020000
+RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:DAYLIGHT
+DTSTART:20070311T020000
+RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20071104T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+""",
+                (
+                    (DateTime(1942, 2, 8), -5),
+                    (DateTime(1942, 2, 10), -4),
+                    (DateTime(2011, 1, 1), -5),
+                    (DateTime(2011, 4, 1), -4),
+                    (DateTime(2011, 10, 24), -4),
+                    (DateTime(2011, 11, 8), -5),
+                    (DateTime(2006, 1, 1), -5),
+                    (DateTime(2006, 4, 1), -5),
+                    (DateTime(2006, 5, 1), -4),
+                    (DateTime(2006, 10, 1), -4),
+                    (DateTime(2006, 10, 24), -4),
+                    (DateTime(2006, 11, 8), -5),
+                )
+            ),
+                    ("""BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//calendarserver.org//Zonal//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Etc/GMT+8
+X-LIC-LOCATION:Etc/GMT+8
+BEGIN:STANDARD
+DTSTART:18000101T000000
+RDATE:18000101T000000
+TZNAME:GMT+8
+TZOFFSETFROM:-0800
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+""",
+                (
+                    (DateTime(1942, 2, 8), -8),
+                    (DateTime(1942, 2, 10), -8),
+                    (DateTime(2011, 1, 1), -8),
+                    (DateTime(2011, 4, 1), -8),
+                )
+            ),
+        )
+
+        for tzdata, offsets in data:
+
+            cal = Calendar.parseText(tzdata.replace("\n", "\r\n"))
+            tz = cal.getComponents()[0]
+
+            for dt, offset in offsets:
+                tzoffset = tz.getTimezoneOffsetSeconds(dt)
+                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching" % (tz.getID(), dt,))
+            for dt, offset in reversed(offsets):
+                tzoffset = tz.getTimezoneOffsetSeconds(dt)
+                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching, reversed" % (tz.getID(), dt,))
+
+            for dt, offset in offsets:
+                tz.mCachedExpandAllMax = None
+                tzoffset = tz.getTimezoneOffsetSeconds(dt)
+                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching" % (tz.getID(), dt,))
+            for dt, offset in reversed(offsets):
+                tz.mCachedExpandAllMax = None
+                tzoffset = tz.getTimezoneOffsetSeconds(dt)
+                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching, reversed" % (tz.getID(), dt,))

Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_validation.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_validation.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,8 +14,8 @@
 #    limitations under the License.
 ##
 
-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarValidationError
+from pycalendar.exceptions import ValidationError
+from pycalendar.icalendar.calendar import Calendar
 import unittest
 
 class TestValidation(unittest.TestCase):
@@ -65,7 +65,7 @@
         )
 
         for title, item, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(item)
+            cal = Calendar.parseText(item)
             fixed, unfixed = cal.validate(doFix=False)
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
             self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
@@ -216,7 +216,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=False, doRaise=False)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -367,7 +367,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True, doRaise=False)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -523,9 +523,9 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             if test_raises:
-                self.assertRaises(PyCalendarValidationError, cal.validate, doFix=False, doRaise=True)
+                self.assertRaises(ValidationError, cal.validate, doFix=False, doRaise=True)
             else:
                 try:
                     fixed, unfixed = cal.validate(doFix=False, doRaise=False)
@@ -685,9 +685,9 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             if test_raises:
-                self.assertRaises(PyCalendarValidationError, cal.validate, doFix=False, doRaise=True)
+                self.assertRaises(ValidationError, cal.validate, doFix=False, doRaise=True)
             else:
                 try:
                     fixed, unfixed = cal.validate(doFix=True, doRaise=False)
@@ -977,7 +977,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1047,7 +1047,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1115,7 +1115,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1260,7 +1260,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1482,7 +1482,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1680,7 +1680,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -1874,7 +1874,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -2564,7 +2564,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -2713,7 +2713,7 @@
         )
 
         for title, test_old, test_new, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=True)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
@@ -3182,7 +3182,7 @@
         )
 
         for title, test_old, test_new, test_doFix, test_fixed, test_unfixed in data:
-            cal = PyCalendar.parseText(test_old)
+            cal = Calendar.parseText(test_old)
             fixed, unfixed = cal.validate(doFix=test_doFix)
             self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
             self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_xml.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_xml.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_xml.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_xml.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,104 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar.calendar import Calendar
+import cStringIO as StringIO
+import difflib
+import unittest
+
+class TestXML(unittest.TestCase):
+
+    data = (
+                (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//mulberrymail.com//Mulberry v4.0//EN
+BEGIN:VEVENT
+UID:12345-67890-3
+DTSTART:20071114T000000Z
+ATTENDEE:mailto:user2 at example.com
+EXDATE:20081114T000000Z
+ORGANIZER:mailto:user1 at example.com
+RRULE:FREQ=YEARLY
+END:VEVENT
+X-TEST:Testing
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""<?xml version="1.0" encoding="utf-8"?>
+<ns0:icalendar xmlns:ns0="urn:ietf:params:xml:ns:icalendar-2.0">
+  <ns0:vcalendar>
+    <ns0:properties>
+      <ns0:version>
+        <ns0:text>2.0</ns0:text>
+      </ns0:version>
+      <ns0:prodid>
+        <ns0:text>-//mulberrymail.com//Mulberry v4.0//EN</ns0:text>
+      </ns0:prodid>
+      <ns0:x-test>
+        <ns0:unknown>Testing</ns0:unknown>
+      </ns0:x-test>
+    </ns0:properties>
+    <ns0:components>
+      <ns0:vevent>
+        <ns0:properties>
+          <ns0:uid>
+            <ns0:text>12345-67890-3</ns0:text>
+          </ns0:uid>
+          <ns0:dtstart>
+            <ns0:date-time>2007-11-14T00:00:00Z</ns0:date-time>
+          </ns0:dtstart>
+          <ns0:attendee>
+            <ns0:cal-address>mailto:user2 at example.com</ns0:cal-address>
+          </ns0:attendee>
+          <ns0:exdate>
+            <ns0:date-time>2008-11-14T00:00:00Z</ns0:date-time>
+          </ns0:exdate>
+          <ns0:organizer>
+            <ns0:cal-address>mailto:user1 at example.com</ns0:cal-address>
+          </ns0:organizer>
+          <ns0:rrule>
+            <ns0:recur>
+              <ns0:freq>YEARLY</ns0:freq>
+            </ns0:recur>
+          </ns0:rrule>
+        </ns0:properties>
+      </ns0:vevent>
+    </ns0:components>
+  </ns0:vcalendar>
+</ns0:icalendar>
+""",
+                ),
+)
+
+    def testGenerateXML(self):
+
+        def _doRoundtrip(caldata, resultdata=None):
+            test1 = resultdata if resultdata is not None else caldata
+
+            cal = Calendar()
+            cal.parse(StringIO.StringIO(caldata))
+
+            test2 = cal.getTextXML()
+
+            self.assertEqual(
+                test1,
+                test2,
+                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+            )
+
+        for item1, item2 in self.data:
+            _doRoundtrip(item1, item2)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/valarm.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/valarm.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/valarm.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/valarm.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,707 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.parameter import Parameter
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+from pycalendar.value import Value
+
+class VAlarm(Component):
+
+    sActionMap = {
+        definitions.cICalProperty_ACTION_AUDIO: definitions.eAction_VAlarm_Audio,
+        definitions.cICalProperty_ACTION_DISPLAY: definitions.eAction_VAlarm_Display,
+        definitions.cICalProperty_ACTION_EMAIL: definitions.eAction_VAlarm_Email,
+        definitions.cICalProperty_ACTION_PROCEDURE: definitions.eAction_VAlarm_Procedure,
+        definitions.cICalProperty_ACTION_URI: definitions.eAction_VAlarm_URI,
+        definitions.cICalProperty_ACTION_NONE: definitions.eAction_VAlarm_None,
+    }
+
+    sActionValueMap = {
+        definitions.eAction_VAlarm_Audio: definitions.cICalProperty_ACTION_AUDIO,
+        definitions.eAction_VAlarm_Display: definitions.cICalProperty_ACTION_DISPLAY,
+        definitions.eAction_VAlarm_Email: definitions.cICalProperty_ACTION_EMAIL,
+        definitions.eAction_VAlarm_Procedure: definitions.cICalProperty_ACTION_PROCEDURE,
+        definitions.eAction_VAlarm_URI: definitions.cICalProperty_ACTION_URI,
+        definitions.eAction_VAlarm_None: definitions.cICalProperty_ACTION_NONE,
+    }
+
+    # Classes for each action encapsulating action-specific data
+    class VAlarmAction(object):
+
+        propertyCardinality_1 = ()
+        propertyCardinality_1_Fix_Empty = ()
+        propertyCardinality_0_1 = ()
+        propertyCardinality_1_More = ()
+
+        def __init__(self, type):
+            self.mType = type
+
+        def duplicate(self):
+            return VAlarm.VAlarmAction(self.mType)
+
+        def load(self, valarm):
+            pass
+
+        def add(self, valarm):
+            pass
+
+        def remove(self, valarm):
+            pass
+
+        def getType(self):
+            return self.mType
+
+
+    class VAlarmAudio(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+            definitions.cICalProperty_TRIGGER,
+        )
+
+        propertyCardinality_0_1 = (
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_REPEAT,
+            definitions.cICalProperty_ATTACH,
+            definitions.cICalProperty_ACKNOWLEDGED,
+        )
+
+        def __init__(self, speak=None):
+            super(VAlarm.VAlarmAudio, self).__init__(type=definitions.eAction_VAlarm_Audio)
+            self.mSpeakText = speak
+
+        def duplicate(self):
+            return VAlarm.VAlarmAudio(self.mSpeakText)
+
+        def load(self, valarm):
+            # Get properties
+            self.mSpeakText = valarm.loadValueString(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
+
+        def add(self, valarm):
+            # Delete existing then add
+            self.remove(valarm)
+
+            prop = Property(definitions.cICalProperty_ACTION_X_SPEAKTEXT, self.mSpeakText)
+            valarm.addProperty(prop)
+
+        def remove(self, valarm):
+            valarm.removeProperties(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
+
+        def isSpeakText(self):
+            return len(self.mSpeakText) != 0
+
+        def getSpeakText(self):
+            return self.mSpeakText
+
+
+    class VAlarmDisplay(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+            definitions.cICalProperty_TRIGGER,
+        )
+
+        propertyCardinality_1_Fix_Empty = (
+            definitions.cICalProperty_DESCRIPTION,
+        )
+
+        propertyCardinality_0_1 = (
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_REPEAT,
+            definitions.cICalProperty_ACKNOWLEDGED,
+        )
+
+        def __init__(self, description=None):
+            super(VAlarm.VAlarmDisplay, self).__init__(type=definitions.eAction_VAlarm_Display)
+            self.mDescription = description
+
+        def duplicate(self):
+            return VAlarm.VAlarmDisplay(self.mDescription)
+
+        def load(self, valarm):
+            # Get properties
+            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
+
+        def add(self, valarm):
+            # Delete existing then add
+            self.remove(valarm)
+
+            prop = Property(definitions.cICalProperty_DESCRIPTION, self.mDescription)
+            valarm.addProperty(prop)
+
+        def remove(self, valarm):
+            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
+
+        def getDescription(self):
+            return self.mDescription
+
+
+    class VAlarmEmail(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+            definitions.cICalProperty_TRIGGER,
+        )
+
+        propertyCardinality_1_Fix_Empty = (
+            definitions.cICalProperty_DESCRIPTION,
+            definitions.cICalProperty_SUMMARY,
+        )
+
+        propertyCardinality_0_1 = (
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_REPEAT,
+            definitions.cICalProperty_ACKNOWLEDGED,
+        )
+
+        propertyCardinality_1_More = (
+            definitions.cICalProperty_ATTENDEE,
+        )
+
+        def __init__(self, description=None, summary=None, attendees=None):
+            super(VAlarm.VAlarmEmail, self).__init__(type=definitions.eAction_VAlarm_Email)
+            self.mDescription = description
+            self.mSummary = summary
+            self.mAttendees = attendees
+
+        def duplicate(self):
+            return VAlarm.VAlarmEmail(self.mDescription, self.mSummary, self.mAttendees)
+
+        def load(self, valarm):
+            # Get properties
+            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
+            self.mSummary = valarm.loadValueString(definitions.cICalProperty_SUMMARY)
+
+            self.mAttendees = []
+            if valarm.hasProperty(definitions.cICalProperty_ATTENDEE):
+                # Get each attendee
+                range = valarm.getProperties().get(definitions.cICalProperty_ATTENDEE, ())
+                for iter in range:
+                    # Get the attendee value
+                    attendee = iter.getCalAddressValue()
+                    if attendee is not None:
+                        self.mAttendees.append(attendee.getValue())
+
+        def add(self, valarm):
+            # Delete existing then add
+            self.remove(valarm)
+
+            prop = Property(definitions.cICalProperty_DESCRIPTION, self.mDescription)
+            valarm.addProperty(prop)
+
+            prop = Property(definitions.cICalProperty_SUMMARY, self.mSummary)
+            valarm.addProperty(prop)
+
+            for iter in self.mAttendees:
+                prop = Property(definitions.cICalProperty_ATTENDEE, iter, Value.VALUETYPE_CALADDRESS)
+                valarm.addProperty(prop)
+
+        def remove(self, valarm):
+            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
+            valarm.removeProperties(definitions.cICalProperty_SUMMARY)
+            valarm.removeProperties(definitions.cICalProperty_ATTENDEE)
+
+        def getDescription(self):
+            return self.mDescription
+
+        def getSummary(self):
+            return self.mSummary
+
+        def getAttendees(self):
+            return self.mAttendees
+
+
+    class VAlarmUnknown(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+            definitions.cICalProperty_TRIGGER,
+        )
+
+        propertyCardinality_0_1 = (
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_REPEAT,
+            definitions.cICalProperty_ACKNOWLEDGED,
+        )
+
+        def __init__(self):
+            super(VAlarm.VAlarmUnknown, self).__init__(type=definitions.eAction_VAlarm_Unknown)
+
+        def duplicate(self):
+            return VAlarm.VAlarmUnknown()
+
+
+    class VAlarmURI(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+            definitions.cICalProperty_TRIGGER,
+            definitions.cICalProperty_URL,
+        )
+
+        propertyCardinality_0_1 = (
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_REPEAT,
+            definitions.cICalProperty_ACKNOWLEDGED,
+        )
+
+        def __init__(self, uri=None):
+            super(VAlarm.VAlarmURI, self).__init__(type=definitions.eAction_VAlarm_URI)
+            self.mURI = uri
+
+        def duplicate(self):
+            return VAlarm.VAlarmURI(self.mURI)
+
+        def load(self, valarm):
+            # Get properties
+            self.mURI = valarm.loadValueString(definitions.cICalProperty_URL)
+
+        def add(self, valarm):
+            # Delete existing then add
+            self.remove(valarm)
+
+            prop = Property(definitions.cICalProperty_URL, self.mURI)
+            valarm.addProperty(prop)
+
+        def remove(self, valarm):
+            valarm.removeProperties(definitions.cICalProperty_URL)
+
+        def getURI(self):
+            return self.mURI
+
+
+    class VAlarmNone(VAlarmAction):
+
+        propertyCardinality_1 = (
+            definitions.cICalProperty_ACTION,
+        )
+
+        def __init__(self):
+            super(VAlarm.VAlarmNone, self).__init__(type=definitions.eAction_VAlarm_None)
+
+        def duplicate(self):
+            return VAlarm.VAlarmNone()
+
+
+    def getMimeComponentName(self):
+        # Cannot be sent as a separate MIME object
+        return None
+
+    sActionToAlarmMap = {
+        definitions.eAction_VAlarm_Audio: VAlarmAudio,
+        definitions.eAction_VAlarm_Display: VAlarmDisplay,
+        definitions.eAction_VAlarm_Email: VAlarmEmail,
+        definitions.eAction_VAlarm_URI: VAlarmURI,
+        definitions.eAction_VAlarm_None: VAlarmNone,
+    }
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+
+        super(VAlarm, self).__init__(parent=parent)
+
+        self.mAction = definitions.eAction_VAlarm_Display
+        self.mTriggerAbsolute = False
+        self.mTriggerOnStart = True
+        self.mTriggerOn = DateTime()
+
+        # Set duration default to 1 hour
+        self.mTriggerBy = Duration()
+        self.mTriggerBy.setDuration(60 * 60)
+
+        # Does not repeat by default
+        self.mRepeats = 0
+        self.mRepeatInterval = Duration()
+        self.mRepeatInterval.setDuration(5 * 60) # Five minutes
+
+        # Status
+        self.mStatusInit = False
+        self.mAlarmStatus = definitions.eAlarm_Status_Pending
+        self.mLastTrigger = DateTime()
+        self.mNextTrigger = DateTime()
+        self.mDoneCount = 0
+
+        # Create action data
+        self.mActionData = VAlarm.VAlarmDisplay("")
+
+
+    def duplicate(self, parent=None):
+        other = super(VAlarm, self).duplicate(parent=parent)
+        other.mAction = self.mAction
+        other.mTriggerAbsolute = self.mTriggerAbsolute
+        other.mTriggerOn = self.mTriggerOn.duplicate()
+        other.mTriggerBy = self.mTriggerBy.duplicate()
+        other.mTriggerOnStart = self.mTriggerOnStart
+
+        other.mRepeats = self.mRepeats
+        other.mRepeatInterval = self.mRepeatInterval.duplicate()
+
+        other.mAlarmStatus = self.mAlarmStatus
+        if self.mLastTrigger is not None:
+            other.mLastTrigger = self.mLastTrigger.duplicate()
+        if self.mNextTrigger is not None:
+            other.mNextTrigger = self.mNextTrigger.duplicate()
+        other.mDoneCount = self.mDoneCount
+
+        other.mActionData = self.mActionData.duplicate()
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VALARM
+
+
+    def getAction(self):
+        return self.mAction
+
+
+    def getActionData(self):
+        return self.mActionData
+
+
+    def isTriggerAbsolute(self):
+        return self.mTriggerAbsolute
+
+
+    def getTriggerOn(self):
+        return self.mTriggerOn
+
+
+    def getTriggerDuration(self):
+        return self.mTriggerBy
+
+
+    def isTriggerOnStart(self):
+        return self.mTriggerOnStart
+
+
+    def getRepeats(self):
+        return self.mRepeats
+
+
+    def getInterval(self):
+        return self.mRepeatInterval
+
+
+    def added(self):
+        # Added to calendar so add to calendar notifier
+        # calstore::CCalendarNotifier::sCalendarNotifier.AddAlarm(this)
+
+        # Do inherited
+        super(VAlarm, self).added()
+
+
+    def removed(self):
+        # Removed from calendar so add to calendar notifier
+        # calstore::CCalendarNotifier::sCalendarNotifier.RemoveAlarm(this)
+
+        # Do inherited
+        super(VAlarm, self).removed()
+
+
+    def changed(self):
+        # Always force recalc of trigger status
+        self.mStatusInit = False
+
+        # Changed in calendar so change in calendar notifier
+        # calstore::CCalendarNotifier::sCalendarNotifier.ChangedAlarm(this)
+
+        # Do not do inherited as this is always a sub-component and we do not
+        # do top-level component changes
+        # super.changed()
+
+
+    def finalise(self):
+        # Do inherited
+        super(VAlarm, self).finalise()
+
+        # Get the ACTION
+        temp = self.loadValueString(definitions.cICalProperty_ACTION)
+        if temp is not None:
+            self.mAction = VAlarm.sActionMap.get(temp, definitions.eAction_VAlarm_Unknown)
+            self.loadAction()
+
+        # Get the trigger
+        if self.hasProperty(definitions.cICalProperty_TRIGGER):
+            # Determine the type of the value
+            temp1 = self.loadValueDateTime(definitions.cICalProperty_TRIGGER)
+            temp2 = self.loadValueDuration(definitions.cICalProperty_TRIGGER)
+            if temp1 is not None:
+                self.mTriggerAbsolute = True
+                self.mTriggerOn = temp1
+            elif temp2 is not None:
+                self.mTriggerAbsolute = False
+                self.mTriggerBy = temp2
+
+                # Get the property
+                prop = self.findFirstProperty(definitions.cICalProperty_TRIGGER)
+
+                # Look for RELATED parameter
+                if prop.hasParameter(definitions.cICalParameter_RELATED):
+                    temp = prop.getParameterValue(definitions.cICalParameter_RELATED)
+                    if temp == definitions.cICalParameter_RELATED_START:
+                        self.mTriggerOnStart = True
+                    elif temp == definitions.cICalParameter_RELATED_END:
+                        self.mTriggerOnStart = False
+                else:
+                    self.mTriggerOnStart = True
+
+        # Get repeat & interval
+        temp = self.loadValueInteger(definitions.cICalProperty_REPEAT)
+        if temp is not None:
+            self.mRepeats = temp
+        temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
+        if temp is not None:
+            self.mRepeatInterval = temp
+
+        # Set a map key for sorting
+        self.mMapKey = "%s:%s" % (self.mAction, self.mTriggerOn if self.mTriggerAbsolute else self.mTriggerBy,)
+
+        # Alarm status - private to Mulberry
+        status = self.loadValueString(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
+        if status is not None:
+            if status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING:
+                self.mAlarmStatus = definitions.eAlarm_Status_Pending
+            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED:
+                self.mAlarmStatus = definitions.eAlarm_Status_Completed
+            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED:
+                self.mAlarmStatus = definitions.eAlarm_Status_Disabled
+            else:
+                self.mAlarmStatus = definitions.eAlarm_Status_Pending
+
+        # Last trigger time - private to Mulberry
+        temp = self.loadValueDateTime(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
+        if temp is not None:
+            self.mLastTrigger = temp
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        # Validate using action specific constraints
+        self.propertyCardinality_1 = self.mActionData.propertyCardinality_1
+        self.propertyCardinality_1_Fix_Empty = self.mActionData.propertyCardinality_1_Fix_Empty
+        self.propertyCardinality_0_1 = self.mActionData.propertyCardinality_0_1
+        self.propertyCardinality_1_More = self.mActionData.propertyCardinality_1_More
+
+        fixed, unfixed = super(VAlarm, self).validate(doFix)
+
+        # Extra constraint: both DURATION and REPEAT must be present togethe
+        if self.hasProperty(definitions.cICalProperty_DURATION) ^ self.hasProperty(definitions.cICalProperty_REPEAT):
+            # Cannot fix this
+            logProblem = "[%s] Properties must be present together: %s, %s" % (
+                self.getType(),
+                definitions.cICalProperty_DURATION,
+                definitions.cICalProperty_REPEAT,
+            )
+            unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    def editStatus(self, status):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
+
+        # Updated cached values
+        self.mAlarmStatus = status
+
+        # Add new
+        status_txt = ""
+        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
+            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
+        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
+            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
+        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
+            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
+        self.addProperty(Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status_txt))
+
+
+    def editAction(self, action, data):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_ACTION)
+        self.mActionData.remove(self)
+        self.mActionData = None
+
+        # Updated cached values
+        self.mAction = action
+        self.mActionData = data
+
+        # Add new properties to alarm
+        action_txt = VAlarm.sActionValueMap.get(self.mAction, definitions.cICalProperty_ACTION_PROCEDURE)
+
+        prop = Property(definitions.cICalProperty_ACTION, action_txt)
+        self.addProperty(prop)
+
+        self.mActionData.add(self)
+
+
+    def editTriggerOn(self, dt):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_TRIGGER)
+
+        # Updated cached values
+        self.mTriggerAbsolute = True
+        self.mTriggerOn = dt
+
+        # Add new
+        prop = Property(definitions.cICalProperty_TRIGGER, dt)
+        self.addProperty(prop)
+
+
+    def editTriggerBy(self, duration, trigger_start):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_TRIGGER)
+
+        # Updated cached values
+        self.mTriggerAbsolute = False
+        self.mTriggerBy = duration
+        self.mTriggerOnStart = trigger_start
+
+        # Add new (with parameter)
+        prop = Property(definitions.cICalProperty_TRIGGER, duration)
+        attr = Parameter(definitions.cICalParameter_RELATED,
+                 (definitions.cICalParameter_RELATED_START,
+                  definitions.cICalParameter_RELATED_END)[not trigger_start])
+        prop.addParameter(attr)
+        self.addProperty(prop)
+
+
+    def editRepeats(self, repeat, interval):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_REPEAT)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+
+        # Updated cached values
+        self.mRepeats = repeat
+        self.mRepeatInterval = interval
+
+        # Add new
+        if self.mRepeats > 0:
+            self.addProperty(Property(definitions.cICalProperty_REPEAT, repeat))
+            self.addProperty(Property(definitions.cICalProperty_DURATION, interval))
+
+
+    def getAlarmStatus(self):
+        return self.mAlarmStatus
+
+
+    def getNextTrigger(self, dt):
+        if not self.mStatusInit:
+            self.initNextTrigger()
+        dt.copy(self.mNextTrigger)
+
+
+    def alarmTriggered(self, dt):
+        # Remove existing
+        self.removeProperties(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
+        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
+
+        # Updated cached values
+        self.mLastTrigger.copy(dt)
+
+        if self.mDoneCount < self.mRepeats:
+            self.mNextTrigger = self.mLastTrigger + self.mRepeatInterval
+            dt.copy(self.mNextTrigger)
+            self.mDoneCount += 1
+            self.mAlarmStatus = definitions.eAlarm_Status_Pending
+        else:
+            self.mAlarmStatus = definitions.eAlarm_Status_Completed
+
+        # Add new
+        self.addProperty(Property(definitions.cICalProperty_ALARM_X_LASTTRIGGER, dt))
+        status = ""
+        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
+            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
+        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
+            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
+        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
+            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
+        self.addProperty(Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status))
+
+        # Now update dt to the next alarm time
+        return self.mAlarmStatus == definitions.eAlarm_Status_Pending
+
+
+    def loadAction(self):
+        # Delete current one
+        self.mActionData = None
+        self.mActionData = VAlarm.sActionToAlarmMap.get(self.mAction, VAlarm.VAlarmUnknown)()
+        self.mActionData.load(self)
+
+
+    def initNextTrigger(self):
+        # Do not bother if its completed
+        if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
+            return
+        self.mStatusInit = True
+
+        # Look for trigger immediately preceeding or equal to utc now
+        nowutc = DateTime.getNowUTC()
+
+        # Init done counter
+        self.mDoneCount = 0
+
+        # Determine the first trigger
+        trigger = DateTime()
+        self.getFirstTrigger(trigger)
+
+        while self.mDoneCount < self.mRepeats:
+            # See if next trigger is later than now
+            next_trigger = trigger + self.mRepeatInterval
+            if next_trigger > nowutc:
+                break
+            self.mDoneCount += 1
+            trigger = next_trigger
+
+        # Check for completion
+        if trigger == self.mLastTrigger or (nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
+            if self.mDoneCount == self.mRepeats:
+                self.mAlarmStatus = definitions.eAlarm_Status_Completed
+                return
+            else:
+                trigger = trigger + self.mRepeatInterval
+                self.mDoneCount += 1
+
+        self.mNextTrigger = trigger
+
+
+    def getFirstTrigger(self, dt):
+        # If absolute trigger, use that
+        if self.isTriggerAbsolute():
+            # Get the trigger on
+            dt.copy(self.getTriggerOn())
+        else:
+            # Get the parent embedder class (must be CICalendarComponentRecur type)
+            owner = self.getEmbedder()
+            if owner is not None:
+                # Determine time at which alarm will trigger
+                trigger = (owner.getStart(), owner.getEnd())[not self.isTriggerOnStart()]
+
+                # Offset by duration
+                dt.copy(trigger + self.getTriggerDuration())
+
+Component.registerComponent(definitions.cICalComponent_VALARM, VAlarm)

Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/validation.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/validation.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-from pycalendar import definitions
+from pycalendar.icalendar import definitions
 from pycalendar.validation import partial, PropertyValueChecks
 
 ICALENDAR_VALUE_CHECKS = {

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vavailability.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vavailability.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vavailability.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vavailability.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,109 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar import itipdefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+
+class VAvailability(Component):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_BUSYTYPE,
+        definitions.cICalProperty_CLASS,
+        definitions.cICalProperty_CREATED,
+        definitions.cICalProperty_DESCRIPTION,
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_ORGANIZER,
+        definitions.cICalProperty_SEQUENCE,
+        definitions.cICalProperty_SUMMARY,
+        definitions.cICalProperty_URL,
+        definitions.cICalProperty_RECURRENCE_ID,
+        definitions.cICalProperty_DTEND,
+        definitions.cICalProperty_DURATION,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(VAvailability, self).__init__(parent=parent)
+
+
+    def duplicate(self, parent=None):
+        return super(VAvailability, self).duplicate(parent=parent)
+
+
+    def getType(self):
+        return definitions.cICalComponent_VAVAILABILITY
+
+
+    def getMimeComponentName(self):
+        return itipdefinitions.cICalMIMEComponent_VAVAILABILITY
+
+
+    def finalise(self):
+        super(VAvailability, self).finalise()
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        fixed, unfixed = super(VAvailability, self).validate(doFix)
+
+        # Extra constraint: only one of DTEND or DURATION
+        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
+            # Fix by removing the DTEND
+            logProblem = "[%s] Properties must not both be present: %s, %s" % (
+                self.getType(),
+                definitions.cICalProperty_DTEND,
+                definitions.cICalProperty_DURATION,
+            )
+            if doFix:
+                self.removeProperties(definitions.cICalProperty_DTEND)
+                fixed.append(logProblem)
+            else:
+                unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    def addComponent(self, comp):
+        # We can embed the available components only
+        if comp.getType() == definitions.cICalComponent_AVAILABLE:
+            super(VAvailability, self).addComponent(comp)
+        else:
+            raise ValueError
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_DTSTART,
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_DTEND,
+        )
+
+Component.registerComponent(definitions.cICalComponent_VAVAILABILITY, VAvailability)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vevent.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vevent.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vevent.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vevent.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,172 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar import itipdefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentrecur import ComponentRecur
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+
+class VEvent(ComponentRecur):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CLASS,
+        definitions.cICalProperty_CREATED,
+        definitions.cICalProperty_DESCRIPTION,
+        definitions.cICalProperty_GEO,
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_LOCATION,
+        definitions.cICalProperty_ORGANIZER,
+        definitions.cICalProperty_PRIORITY,
+        definitions.cICalProperty_SEQUENCE,
+        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
+        definitions.cICalProperty_SUMMARY,
+        definitions.cICalProperty_TRANSP,
+        definitions.cICalProperty_URL,
+        definitions.cICalProperty_RECURRENCE_ID,
+        definitions.cICalProperty_RRULE,
+        definitions.cICalProperty_DTEND,
+        definitions.cICalProperty_DURATION,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(VEvent, self).__init__(parent=parent)
+        self.mStatus = definitions.eStatus_VEvent_None
+
+
+    def duplicate(self, parent=None):
+        other = super(VEvent, self).duplicate(parent=parent)
+        other.mStatus = self.mStatus
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VEVENT
+
+
+    def getMimeComponentName(self):
+        return itipdefinitions.cICalMIMEComponent_VEVENT
+
+
+    def addComponent(self, comp):
+        # We can embed the alarm components only
+        if comp.getType() == definitions.cICalComponent_VALARM:
+            super(VEvent, self).addComponent(comp)
+        else:
+            raise ValueError
+
+
+    def getStatus(self):
+        return self.mStatus
+
+
+    def setStatus(self, status):
+        self.mStatus = status
+
+
+    def finalise(self):
+        # Do inherited
+        super(VEvent, self).finalise()
+
+        temp = self.loadValueString(definitions.cICalProperty_STATUS)
+        if temp is not None:
+            if temp == definitions.cICalProperty_STATUS_TENTATIVE:
+                self.mStatus = definitions.eStatus_VEvent_Tentative
+            elif temp == definitions.cICalProperty_STATUS_CONFIRMED:
+                self.mStatus = definitions.eStatus_VEvent_Confirmed
+            elif temp == definitions.cICalProperty_STATUS_CANCELLED:
+                self.mStatus = definitions.eStatus_VEvent_Cancelled
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        fixed, unfixed = super(VEvent, self).validate(doFix)
+
+        # Extra constraint: if METHOD not present, DTSTART must be
+        if self.mParentComponent and not self.mParentComponent.hasProperty(definitions.cICalProperty_METHOD):
+            if not self.hasProperty(definitions.cICalProperty_DTSTART):
+                # Cannot fix a missing required property
+                logProblem = "[%s] Missing required property: %s" % (self.getType(), definitions.cICalProperty_DTSTART,)
+                unfixed.append(logProblem)
+
+        # Extra constraint: only one of DTEND or DURATION
+        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
+            # Fix by removing the DTEND
+            logProblem = "[%s] Properties must not both be present: %s, %s" % (
+                self.getType(),
+                definitions.cICalProperty_DTEND,
+                definitions.cICalProperty_DURATION,
+            )
+            if doFix:
+                self.removeProperties(definitions.cICalProperty_DTEND)
+                fixed.append(logProblem)
+            else:
+                unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    # Editing
+    def editStatus(self, status):
+        # Only if it is different
+        if self.mStatus != status:
+            # Updated cached values
+            self.mStatus = status
+
+            # Remove existing STATUS items
+            self.removeProperties(definitions.cICalProperty_STATUS)
+
+            # Now create properties
+            value = None
+            if status == definitions.eStatus_VEvent_None:
+                pass
+            elif status == definitions.eStatus_VEvent_Tentative:
+                value = definitions.cICalProperty_STATUS_TENTATIVE
+            elif status == definitions.eStatus_VEvent_Confirmed:
+                value = definitions.cICalProperty_STATUS_CONFIRMED
+            elif status == definitions.eStatus_VEvent_Cancelled:
+                value = definitions.cICalProperty_STATUS_CANCELLED
+            else:
+                pass
+
+            if value is not None:
+                prop = Property(definitions.cICalProperty_STATUS, value)
+                self.addProperty(prop)
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_RECURRENCE_ID,
+            definitions.cICalProperty_DTSTART,
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_DTEND,
+        )
+
+Component.registerComponent(definitions.cICalComponent_VEVENT, VEvent)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vfreebusy.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vfreebusy.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vfreebusy.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vfreebusy.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,347 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar import itipdefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.freebusy import FreeBusy
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+from pycalendar.period import Period
+from pycalendar.periodvalue import PeriodValue
+from pycalendar.value import Value
+
+class VFreeBusy(Component):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CONTACT,
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_DTEND,
+        definitions.cICalProperty_ORGANIZER,
+        definitions.cICalProperty_URL,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(VFreeBusy, self).__init__(parent=parent)
+        self.mStart = DateTime()
+        self.mHasStart = False
+        self.mEnd = DateTime()
+        self.mHasEnd = False
+        self.mDuration = False
+        self.mCachedBusyTime = False
+        self.mSpanPeriod = None
+        self.mBusyTime = None
+
+
+    def duplicate(self, parent=None):
+        other = super(VFreeBusy, self).duplicate(parent=parent)
+        other.mStart = self.mStart.duplicate()
+        other.mHasStart = self.mHasStart
+        other.mEnd = self.mEnd.duplicate()
+        other.mHasEnd = self.mHasEnd
+        other.mDuration = self.mDuration
+        other.mCachedBusyTime = False
+        other.mBusyTime = None
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VFREEBUSY
+
+
+    def getMimeComponentName(self):
+        return itipdefinitions.cICalMIMEComponent_VFREEBUSY
+
+
+    def finalise(self):
+        # Do inherited
+        super(VFreeBusy, self).finalise()
+
+        # Get DTSTART
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
+        self.mHasStart = temp is not None
+        if self.mHasStart:
+            self.mStart = temp
+
+        # Get DTEND
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
+        if temp is None:
+            # Try DURATION instead
+            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
+            if temp is not None:
+                self.mEnd = self.mStart + temp
+                self.mDuration = True
+            else:
+                # Force end to start, which will then be fixed to sensible
+                # value later
+                self.mEnd = self.mStart
+        else:
+            self.mHasEnd = True
+            self.mDuration = False
+            self.mEnd = temp
+
+
+    def fixStartEnd(self):
+        # End is always greater than start if start exists
+        if self.mHasStart and self.mEnd <= self.mStart:
+            # Use the start
+            self.mEnd = self.mStart.duplicate()
+            self.mDuration = False
+
+            # Adjust to appropiate non-inclusive end point
+            if self.mStart.isDateOnly():
+                self.mEnd.offsetDay(1)
+
+                # For all day events it makes sense to use duration
+                self.mDuration = True
+            else:
+                # Use end of current day
+                self.mEnd.offsetDay(1)
+                self.mEnd.setHHMMSS(0, 0, 0)
+
+
+    def getStart(self):
+        return self.mStart
+
+
+    def hasStart(self):
+        return self.mHasStart
+
+
+    def getEnd(self):
+        return self.mEnd
+
+
+    def hasEnd(self):
+        return self.mHasEnd
+
+
+    def useDuration(self):
+        return self.mDuration
+
+
+    def getSpanPeriod(self):
+        return self.mSpanPeriod
+
+
+    def getBusyTime(self):
+        return self.mBusyTime
+
+
+    def editTiming(self):
+        # Updated cached values
+        self.mHasStart = False
+        self.mHasEnd = False
+        self.mDuration = False
+        self.mStart.setToday()
+        self.mEnd.setToday()
+
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+
+
+    def editTimingStartEnd(self, start, end):
+        # Updated cached values
+        self.mHasStart = self.mHasEnd = True
+        self.mStart = start
+        self.mEnd = end
+        self.mDuration = False
+        self.fixStartEnd()
+
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+
+        # Now create properties
+        prop = Property(definitions.cICalProperty_DTSTART, start)
+        self.addProperty(prop)
+
+        # If its an all day event and the end one day after the start, ignore it
+        temp = start.duplicate()
+        temp.offsetDay(1)
+        if not start.isDateOnly() or end != temp:
+            prop = Property(definitions.cICalProperty_DTEND, end)
+            self.addProperty(prop)
+
+
+    def editTimingStartDuration(self, start, duration):
+        # Updated cached values
+        self.mHasStart = True
+        self.mHasEnd = False
+        self.mStart = start
+        self.mEnd = start + duration
+        self.mDuration = True
+
+        # Remove existing DTSTART & DTEND & DURATION & DUE items
+        self.removeProperties(definitions.cICalProperty_DTSTART)
+        self.removeProperties(definitions.cICalProperty_DTEND)
+        self.removeProperties(definitions.cICalProperty_DURATION)
+        self.removeProperties(definitions.cICalProperty_DUE)
+
+        # Now create properties
+        prop = Property(definitions.cICalProperty_DTSTART, start)
+        self.addProperty(prop)
+
+        # If its an all day event and the duration is one day, ignore it
+        if (not start.isDateOnly() or (duration.getWeeks() != 0)
+                or (duration.getDays() > 1)):
+            prop = Property(definitions.cICalProperty_DURATION, duration)
+            self.addProperty(prop)
+
+
+    # Generating info
+    def expandPeriodComp(self, period, list):
+        # Cache the busy-time details if not done already
+        if not self.mCachedBusyTime:
+            self.cacheBusyTime()
+
+        # See if period intersects the busy time span range
+        if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
+            list.append(self)
+
+
+    def expandPeriodFB(self, period, list):
+        # Cache the busy-time details if not done already
+        if not self.mCachedBusyTime:
+            self.cacheBusyTime()
+
+        # See if period intersects the busy time span range
+        if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
+            for fb in self.mBusyTime:
+                list.append(FreeBusy(fb))
+
+
+    def cacheBusyTime(self):
+
+        # Clear out any existing cache
+        self.mBusyTime = []
+
+        # Get all FREEBUSY items and add those that are BUSY
+        min_start = DateTime()
+        max_end = DateTime()
+        props = self.getProperties()
+        result = props.get(definitions.cICalProperty_FREEBUSY, ())
+        for iter in result:
+
+            # Check the properties FBTYPE parameter
+            type = 0
+            is_busy = False
+            if iter.hasParameter(definitions.cICalParameter_FBTYPE):
+
+                fbyype = iter.getParameterValue(definitions.cICalParameter_FBTYPE)
+                if fbyype.upper() == definitions.cICalParameter_FBTYPE_BUSY:
+
+                    is_busy = True
+                    type = FreeBusy.BUSY
+
+                elif fbyype.upper() == definitions.cICalParameter_FBTYPE_BUSYUNAVAILABLE:
+
+                    is_busy = True
+                    type = FreeBusy.BUSYUNAVAILABLE
+
+                elif fbyype.upper() == definitions.cICalParameter_FBTYPE_BUSYTENTATIVE:
+
+                    is_busy = True
+                    type = FreeBusy.BUSYTENTATIVE
+
+                else:
+
+                    is_busy = False
+                    type = FreeBusy.FREE
+
+            else:
+
+                # Default is busy when no parameter
+                is_busy = True
+                type = FreeBusy.BUSY
+
+            # Add this period
+            if is_busy:
+
+                multi = iter.getMultiValue()
+                if (multi is not None) and (multi.getType() == Value.VALUETYPE_PERIOD):
+
+                    for o in multi.getValues():
+
+                        # Double-check type
+                        period = None
+                        if isinstance(o, PeriodValue):
+                            period = o
+
+                        # Double-check type
+                        if period is not None:
+
+                            self.mBusyTime.append(FreeBusy(type, period.getValue()))
+
+                            if len(self.mBusyTime) == 1:
+
+                                min_start = period.getValue().getStart()
+                                max_end = period.getValue().getEnd()
+
+                            else:
+
+                                if min_start > period.getValue().getStart():
+                                    min_start = period.getValue().getStart()
+                                if max_end < period.getValue().getEnd():
+                                    max_end = period.getValue().getEnd()
+
+        # If nothing present, empty the list
+        if len(self.mBusyTime) == 0:
+
+            self.mBusyTime = None
+
+        else:
+
+            # Sort the list by period
+            self.mBusyTime.sort(cmp=lambda x, y: x.getPeriod().getStart().compareDateTime(y.getPeriod().getStart()))
+
+            # Determine range
+            start = DateTime()
+            end = DateTime()
+            if self.mHasStart:
+                start = self.mStart
+            else:
+                start = min_start
+            if self.mHasEnd:
+                end = self.mEnd
+            else:
+                end = max_end
+
+            self.mSpanPeriod = Period(start, end)
+
+        self.mCachedBusyTime = True
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_DTSTART,
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_DTEND,
+        )
+
+Component.registerComponent(definitions.cICalComponent_VFREEBUSY, VFreeBusy)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vjournal.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vjournal.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vjournal.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vjournal.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,73 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar import itipdefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentrecur import ComponentRecur
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+
+class VJournal(ComponentRecur):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CLASS,
+        definitions.cICalProperty_CREATED,
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_ORGANIZER,
+        definitions.cICalProperty_RECURRENCE_ID,
+        definitions.cICalProperty_SEQUENCE,
+        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
+        definitions.cICalProperty_SUMMARY,
+        definitions.cICalProperty_URL,
+        definitions.cICalProperty_RRULE,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(VJournal, self).__init__(parent=parent)
+
+
+    def duplicate(self, parent=None):
+        return super(VJournal, self).duplicate(parent=parent)
+
+
+    def getType(self):
+        return definitions.cICalComponent_VJOURNAL
+
+
+    def getMimeComponentName(self):
+        return itipdefinitions.cICalMIMEComponent_VJOURNAL
+
+
+    def finalise(self):
+        super(VJournal, self).finalise()
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_RECURRENCE_ID,
+            definitions.cICalProperty_DTSTART,
+        )
+
+Component.registerComponent(definitions.cICalComponent_VJOURNAL, VJournal)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezone.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vtimezone.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezone.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,287 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+
+class VTimezone(Component):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_TZID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_TZURL,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    UTCOFFSET_CACHE_MAX_ENTRIES = 100000
+
+    sortSubComponents = False
+
+    def __init__(self, parent=None):
+        super(VTimezone, self).__init__(parent=parent)
+        self.mID = ""
+        self.mUTCOffsetSortKey = None
+        self.mCachedExpandAllMaxYear = None
+        self.mCachedOffsets = None
+
+
+    def duplicate(self, parent=None):
+        other = super(VTimezone, self).duplicate(parent=parent)
+        other.mID = self.mID
+        other.mUTCOffsetSortKey = self.mUTCOffsetSortKey
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VTIMEZONE
+
+
+    def getMimeComponentName(self):
+        # Cannot be sent as a separate MIME object
+        return None
+
+
+    def addComponent(self, comp):
+        # We can embed the timezone components only
+        if ((comp.getType() == definitions.cICalComponent_STANDARD)
+                or (comp.getType() == definitions.cICalComponent_DAYLIGHT)):
+            super(VTimezone, self).addComponent(comp)
+        else:
+            raise ValueError
+
+
+    def getMapKey(self):
+        return self.mID
+
+
+    def finalise(self):
+        # Get TZID
+        temp = self.loadValueString(definitions.cICalProperty_TZID)
+        if temp is not None:
+            self.mID = temp
+
+        # Sort sub-components by DTSTART
+        self.mComponents.sort(key=lambda x: x.getStart())
+
+        # Do inherited
+        super(VTimezone, self).finalise()
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        fixed, unfixed = super(VTimezone, self).validate(doFix)
+
+        # Must have at least one STANDARD or DAYLIGHT sub-component
+        for component in self.mComponents:
+            if component.getType() in (definitions.cICalComponent_STANDARD, definitions.cICalComponent_DAYLIGHT):
+                break
+        else:
+            # Cannot fix a missing required component
+            logProblem = "[%s] At least one component must be present: %s or %s" % (
+                self.getType(),
+                definitions.cICalComponent_STANDARD,
+                definitions.cICalComponent_DAYLIGHT,
+            )
+            unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    def getID(self):
+        return self.mID
+
+
+    def getUTCOffsetSortKey(self):
+        if self.mUTCOffsetSortKey is None:
+            # Take time from first element
+            if len(self.mComponents) > 0:
+                # Initial offset provides the primary key
+                utc_offset1 = self.mComponents[0].getUTCOffset()
+
+                # Presence of secondary is the next key
+                utc_offset2 = utc_offset1
+                if len(self.mComponents) > 1:
+                    utc_offset2 = self.mComponents[1].getUTCOffset()
+
+                # Create key
+                self.mUTCOffsetSortKey = (utc_offset1 + utc_offset2) / 2
+            else:
+                self.mUTCOffsetSortKey = 0
+
+        return self.mUTCOffsetSortKey
+
+
+    def getTimezoneOffsetSeconds(self, dt):
+        """
+        Caching implementation of expansion. We cache the entire set of transitions up to one year ahead
+        of the requested time.
+        """
+
+        # Need to make the incoming date-time relative to the DTSTART in the
+        # timezone component for proper comparison.
+        # This means making the incoming date-time a floating (no timezone)
+        # item
+        temp = dt.duplicate()
+        temp.setTimezoneID(None)
+
+        # Check whether we need to recache
+        if self.mCachedExpandAllMaxYear is None or temp.mYear >= self.mCachedExpandAllMaxYear:
+            cacheMax = temp.duplicate()
+            cacheMax.setHHMMSS(0, 0, 0)
+            cacheMax.offsetYear(2)
+            cacheMax.setMonth(1)
+            cacheMax.setDay(1)
+            self.mCachedExpandAll = self.expandAll(None, cacheMax)
+            self.mCachedExpandAllMaxYear = cacheMax.mYear
+            self.mCachedOffsets = {}
+
+        # Now search for the transition just below the time we want
+        if len(self.mCachedExpandAll):
+            cacheKey = (temp.mYear, temp.mMonth, temp.mDay, temp.mHours, temp.mMinutes,)
+            i = self.mCachedOffsets.get(cacheKey)
+            if i is None:
+                i = VTimezone.tuple_bisect_right(self.mCachedExpandAll, temp)
+                if len(self.mCachedOffsets) >= self.UTCOFFSET_CACHE_MAX_ENTRIES:
+                    self.mCachedOffsets = {}
+                self.mCachedOffsets[cacheKey] = i
+            if i != 0:
+                return self.mCachedExpandAll[i - 1][2]
+
+        return 0
+
+
+    def getTimezoneDescriptor(self, dt):
+        result = ""
+
+        # Get the closet matching element to the time
+        found = self.findTimezoneElement(dt)
+
+        # Get it
+        if found is not None:
+            if len(found.getTZName()) == 0:
+                tzoffset = found.getUTCOffset()
+                negative = False
+                if tzoffset < 0:
+                    tzoffset = -tzoffset
+                    negative = True
+                result = ("+", "-")[negative]
+                hours_offset = tzoffset / (60 * 60)
+                if hours_offset < 10:
+                    result += "0"
+                result += str(hours_offset)
+                mins_offset = (tzoffset / 60) % 60
+                if mins_offset < 10:
+                    result += "0"
+                result += str(mins_offset)
+            else:
+                result = "("
+                result += found.getTZName()
+                result += ")"
+
+        return result
+
+
+    def mergeTimezone(self, tz):
+        pass
+
+
+    @staticmethod
+    def tuple_bisect_right(a, x):
+        """
+        Same as bisect_right except that the values being compared are the first elements
+        of a tuple.
+        """
+
+        lo = 0
+        hi = len(a)
+        while lo < hi:
+            mid = (lo + hi) // 2
+            if x < a[mid][0]:
+                hi = mid
+            else:
+                lo = mid + 1
+        return lo
+
+
+    def findTimezoneElement(self, dt):
+        # Need to make the incoming date-time relative to the DTSTART in the
+        # timezone component for proper comparison.
+        # This means making the incoming date-time a floating (no timezone)
+        # item
+        temp = dt.duplicate()
+        temp.setTimezoneID(None)
+
+        # Had to rework this because some VTIMEZONEs have sub-components where the DST instances are interleaved. That
+        # means we have to evaluate each and every sub-component to find the instance immediately less than the time we are checking.
+
+        # Now do the expansion for each one found and pick the lowest
+        found = None
+        dt_found = DateTime()
+
+        for item in self.mComponents:
+            dt_item = item.expandBelow(temp)
+            if temp >= dt_item:
+                if found is not None:
+                    # Compare with the one previously cached and switch to this
+                    # one if newer
+                    if dt_item > dt_found:
+                        found = item
+                        dt_found = dt_item
+                else:
+                    found = item
+                    dt_found = dt_item
+
+        return found
+
+
+    def expandAll(self, start, end, with_name=False):
+        results = []
+        for item in self.mComponents:
+            results.extend(item.expandAll(start, end, with_name))
+        results = [x for x in set(results)]
+        results.sort(key=lambda x: x[0].getPosixTime())
+        return results
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_TZID,
+            definitions.cICalProperty_LAST_MODIFIED,
+            definitions.cICalProperty_TZURL,
+        )
+
+
+    @staticmethod
+    def sortByUTCOffsetComparator(tz1, tz2):
+        sort1 = tz1.getUTCOffsetSortKey()
+        sort2 = tz2.getUTCOffsetSortKey()
+        if sort1 == sort2:
+            return tz1.getID().compareToIgnoreCase(tz2.getID())
+        else:
+            return (1, -1)[sort1 < sort2]
+
+Component.registerComponent(definitions.cICalComponent_VTIMEZONE, VTimezone)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonedaylight.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vtimezonedaylight.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonedaylight.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonedaylight.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,34 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.vtimezoneelement import VTimezoneElement
+
+class Daylight(VTimezoneElement):
+
+    def __init__(self, parent=None):
+        super(Daylight, self).__init__(parent=parent)
+
+
+    def duplicate(self, parent=None):
+        return super(Daylight, self).duplicate(parent=parent)
+
+
+    def getType(self):
+        return definitions.cICalComponent_DAYLIGHT
+
+Component.registerComponent(definitions.cICalComponent_DAYLIGHT, Daylight)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezoneelement.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vtimezoneelement.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezoneelement.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezoneelement.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,219 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from bisect import bisect_right
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.recurrenceset import RecurrenceSet
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+from pycalendar.period import Period
+from pycalendar.value import Value
+
+class VTimezoneElement(Component):
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_TZOFFSETTO,
+        definitions.cICalProperty_TZOFFSETFROM,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_RRULE,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None, dt=None, offset=None):
+        super(VTimezoneElement, self).__init__(parent=parent)
+        self.mStart = dt if dt is not None else DateTime()
+        self.mTZName = ""
+        self.mUTCOffset = offset if offset is not None else 0
+        self.mUTCOffsetFrom = 0
+        self.mRecurrences = RecurrenceSet()
+        self.mCachedExpandBelow = None
+        self.mCachedExpandBelowItems = None
+
+
+    def duplicate(self, parent=None):
+        other = super(VTimezoneElement, self).duplicate(parent=parent)
+        other.mStart = self.mStart.duplicate()
+        other.mTZName = self.mTZName
+        other.mUTCOffset = self.mUTCOffset
+        other.mUTCOffsetFrom = self.mUTCOffsetFrom
+        other.mRecurrences = self.mRecurrences.duplicate()
+        other.mCachedExpandBelow = None
+        other.mCachedExpandBelowItems = None
+        return other
+
+
+    def finalise(self):
+        # Get DTSTART
+        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
+        if temp is not None:
+            self.mStart = temp
+
+        # Get TZOFFSETTO
+        temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, Value.VALUETYPE_UTC_OFFSET)
+        if temp is not None:
+            self.mUTCOffset = temp
+
+        # Get TZOFFSETFROM
+        temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, Value.VALUETYPE_UTC_OFFSET)
+        if temp is not None:
+            self.mUTCOffsetFrom = temp
+
+        # Get TZNAME
+        temps = self.loadValueString(definitions.cICalProperty_TZNAME)
+        if temps is not None:
+            self.mTZName = temps
+
+        # Get RRULEs
+        self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
+
+        # Get RDATEs
+        self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
+
+        # Do inherited
+        super(VTimezoneElement, self).finalise()
+
+
+    def getSortKey(self):
+        """
+        We do not want these components sorted.
+        """
+        return ""
+
+
+    def getStart(self):
+        return self.mStart
+
+
+    def getUTCOffset(self):
+        return self.mUTCOffset
+
+
+    def getUTCOffsetFrom(self):
+        return self.mUTCOffsetFrom
+
+
+    def getTZName(self):
+        return self.mTZName
+
+
+    def expandBelow(self, below):
+
+        # Look for recurrences
+        if not self.mRecurrences.hasRecurrence() or self.mStart > below:
+            # Return DTSTART even if it is newer
+            return self.mStart
+        else:
+            # We want to allow recurrence calculation caching to help us here
+            # as this method
+            # gets called a lot - most likely for ever increasing dt values
+            # (which will therefore
+            # invalidate the recurrence cache).
+            #
+            # What we will do is round up the date-time to the next year so
+            # that the recurrence
+            # cache is invalidated less frequently
+
+            temp = DateTime(below.getYear(), 1, 1, 0, 0, 0)
+
+            # Use cache of expansion
+            if self.mCachedExpandBelowItems is None:
+                self.mCachedExpandBelowItems = []
+            if self.mCachedExpandBelow is None:
+                self.mCachedExpandBelow = self.mStart.duplicate()
+            if temp > self.mCachedExpandBelow:
+                self.mCachedExpandBelowItems = []
+                period = Period(self.mStart, temp)
+                self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
+                self.mCachedExpandBelow = temp
+
+            if len(self.mCachedExpandBelowItems) != 0:
+                # List comes back sorted so we pick the element just less than
+                # the dt value we want
+                i = bisect_right(self.mCachedExpandBelowItems, below)
+                if i != 0:
+                    return self.mCachedExpandBelowItems[i - 1]
+
+                # The first one in the list is the one we want
+                return self.mCachedExpandBelowItems[0]
+
+            return self.mStart
+
+
+    def expandAll(self, start, end, with_name):
+
+        if start is None:
+            start = self.mStart
+
+        # Ignore if there is no change in offset
+        offsetto = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, Value.VALUETYPE_UTC_OFFSET)
+        offsetfrom = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, Value.VALUETYPE_UTC_OFFSET)
+#        if offsetto == offsetfrom:
+#            return ()
+
+        # Look for recurrences
+        if self.mStart > end:
+            # Return nothing
+            return ()
+        elif not self.mRecurrences.hasRecurrence():
+            # Return DTSTART even if it is newer
+            if self.mStart >= start:
+                result = (self.mStart, offsetfrom, offsetto,)
+                if with_name:
+                    result += (self.getTZName(),)
+                return (result,)
+            else:
+                return ()
+        else:
+            # We want to allow recurrence calculation caching to help us here
+            # as this method
+            # gets called a lot - most likely for ever increasing dt values
+            # (which will therefore
+            # invalidate the recurrence cache).
+            #
+            # What we will do is round up the date-time to the next year so
+            # that the recurrence
+            # cache is invalidated less frequently
+
+            temp = DateTime(end.getYear(), 1, 1, 0, 0, 0)
+
+            # Use cache of expansion
+            if self.mCachedExpandBelowItems is None:
+                self.mCachedExpandBelowItems = []
+            if self.mCachedExpandBelow is None:
+                self.mCachedExpandBelow = self.mStart.duplicate()
+            if temp > self.mCachedExpandBelow:
+                self.mCachedExpandBelowItems = []
+                period = Period(self.mStart, end)
+                self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
+                self.mCachedExpandBelow = temp
+
+            if len(self.mCachedExpandBelowItems) != 0:
+                # Return them all within the range
+                results = []
+                for dt in self.mCachedExpandBelowItems:
+                    if dt >= start and dt < end:
+                        result = (dt, offsetfrom, offsetto,)
+                        if with_name:
+                            result += (self.getTZName(),)
+                        results.append(result)
+                return results
+
+            return ()

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonestandard.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vtimezonestandard.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonestandard.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vtimezonestandard.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,34 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.vtimezoneelement import VTimezoneElement
+
+class Standard(VTimezoneElement):
+
+    def __init__(self, parent=None):
+        super(Standard, self).__init__(parent=parent)
+
+
+    def duplicate(self, parent=None):
+        return super(Standard, self).duplicate(parent=parent)
+
+
+    def getType(self):
+        return definitions.cICalComponent_STANDARD
+
+Component.registerComponent(definitions.cICalComponent_STANDARD, Standard)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vtodo.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vtodo.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vtodo.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vtodo.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,369 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar import itipdefinitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.componentrecur import ComponentRecur
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+import cStringIO as StringIO
+
+class VToDo(ComponentRecur):
+
+    OVERDUE = 0
+    DUE_NOW = 1
+    DUE_LATER = 2
+    DONE = 3
+    CANCELLED = 4
+
+    @staticmethod
+    def sort_for_display(e1, e2):
+        s1 = e1.getMaster()
+        s2 = e2.getMaster()
+
+        # Check status first (convert None -> Needs action for tests)
+        status1 = s1.self.mStatus
+        status2 = s2.self.mStatus
+        if status1 == definitions.eStatus_VToDo_None:
+            status1 = definitions.eStatus_VToDo_NeedsAction
+        if status2 == definitions.eStatus_VToDo_None:
+            status2 = definitions.eStatus_VToDo_NeedsAction
+        if status1 != status2:
+            # More important ones at the top
+            return status1 < status2
+
+        # At this point the status of each is the same
+
+        # If status is cancelled sort by start time
+        if s1.self.mStatus == definitions.eStatus_VToDo_Cancelled:
+            # Older ones at the bottom
+            return s1.mStart > s2.mStart
+
+        # If status is completed sort by completion time
+        if s1.self.mStatus == definitions.eStatus_VToDo_Completed:
+            # Older ones at the bottom
+            return s1.self.mCompleted > s2.self.mCompleted
+
+        # Check due date exists
+        if s1.mHasEnd != s2.mHasEnd:
+            now = DateTime()
+            now.setToday()
+
+            # Ones with due dates after today below ones without due dates
+            if s1.hasEnd():
+                return s1.mEnd <= now
+            elif s2.hasEnd():
+                return now < s2.mEnd
+
+        # Check due dates if present
+        if s1.mHasEnd:
+            if s1.mEnd != s2.mEnd:
+                # Soonest dues dates above later ones
+                return s1.mEnd < s2.mEnd
+
+        # Check priority next
+        if s1.self.mPriority != s2.self.mPriority:
+            # Higher priority above lower ones
+            return s1.self.mPriority < s2.self.mPriority
+
+        # Just use start time - older ones at the top
+        return s1.mStart < s2.mStart
+
+    propertyCardinality_1 = (
+        definitions.cICalProperty_DTSTAMP,
+        definitions.cICalProperty_UID,
+    )
+
+    propertyCardinality_0_1 = (
+        definitions.cICalProperty_CLASS,
+        definitions.cICalProperty_COMPLETED,
+        definitions.cICalProperty_CREATED,
+        definitions.cICalProperty_DESCRIPTION,
+        definitions.cICalProperty_DTSTART,
+        definitions.cICalProperty_GEO,
+        definitions.cICalProperty_LAST_MODIFIED,
+        definitions.cICalProperty_LOCATION,
+        definitions.cICalProperty_ORGANIZER,
+        definitions.cICalProperty_PERCENT_COMPLETE,
+        definitions.cICalProperty_PRIORITY,
+        definitions.cICalProperty_RECURRENCE_ID,
+        definitions.cICalProperty_SEQUENCE,
+        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
+        definitions.cICalProperty_SUMMARY,
+        definitions.cICalProperty_URL,
+        definitions.cICalProperty_RRULE,
+        definitions.cICalProperty_DUE,
+        definitions.cICalProperty_DURATION,
+    )
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None):
+        super(VToDo, self).__init__(parent=parent)
+        self.mPriority = 0
+        self.mStatus = definitions.eStatus_VToDo_None
+        self.mPercentComplete = 0
+        self.mCompleted = DateTime()
+        self.mHasCompleted = False
+
+
+    def duplicate(self, parent=None):
+        other = super(VToDo, self).duplicate(parent=parent)
+        other.mPriority = self.mPriority
+        other.mStatus = self.mStatus
+        other.mPercentComplete = self.mPercentComplete
+        other.mCompleted = self.mCompleted.duplicate()
+        other.mHasCompleted = self.mHasCompleted
+        return other
+
+
+    def getType(self):
+        return definitions.cICalComponent_VTODO
+
+
+    def getMimeComponentName(self):
+        return itipdefinitions.cICalMIMEComponent_VTODO
+
+
+    def addComponent(self, comp):
+        # We can embed the alarm components only
+        if comp.getType() == definitions.cICalComponent_VALARM:
+            super(VToDo, self).addComponent(comp)
+        else:
+            raise ValueError
+
+
+    def getStatus(self):
+        return self.mStatus
+
+
+    def setStatus(self, status):
+        self.mStatus = status
+
+
+    def getStatusText(self):
+        sout = StringIO()
+
+        if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
+            if self.hasEnd():
+                # Check due date
+                today = DateTime()
+                today.setToday()
+                if self.getEnd() > today:
+                    sout.append("Due: ")
+                    whendue = self.getEnd() - today
+                    if (whendue.getDays() > 0) and (whendue.getDays() <= 7):
+                        sout.write(whendue.getDays())
+                        sout.write(" days")
+                    else:
+                        sout.write(self.getEnd().getLocaleDate(DateTime.NUMERICDATE))
+                elif self.getEnd() == today:
+                    sout.write("Due today")
+                else:
+                    sout.write("Overdue: ")
+                    overdue = today - self.getEnd()
+                    if overdue.getWeeks() != 0:
+                        sout.write(overdue.getWeeks())
+                        sout.write(" weeks")
+                    else:
+                        sout.write(overdue.getDays() + 1)
+                        sout.write(" days")
+            else:
+                sout.write("Not Completed")
+        elif self.mStatus == definitions.eStatus_VToDo_Completed:
+            if self.hasCompleted():
+                sout.write("Completed: ")
+                sout.write(self.getCompleted().getLocaleDate(DateTime.NUMERICDATE))
+            else:
+                sout.write("Completed")
+        elif definitions.eStatus_VToDo_Cancelled:
+            sout.write("Cancelled")
+
+        return sout.toString()
+
+
+    def getCompletionState(self):
+        if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
+            if self.hasEnd():
+                # Check due date
+                today = DateTime()
+                today.setToday()
+                if self.getEnd() > today:
+                    return VToDo.DUE_LATER
+                elif self.getEnd() == today:
+                    return VToDo.DUE_NOW
+                else:
+                    return VToDo.OVERDUE
+            else:
+                return VToDo.DUE_NOW
+        elif self.mStatus == definitions.eStatus_VToDo_Completed:
+            return VToDo.DONE
+        elif self.mStatus == definitions.eStatus_VToDo_Cancelled:
+            return VToDo.CANCELLED
+
+
+    def getPriority(self):
+        return self.mPriority
+
+
+    def setPriority(self, priority):
+        self.mPriority = priority
+
+
+    def getCompleted(self):
+        return self.mCompleted
+
+
+    def hasCompleted(self):
+        return self.mHasCompleted
+
+
+    def finalise(self):
+        # Do inherited
+        super(VToDo, self).finalise()
+
+        # Get DUE
+        temp = self.loadValueDateTime(definitions.cICalProperty_DUE)
+        if temp is None:
+            # Try DURATION instead
+            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
+            if temp is not None:
+                self.mEnd = self.mStart + temp
+                self.mHasEnd = True
+            else:
+                self.mHasEnd = False
+        else:
+            self.mHasEnd = True
+            self.mEnd = temp
+
+        # Get PRIORITY
+        self.mPriority = self.loadValueInteger(definitions.cICalProperty_PRIORITY)
+
+        # Get STATUS
+        temp = self.loadValueString(definitions.cICalProperty_STATUS)
+        if temp is not None:
+            if temp == definitions.cICalProperty_STATUS_NEEDS_ACTION:
+                self.mStatus = definitions.eStatus_VToDo_NeedsAction
+            elif temp == definitions.cICalProperty_STATUS_COMPLETED:
+                self.mStatus = definitions.eStatus_VToDo_Completed
+            elif temp == definitions.cICalProperty_STATUS_IN_PROCESS:
+                self.mStatus = definitions.eStatus_VToDo_InProcess
+            elif temp == definitions.cICalProperty_STATUS_CANCELLED:
+                self.mStatus = definitions.eStatus_VToDo_Cancelled
+
+        # Get PERCENT-COMPLETE
+        self.mPercentComplete = self.loadValueInteger(definitions.cICalProperty_PERCENT_COMPLETE)
+
+        # Get COMPLETED
+        temp = self.loadValueDateTime(definitions.cICalProperty_COMPLETED)
+        self.mHasCompleted = temp is not None
+        if self.mHasCompleted:
+            self.mCompleted = temp
+
+
+    def validate(self, doFix=False):
+        """
+        Validate the data in this component and optionally fix any problems, else raise. If
+        loggedProblems is not None it must be a C{list} and problem descriptions are appended
+        to that.
+        """
+
+        fixed, unfixed = super(VToDo, self).validate(doFix)
+
+        # Extra constraint: only one of DUE or DURATION
+        if self.hasProperty(definitions.cICalProperty_DUE) and self.hasProperty(definitions.cICalProperty_DURATION):
+            # Fix by removing the DURATION
+            logProblem = "[%s] Properties must not both be present: %s, %s" % (
+                self.getType(),
+                definitions.cICalProperty_DUE,
+                definitions.cICalProperty_DURATION,
+            )
+            if doFix:
+                self.removeProperties(definitions.cICalProperty_DURATION)
+                fixed.append(logProblem)
+            else:
+                unfixed.append(logProblem)
+
+        # Extra constraint: DTSTART must be present if DURATION is present
+        if self.hasProperty(definitions.cICalProperty_DURATION) and not self.hasProperty(definitions.cICalProperty_DTSTART):
+            # Cannot fix this one
+            logProblem = "[%s] Property must be present: %s with %s" % (
+                self.getType(),
+                definitions.cICalProperty_DTSTART,
+                definitions.cICalProperty_DURATION,
+            )
+            unfixed.append(logProblem)
+
+        return fixed, unfixed
+
+
+    # Editing
+    def editStatus(self, status):
+        # Only if it is different
+        if self.mStatus != status:
+            # Updated cached values
+            self.mStatus = status
+
+            # Remove existing STATUS & COMPLETED items
+            self.removeProperties(definitions.cICalProperty_STATUS)
+            self.removeProperties(definitions.cICalProperty_COMPLETED)
+            self.mHasCompleted = False
+
+            # Now create properties
+            value = None
+            if status == definitions.eStatus_VToDo_NeedsAction:
+                value = definitions.cICalProperty_STATUS_NEEDS_ACTION
+            if status == definitions.eStatus_VToDo_Completed:
+                value = definitions.cICalProperty_STATUS_COMPLETED
+                # Add the completed item
+                self.mCompleted.setNowUTC()
+                self.mHasCompleted = True
+                prop = Property(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
+                self.addProperty(prop)
+            elif status == definitions.eStatus_VToDo_InProcess:
+                value = definitions.cICalProperty_STATUS_IN_PROCESS
+            elif status == definitions.eStatus_VToDo_Cancelled:
+                value = definitions.cICalProperty_STATUS_CANCELLED
+            prop = Property(definitions.cICalProperty_STATUS, value)
+            self.addProperty(prop)
+
+
+    def editCompleted(self, completed):
+        # Remove existing COMPLETED item
+        self.removeProperties(definitions.cICalProperty_COMPLETED)
+        self.mHasCompleted = False
+
+        # Always UTC
+        self.mCompleted = completed.duplicate()
+        self.mCompleted.adjustToUTC()
+        self.mHasCompleted = True
+        prop = Property(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
+        self.addProperty(prop)
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+            definitions.cICalProperty_RECURRENCE_ID,
+            definitions.cICalProperty_DTSTART,
+            definitions.cICalProperty_DURATION,
+            definitions.cICalProperty_DUE,
+            definitions.cICalProperty_COMPLETED,
+        )
+
+Component.registerComponent(definitions.cICalComponent_VTODO, VToDo)

Copied: PyCalendar/branches/json-2/src/pycalendar/icalendar/vunknown.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/vunknown.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/vunknown.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/vunknown.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,68 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.component import Component
+from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
+import uuid
+
+class UnknownComponent(Component):
+
+    propertyValueChecks = ICALENDAR_VALUE_CHECKS
+
+    def __init__(self, parent=None, comptype=""):
+        super(UnknownComponent, self).__init__(parent=parent)
+        self.mType = comptype
+        self.mMapKey = str(uuid.uuid4())
+
+
+    def duplicate(self, parent=None):
+        return super(UnknownComponent, self).duplicate(parent=parent, comptype=self.mType)
+
+
+    def getType(self):
+        return self.mType
+
+
+    def getBeginDelimiter(self):
+        return "BEGIN:" + self.mType
+
+
+    def getEndDelimiter(self):
+        return "END:" + self.mType
+
+
+    def getMimeComponentName(self):
+        return "unknown"
+
+
+    def getMapKey(self):
+        return self.mMapKey
+
+
+    def getSortKey(self):
+        """
+        We do not want unknown components sorted.
+        """
+        return ""
+
+
+    def sortedPropertyKeyOrder(self):
+        return (
+            definitions.cICalProperty_UID,
+        )
+
+Component.registerComponent(definitions.cICalComponent_UNKNOWN, UnknownComponent)

Added: PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,51 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# iCalendar XML definitions
+
+iCalendar20_namespace = "urn:ietf:params:xml:ns:icalendar-2.0"
+
+icalendar = "icalendar"
+
+value_recur = "recur"
+
+recur_freq = "freq"
+recur_freq_secondly = "SECONDLY"
+recur_freq_minutely = "MINUTELY"
+recur_freq_hourly = "HOURLY"
+recur_freq_daily = "DAILY"
+recur_freq_weekly = "WEEKLY"
+recur_freq_monthly = "MONTHLY"
+recur_freq_yearly = "YEARLY"
+
+recur_count = "count"
+recur_until = "until"
+recur_interval = "interval"
+
+recur_bysecond = "bysecond"
+recur_byminute = "byminute"
+recur_byhour = "byhour"
+recur_byday = "byday"
+recur_bymonthday = "bymonthday"
+recur_byyearday = "byyearday"
+recur_byweekno = "byweekno"
+recur_bymonth = "bymonth"
+recur_bysetpos = "bysetpos"
+recur_wkst = "wkst"
+
+req_status_code = "code"
+req_status_description = "description"
+req_status_data = "data"

Modified: PyCalendar/branches/json-2/src/pycalendar/integervalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/integervalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/integervalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,24 +16,24 @@
 
 # iCalendar UTC Offset value
 
-from pycalendar import xmldefs
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.value import Value
 
-class PyCalendarIntegerValue(PyCalendarValue):
+class IntegerValue(Value):
 
     def __init__(self, value=None):
         self.mValue = value if value is not None else 0
 
 
     def duplicate(self):
-        return PyCalendarIntegerValue(self.mValue)
+        return IntegerValue(self.mValue)
 
 
     def getType(self):
-        return PyCalendarValue.VALUETYPE_INTEGER
+        return Value.VALUETYPE_INTEGER
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
         self.mValue = int(data)
 
 
@@ -57,4 +57,4 @@
     def setValue(self, value):
         self.mValue = value
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_INTEGER, PyCalendarIntegerValue, xmldefs.value_integer)
+Value.registerType(Value.VALUETYPE_INTEGER, IntegerValue, xmldefinitions.value_integer)

Deleted: PyCalendar/branches/json-2/src/pycalendar/itipdefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/itipdefinitions.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/itipdefinitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,42 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-#     2446 Section 3
-
-cICalMethod_PUBLISH = "PUBLISH"
-cICalMethod_REQUEST = "REQUEST"
-cICalMethod_REFRESH = "REFRESH"
-cICalMethod_CANCEL = "CANCEL"
-cICalMethod_ADD = "ADD"
-cICalMethod_REPLY = "REPLY"
-cICalMethod_COUNTER = "COUNTER"
-cICalMethod_DECLINECOUNTER = "DECLINECOUNTER"
-
-#     2447 Section 2.4
-cICalMIMEMethod_PUBLISH = "publish"
-cICalMIMEMethod_REQUEST = "request"
-cICalMIMEMethod_REFRESH = "refresh"
-cICalMIMEMethod_CANCEL = "cancel"
-cICalMIMEMethod_ADD = "add"
-cICalMIMEMethod_REPLY = "reply"
-cICalMIMEMethod_COUNTER = "counter"
-cICalMIMEMethod_DECLINECOUNTER = "declinecounter"
-
-cICalMIMEComponent_VEVENT = "vevent"
-cICalMIMEComponent_VTODO = "vtodo"
-cICalMIMEComponent_VJOURNAL = "vjournal"
-cICalMIMEComponent_VFREEBUSY = "vfreebusy"
-cICalMIMEComponent_VAVAILABILITY = "vavailability"

Modified: PyCalendar/branches/json-2/src/pycalendar/manager.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/manager.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/manager.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,14 +14,14 @@
 #    limitations under the License.
 ##
 
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.timezone import Timezone
 
-class PyCalendarManager(object):
+class CalendarManager(object):
 
     sICalendarManager = None
 
     def __init__(self):
-        PyCalendarTimezone.sDefaultTimezone = PyCalendarTimezone()
+        Timezone.sDefaultTimezone = Timezone()
 
 
     def initManager(self):
@@ -29,31 +29,31 @@
 
         # Eventually we need to read these from prefs - for now they are
         # hard-coded to my personal prefs!
-        self.setDefaultTimezone(PyCalendarTimezone(utc=False, tzid="US/Eastern"))
+        self.setDefaultTimezone(Timezone(utc=False, tzid="US/Eastern"))
 
 
     def setDefaultTimezoneID(self, tzid):
         # Check for UTC
         if tzid == "UTC":
-            temp = PyCalendarTimezone(utc=True)
+            temp = Timezone(utc=True)
             self.setDefaultTimezone(temp)
         else:
-            temp = PyCalendarTimezone(utc=False, tzid=tzid)
+            temp = Timezone(utc=False, tzid=tzid)
             self.setDefaultTimezone(temp)
 
 
     def setDefaultTimezone(self, tzid):
-        PyCalendarTimezone.sDefaultTimezone = tzid
+        Timezone.sDefaultTimezone = tzid
 
 
     def getDefaultTimezoneID(self):
-        if PyCalendarTimezone.sDefaultTimezone.getUTC():
+        if Timezone.sDefaultTimezone.getUTC():
             return "UTC"
         else:
-            return PyCalendarTimezone.sDefaultTimezone.getTimezoneID()
+            return Timezone.sDefaultTimezone.getTimezoneID()
 
 
     def getDefaultTimezone(self):
-        return PyCalendarTimezone.sDefaultTimezone
+        return Timezone.sDefaultTimezone
 
-PyCalendarManager.sICalendarManager = PyCalendarManager()
+CalendarManager.sICalendarManager = CalendarManager()

Modified: PyCalendar/branches/json-2/src/pycalendar/multivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/multivalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/multivalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,9 +14,9 @@
 #    limitations under the License.
 ##
 
-from pycalendar.value import PyCalendarValue
+from pycalendar.value import Value
 
-class PyCalendarMultiValue(PyCalendarValue):
+class MultiValue(Value):
 
     def __init__(self, type):
 
@@ -29,7 +29,7 @@
 
 
     def duplicate(self):
-        other = PyCalendarMultiValue(self.mType)
+        other = MultiValue(self.mType)
         other.mValues = [i.duplicate() for i in self.mValues]
         return other
 
@@ -39,7 +39,7 @@
 
 
     def getRealType(self):
-        return PyCalendarValue.VALUETYPE_MULTIVALUE
+        return Value.VALUETYPE_MULTIVALUE
 
 
     def getValue(self):
@@ -57,13 +57,13 @@
     def setValue(self, value):
         newValues = []
         for v in value:
-            pyCalendarValue = PyCalendarValue.createFromType(self.mType)
-            pyCalendarValue.setValue(v)
-            newValues.append(pyCalendarValue)
+            val = Value.createFromType(self.mType)
+            val.setValue(v)
+            newValues.append(val)
         self.mValues = newValues
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
         # Tokenize on comma
         if "," in data:
             tokens = data.split(",")
@@ -71,8 +71,8 @@
             tokens = (data,)
         for token in tokens:
             # Create single value, and parse data
-            value = PyCalendarValue.createFromType(self.mType)
-            value.parse(token)
+            value = Value.createFromType(self.mType)
+            value.parse(token, variant)
             self.mValues.append(value)
 
 
@@ -93,4 +93,4 @@
         for iter in self.mValues:
             iter.writeXML(node, namespace)
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_MULTIVALUE, PyCalendarMultiValue, None)
+Value.registerType(Value.VALUETYPE_MULTIVALUE, MultiValue, None)

Deleted: PyCalendar/branches/json-2/src/pycalendar/n.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/n.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,124 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# vCard ADR value
-
-from pycalendar import utils
-from pycalendar.valueutils import ValueMixin
-
-class N(ValueMixin):
-    """
-    mValue is a tuple of seven str or tuples of str
-    """
-
-    (
-        LAST,
-        FIRST,
-        MIDDLE,
-        PREFIX,
-        SUFFIX,
-        MAXITEMS
-    ) = range(6)
-
-    def __init__(self, last="", first="", middle="", prefix="", suffix=""):
-        self.mValue = (last, first, middle, prefix, suffix)
-
-
-    def duplicate(self):
-        return N(*self.mValue)
-
-
-    def __hash__(self):
-        return hash(self.mValue)
-
-
-    def __repr__(self):
-        return "N %s" % (self.getText(),)
-
-
-    def __eq__(self, comp):
-        return self.mValue == comp.mValue
-
-
-    def getFirst(self):
-        return self.mValue[N.FIRST]
-
-
-    def setFirst(self, value):
-        self.mValue[N.FIRST] = value
-
-
-    def getLast(self):
-        return self.mValue[N.LAST]
-
-
-    def setLast(self, value):
-        self.mValue[N.LAST] = value
-
-
-    def getMiddle(self):
-        return self.mValue[N.MIDDLE]
-
-
-    def setMiddle(self, value):
-        self.mValue[N.MIDDLE] = value
-
-
-    def getPrefix(self):
-        return self.mValue[N.PREFIX]
-
-
-    def setPrefix(self, value):
-        self.mValue[N.PREFIX] = value
-
-
-    def getSuffix(self):
-        return self.mValue[N.SUFFIX]
-
-
-    def setSuffix(self, value):
-        self.mValue[N.SUFFIX] = value
-
-
-    def getFullName(self):
-
-
-        def _stringOrList(item):
-            return item if isinstance(item, basestring) else " ".join(item)
-
-        results = []
-        for i in (N.PREFIX, N.FIRST, N.MIDDLE, N.LAST, N.SUFFIX):
-            result = _stringOrList(self.mValue[i])
-            if result:
-                results.append(result)
-
-        return " ".join(results)
-
-
-    def parse(self, data):
-        self.mValue = utils.parseDoubleNestedList(data, N.MAXITEMS)
-
-
-    def generate(self, os):
-        utils.generateDoubleNestedList(os, self.mValue)
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value

Deleted: PyCalendar/branches/json-2/src/pycalendar/nvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/nvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/nvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,51 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# vCard ADR value
-
-from pycalendar.n import N
-from pycalendar.value import PyCalendarValue
-
-class NValue(PyCalendarValue):
-
-    def __init__(self, value=None):
-        self.mValue = value if value is not None else N()
-
-
-    def duplicate(self):
-        return NValue(self.mValue.duplicate())
-
-
-    def getType(self):
-        return PyCalendarValue.VALUETYPE_N
-
-
-    def parse(self, data):
-        self.mValue.parse(data)
-
-
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_N, NValue, None)

Deleted: PyCalendar/branches/json-2/src/pycalendar/orgvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/orgvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/orgvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,54 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# vCard ORG value
-
-from pycalendar import utils
-from pycalendar.value import PyCalendarValue
-
-class OrgValue(PyCalendarValue):
-    """
-    mValue is a str or tuple of str
-    """
-
-    def __init__(self, value=None):
-        self.mValue = value
-
-
-    def duplicate(self):
-        return OrgValue(self.mValue)
-
-
-    def getType(self):
-        return PyCalendarValue.VALUETYPE_ORG
-
-
-    def parse(self, data):
-        self.mValue = utils.parseTextList(data, ';')
-
-
-    def generate(self, os):
-        utils.generateTextList(os, self.mValue, ';')
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_ORG, OrgValue, None)

Modified: PyCalendar/branches/json-2/src/pycalendar/outputfilter.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/outputfilter.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/outputfilter.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,7 +15,7 @@
 ##
 
 
-class PyCalendarOutputFilter(object):
+class OutputFilter(object):
 
     def __init__(self, type):
         self.mType = type

Copied: PyCalendar/branches/json-2/src/pycalendar/parameter.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/attribute.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/parameter.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/parameter.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,128 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+"""
+ICalendar parameter.
+
+The parameter can consist of one or more values, all string.
+"""
+
+from pycalendar import xmldefinitions, xmlutils
+from pycalendar.utils import encodeParameterValue
+import xml.etree.cElementTree as XML
+
+class Parameter(object):
+
+    def __init__(self, name, value=None):
+        self.mName = name
+        if value is None:
+            self.mValues = []
+        elif isinstance(value, basestring):
+            self.mValues = [value]
+        else:
+            self.mValues = value
+
+
+    def duplicate(self):
+        other = Parameter(self.mName)
+        other.mValues = self.mValues[:]
+        return other
+
+
+    def __hash__(self):
+        return hash((self.mName.upper(), tuple(self.mValues)))
+
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+
+    def __eq__(self, other):
+        if not isinstance(other, Parameter):
+            return False
+        return self.mName.upper() == other.mName.upper() and self.mValues == other.mValues
+
+
+    def getName(self):
+        return self.mName
+
+
+    def setName(self, name):
+        self.mName = name
+
+
+    def getFirstValue(self):
+        return self.mValues[0]
+
+
+    def getValues(self):
+        return self.mValues
+
+
+    def setValues(self, values):
+        self.mValues = values
+
+
+    def addValue(self, value):
+        self.mValues.append(value)
+
+
+    def removeValue(self, value):
+        self.mValues.remove(value)
+        return len(self.mValues)
+
+
+    def generate(self, os):
+        try:
+            os.write(self.mName)
+
+            # To support vCard 2.1 syntax we allow parameters without values
+            if self.mValues:
+                os.write("=")
+
+                first = True
+                for s in self.mValues:
+                    if first:
+                        first = False
+                    else:
+                        os.write(",")
+
+                    # Write with quotation if required
+                    self.generateValue(os, s)
+
+        except:
+            # We ignore errors
+            pass
+
+
+    def generateValue(self, os, str):
+
+        # ^-escaping
+        str = encodeParameterValue(str)
+
+        # Look for quoting
+        if str.find(":") != -1 or str.find(";") != -1 or str.find(",") != -1:
+            os.write("\"%s\"" % (str,))
+        else:
+            os.write(str)
+
+
+    def writeXML(self, node, namespace):
+        param = XML.SubElement(node, xmlutils.makeTag(namespace, self.getName()))
+        for value in self.getValues():
+            # TODO: need to figure out proper value types
+            text = XML.SubElement(param, xmlutils.makeTag(namespace, xmldefinitions.value_text))
+            text.text = value

Modified: PyCalendar/branches/json-2/src/pycalendar/period.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/period.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/period.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,17 +14,17 @@
 #    limitations under the License.
 ##
 
-from pycalendar import xmldefs
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
+from pycalendar import xmldefinitions, xmlutils
+from pycalendar.datetime import DateTime
+from pycalendar.duration import Duration
 from pycalendar.valueutils import ValueMixin
 import xml.etree.cElementTree as XML
 
-class PyCalendarPeriod(ValueMixin):
+class Period(ValueMixin):
 
     def __init__(self, start=None, end=None, duration=None):
 
-        self.mStart = start if start is not None else PyCalendarDateTime()
+        self.mStart = start if start is not None else DateTime()
 
         if end is not None:
             self.mEnd = end
@@ -36,12 +36,12 @@
             self.mUseDuration = True
         else:
             self.mEnd = self.mStart.duplicate()
-            self.mDuration = PyCalendarDuration()
+            self.mDuration = Duration()
             self.mUseDuration = False
 
 
     def duplicate(self):
-        other = PyCalendarPeriod(start=self.mStart.duplicate(), end=self.mEnd.duplicate())
+        other = Period(start=self.mStart.duplicate(), end=self.mEnd.duplicate())
         other.mUseDuration = self.mUseDuration
         return other
 
@@ -51,7 +51,7 @@
 
 
     def __repr__(self):
-        return "PyCalendarPeriod %s" % (self.getText(),)
+        return "Period %s" % (self.getText(),)
 
 
     def __str__(self):
@@ -110,14 +110,14 @@
 
 
     def writeXML(self, node, namespace):
-        start = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_start))
+        start = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.period_start))
         start.text = self.mStart.getXMLText()
 
         if self.mUseDuration:
-            duration = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_duration))
+            duration = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.period_duration))
             duration.text = self.mDuration.getText()
         else:
-            end = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_end))
+            end = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.period_end))
             end.text = self.mEnd.getXMLText()
 
 

Modified: PyCalendar/branches/json-2/src/pycalendar/periodvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/periodvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/periodvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,42 +14,18 @@
 #    limitations under the License.
 ##
 
-from pycalendar import xmldefs
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.period import Period
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
 
-class PyCalendarPeriodValue(PyCalendarValue):
+class PeriodValue(WrapperValue, Value):
 
     def __init__(self, value=None):
-        self.mValue = value if value is not None else PyCalendarPeriod()
+        self.mValue = value if value is not None else Period()
 
 
-    def duplicate(self):
-        return PyCalendarPeriodValue(self.mValue.duplicate())
-
-
     def getType(self):
-        return PyCalendarValue.VALUETYPE_PERIOD
+        return Value.VALUETYPE_PERIOD
 
-
-    def parse(self, data):
-        self.mValue.parse(data)
-
-
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
-    def writeXML(self, node, namespace):
-        value = self.getXMLNode(node, namespace)
-        value.text = self.mValue.writeXML()
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_PERIOD, PyCalendarPeriodValue, xmldefs.value_period)
+Value.registerType(Value.VALUETYPE_PERIOD, PeriodValue, xmldefinitions.value_period)

Modified: PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,11 +14,11 @@
 #    limitations under the License.
 ##
 
-# iCalendar UTC Offset value
+# iCalendar generic text-like value
 
-from pycalendar.value import PyCalendarValue
+from pycalendar.value import Value
 
-class PyCalendarPlainTextValue(PyCalendarValue):
+class PlainTextValue(Value):
 
     def __init__(self, value=''):
         self.mValue = value
@@ -28,7 +28,7 @@
         return self.__class__(self.mValue)
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
         # No decoding required
         self.mValue = data
 

Modified: PyCalendar/branches/json-2/src/pycalendar/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/property.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,227 +14,51 @@
 #    limitations under the License.
 ##
 
-from pycalendar import definitions, xmldefs
-from pycalendar import stringutils
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.binaryvalue import PyCalendarBinaryValue
-from pycalendar.caladdressvalue import PyCalendarCalAddressValue
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.datetimevalue import PyCalendarDateTimeValue
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.durationvalue import PyCalendarDurationValue
-from pycalendar.exceptions import PyCalendarInvalidProperty
-from pycalendar.integervalue import PyCalendarIntegerValue
-from pycalendar.multivalue import PyCalendarMultiValue
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.periodvalue import PyCalendarPeriodValue
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.recurrence import PyCalendarRecurrence
-from pycalendar.recurrencevalue import PyCalendarRecurrenceValue
-from pycalendar.requeststatusvalue import PyCalendarRequestStatusValue
-from pycalendar.unknownvalue import PyCalendarUnknownValue
-from pycalendar.urivalue import PyCalendarURIValue
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
+from pycalendar import stringutils, xmlutils, xmldefinitions
+from pycalendar.parameter import Parameter
+from pycalendar.binaryvalue import BinaryValue
+from pycalendar.caladdressvalue import CalAddressValue
+from pycalendar.datetimevalue import DateTimeValue
+from pycalendar.durationvalue import DurationValue
+from pycalendar.exceptions import InvalidProperty
+from pycalendar.integervalue import IntegerValue
+from pycalendar.multivalue import MultiValue
+from pycalendar.periodvalue import PeriodValue
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.unknownvalue import UnknownValue
+from pycalendar.urivalue import URIValue
+from pycalendar.utcoffsetvalue import UTCOffsetValue
 from pycalendar.utils import decodeParameterValue
-from pycalendar.value import PyCalendarValue
+from pycalendar.value import Value
 import cStringIO as StringIO
 import xml.etree.cElementTree as XML
 
-class PyCalendarProperty(object):
+class PropertyBase(object):
 
-    sDefaultValueTypeMap = {
+    # Mappings between various tokens and internal definitions
+    sDefaultValueTypeMap = {}
+    sValueTypeMap = {}
+    sTypeValueMap = {}
+    sMultiValues = set()
+    sTextVariants = set()
 
-        # 5545 Section 3.7
-        definitions.cICalProperty_CALSCALE         : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_METHOD           : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_PRODID           : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_VERSION          : PyCalendarValue.VALUETYPE_TEXT,
+    sUsesGroup = False
 
-        # 5545 Section 3.8.1
-        definitions.cICalProperty_ATTACH           : PyCalendarValue.VALUETYPE_URI,
-        definitions.cICalProperty_CATEGORIES       : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_CLASS            : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_COMMENT          : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_DESCRIPTION      : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_GEO              : PyCalendarValue.VALUETYPE_GEO,
-        definitions.cICalProperty_LOCATION         : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_PERCENT_COMPLETE : PyCalendarValue.VALUETYPE_INTEGER,
-        definitions.cICalProperty_PRIORITY         : PyCalendarValue.VALUETYPE_INTEGER,
-        definitions.cICalProperty_RESOURCES        : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_STATUS           : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_SUMMARY          : PyCalendarValue.VALUETYPE_TEXT,
+    sVariant = "none"   # Used to differentiate different forms of text parsing
 
-        # 5545 Section 3.8.2
-        definitions.cICalProperty_COMPLETED : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_DTEND     : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_DUE       : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_DTSTART   : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_DURATION  : PyCalendarValue.VALUETYPE_DURATION,
-        definitions.cICalProperty_FREEBUSY  : PyCalendarValue.VALUETYPE_PERIOD,
-        definitions.cICalProperty_TRANSP    : PyCalendarValue.VALUETYPE_TEXT,
+    sValue = None
+    sText = None
 
-        # 5545 Section 3.8.3
-        definitions.cICalProperty_TZID         : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_TZNAME       : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_TZOFFSETFROM : PyCalendarValue.VALUETYPE_UTC_OFFSET,
-        definitions.cICalProperty_TZOFFSETTO   : PyCalendarValue.VALUETYPE_UTC_OFFSET,
-        definitions.cICalProperty_TZURL        : PyCalendarValue.VALUETYPE_URI,
-
-        # 5545 Section 3.8.4
-        definitions.cICalProperty_ATTENDEE      : PyCalendarValue.VALUETYPE_CALADDRESS,
-        definitions.cICalProperty_CONTACT       : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_ORGANIZER     : PyCalendarValue.VALUETYPE_CALADDRESS,
-        definitions.cICalProperty_RECURRENCE_ID : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_RELATED_TO    : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_URL           : PyCalendarValue.VALUETYPE_URI,
-        definitions.cICalProperty_UID           : PyCalendarValue.VALUETYPE_TEXT,
-
-        # 5545 Section 3.8.5
-        definitions.cICalProperty_EXDATE : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_EXRULE : PyCalendarValue.VALUETYPE_RECUR, # 2445 only
-        definitions.cICalProperty_RDATE  : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_RRULE  : PyCalendarValue.VALUETYPE_RECUR,
-
-        # 5545 Section 3.8.6
-        definitions.cICalProperty_ACTION  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_REPEAT  : PyCalendarValue.VALUETYPE_INTEGER,
-        definitions.cICalProperty_TRIGGER : PyCalendarValue.VALUETYPE_DURATION,
-
-        # 5545 Section 3.8.7
-        definitions.cICalProperty_CREATED       : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_DTSTAMP       : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_LAST_MODIFIED : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_SEQUENCE      : PyCalendarValue.VALUETYPE_INTEGER,
-
-        # 5545 Section 3.8.8
-        definitions.cICalProperty_REQUEST_STATUS : PyCalendarValue.VALUETYPE_REQUEST_STATUS,
-
-        # Extensions: draft-daboo-valarm-extensions-03
-        definitions.cICalProperty_ACKNOWLEDGED   : PyCalendarValue.VALUETYPE_DATETIME,
-
-        # Apple Extensions
-        definitions.cICalProperty_XWRCALNAME  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_XWRCALDESC  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_XWRALARMUID : PyCalendarValue.VALUETYPE_TEXT,
-
-        # Mulberry extensions
-        definitions.cICalProperty_ACTION_X_SPEAKTEXT  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalProperty_ALARM_X_LASTTRIGGER : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalProperty_ALARM_X_ALARMSTATUS : PyCalendarValue.VALUETYPE_TEXT,
-    }
-
-    sValueTypeMap = {
-
-        # 5545 Section 3.3
-        definitions.cICalValue_BINARY      : PyCalendarValue.VALUETYPE_BINARY,
-        definitions.cICalValue_BOOLEAN     : PyCalendarValue.VALUETYPE_BOOLEAN,
-        definitions.cICalValue_CAL_ADDRESS : PyCalendarValue.VALUETYPE_CALADDRESS,
-        definitions.cICalValue_DATE        : PyCalendarValue.VALUETYPE_DATE,
-        definitions.cICalValue_DATE_TIME   : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.cICalValue_DURATION    : PyCalendarValue.VALUETYPE_DURATION,
-        definitions.cICalValue_FLOAT       : PyCalendarValue.VALUETYPE_FLOAT,
-        definitions.cICalValue_INTEGER     : PyCalendarValue.VALUETYPE_INTEGER,
-        definitions.cICalValue_PERIOD      : PyCalendarValue.VALUETYPE_PERIOD,
-        definitions.cICalValue_RECUR       : PyCalendarValue.VALUETYPE_RECUR,
-        definitions.cICalValue_TEXT        : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.cICalValue_TIME        : PyCalendarValue.VALUETYPE_TIME,
-        definitions.cICalValue_URI         : PyCalendarValue.VALUETYPE_URI,
-        definitions.cICalValue_UTC_OFFSET  : PyCalendarValue.VALUETYPE_UTC_OFFSET,
-    }
-
-    sTypeValueMap = {
-
-        # 5545 Section 3.3
-        PyCalendarValue.VALUETYPE_BINARY         : definitions.cICalValue_BINARY,
-        PyCalendarValue.VALUETYPE_BOOLEAN        : definitions.cICalValue_BOOLEAN,
-        PyCalendarValue.VALUETYPE_CALADDRESS     : definitions.cICalValue_CAL_ADDRESS,
-        PyCalendarValue.VALUETYPE_DATE           : definitions.cICalValue_DATE,
-        PyCalendarValue.VALUETYPE_DATETIME       : definitions.cICalValue_DATE_TIME,
-        PyCalendarValue.VALUETYPE_DURATION       : definitions.cICalValue_DURATION,
-        PyCalendarValue.VALUETYPE_FLOAT          : definitions.cICalValue_FLOAT,
-        PyCalendarValue.VALUETYPE_GEO            : definitions.cICalValue_FLOAT,
-        PyCalendarValue.VALUETYPE_INTEGER        : definitions.cICalValue_INTEGER,
-        PyCalendarValue.VALUETYPE_PERIOD         : definitions.cICalValue_PERIOD,
-        PyCalendarValue.VALUETYPE_RECUR          : definitions.cICalValue_RECUR,
-        PyCalendarValue.VALUETYPE_TEXT           : definitions.cICalValue_TEXT,
-        PyCalendarValue.VALUETYPE_REQUEST_STATUS : definitions.cICalValue_TEXT,
-        PyCalendarValue.VALUETYPE_TIME           : definitions.cICalValue_TIME,
-        PyCalendarValue.VALUETYPE_URI            : definitions.cICalValue_URI,
-        PyCalendarValue.VALUETYPE_UTC_OFFSET     : definitions.cICalValue_UTC_OFFSET,
-    }
-
-    sMultiValues = set((
-        definitions.cICalProperty_CATEGORIES,
-        definitions.cICalProperty_RESOURCES,
-        definitions.cICalProperty_FREEBUSY,
-        definitions.cICalProperty_EXDATE,
-        definitions.cICalProperty_RDATE,
-    ))
-
-    @staticmethod
-    def registerDefaultValue(propname, valuetype):
-        if propname not in PyCalendarProperty.sDefaultValueTypeMap:
-            PyCalendarProperty.sDefaultValueTypeMap[propname] = valuetype
-
-
     def __init__(self, name=None, value=None, valuetype=None):
-        self._init_PyCalendarProperty()
-        self.mName = name if name is not None else ""
+        raise NotImplementedError
 
-        # The None check speeds up .duplicate()
-        if value is None:
-            pass
 
-        # Order these based on most likely occurrence to speed up this method
-        elif isinstance(value, str):
-            self._init_attr_value_text(value, valuetype if valuetype else PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_UNKNOWN))
-
-        elif isinstance(value, PyCalendarDateTime):
-            self._init_attr_value_datetime(value)
-
-        elif isinstance(value, PyCalendarDuration):
-            self._init_attr_value_duration(value)
-
-        elif isinstance(value, PyCalendarRecurrence):
-            self._init_attr_value_recur(value)
-
-        elif isinstance(value, PyCalendarPeriod):
-            self._init_attr_value_period(value)
-
-        elif isinstance(value, int):
-            self._init_attr_value_int(value)
-
-        elif isinstance(value, list):
-            if name.upper() == definitions.cICalProperty_REQUEST_STATUS:
-                self._init_attr_value_requeststatus(value)
-            else:
-                period_list = False
-                if len(value) != 0:
-                    period_list = isinstance(value[0], PyCalendarPeriod)
-                if period_list:
-                    self._init_attr_value_periodlist(value)
-                else:
-                    self._init_attr_value_datetimelist(value)
-
-        elif isinstance(value, PyCalendarUTCOffsetValue):
-            self._init_attr_value_utcoffset(value)
-
-
     def duplicate(self):
-        other = PyCalendarProperty(self.mName)
-        for attrname, attrs in self.mAttributes.items():
-            other.mAttributes[attrname] = [i.duplicate() for i in attrs]
-        other.mValue = self.mValue.duplicate()
+        raise NotImplementedError
 
-        return other
 
-
     def __hash__(self):
-        return hash((
-            self.mName,
-            tuple([tuple(self.mAttributes[attrname]) for attrname in sorted(self.mAttributes.keys())]),
-            self.mValue,
-        ))
+        raise NotImplementedError
 
 
     def __ne__(self, other):
@@ -242,19 +66,26 @@
 
 
     def __eq__(self, other):
-        if not isinstance(other, PyCalendarProperty):
-            return False
-        return self.mName == other.mName and self.mValue == other.mValue and self.mAttributes == other.mAttributes
+        raise NotImplementedError
 
 
     def __repr__(self):
-        return "PyCalendarProperty: %s" % (self.getText(),)
+        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
 
@@ -263,33 +94,33 @@
         self.mName = name
 
 
-    def getAttributes(self):
-        return self.mAttributes
+    def getParameters(self):
+        return self.mParameters
 
 
-    def setAttributes(self, attributes):
-        self.mAttributes = dict([(k.upper(), v) for k, v in attributes.iteritems()])
+    def setParameters(self, parameters):
+        self.mParameters = dict([(k.upper(), v) for k, v in parameters.iteritems()])
 
 
-    def hasAttribute(self, attr):
-        return attr.upper() in self.mAttributes
+    def hasParameter(self, attr):
+        return attr.upper() in self.mParameters
 
 
-    def getAttributeValue(self, attr):
-        return self.mAttributes[attr.upper()][0].getFirstValue()
+    def getParameterValue(self, attr):
+        return self.mParameters[attr.upper()][0].getFirstValue()
 
 
-    def addAttribute(self, attr):
-        self.mAttributes.setdefault(attr.getName().upper(), []).append(attr)
+    def addParameter(self, attr):
+        self.mParameters.setdefault(attr.getName().upper(), []).append(attr)
 
 
-    def replaceAttribute(self, attr):
-        self.mAttributes[attr.getName().upper()] = [attr]
+    def replaceParameter(self, attr):
+        self.mParameters[attr.getName().upper()] = [attr]
 
 
-    def removeAttributes(self, attr):
-        if attr.upper() in self.mAttributes:
-            del self.mAttributes[attr.upper()]
+    def removeParameters(self, attr):
+        if attr.upper() in self.mParameters:
+            del self.mParameters[attr.upper()]
 
 
     def getValue(self):
@@ -298,7 +129,7 @@
 
     def getBinaryValue(self):
 
-        if isinstance(self.mValue, PyCalendarBinaryValue):
+        if isinstance(self.mValue, BinaryValue):
             return self.mValue
         else:
             return None
@@ -306,7 +137,7 @@
 
     def getCalAddressValue(self):
 
-        if isinstance(self.mValue, PyCalendarCalAddressValue):
+        if isinstance(self.mValue, CalAddressValue):
             return self.mValue
         else:
             return None
@@ -314,7 +145,7 @@
 
     def getDateTimeValue(self):
 
-        if isinstance(self.mValue, PyCalendarDateTimeValue):
+        if isinstance(self.mValue, DateTimeValue):
             return self.mValue
         else:
             return None
@@ -322,7 +153,7 @@
 
     def getDurationValue(self):
 
-        if isinstance(self.mValue, PyCalendarDurationValue):
+        if isinstance(self.mValue, DurationValue):
             return self.mValue
         else:
             return None
@@ -330,7 +161,7 @@
 
     def getIntegerValue(self):
 
-        if isinstance(self.mValue, PyCalendarIntegerValue):
+        if isinstance(self.mValue, IntegerValue):
             return self.mValue
         else:
             return None
@@ -338,7 +169,7 @@
 
     def getMultiValue(self):
 
-        if isinstance(self.mValue, PyCalendarMultiValue):
+        if isinstance(self.mValue, MultiValue):
             return self.mValue
         else:
             return None
@@ -346,23 +177,15 @@
 
     def getPeriodValue(self):
 
-        if isinstance(self.mValue, PyCalendarPeriodValue):
+        if isinstance(self.mValue, PeriodValue):
             return self.mValue
         else:
             return None
 
 
-    def getRecurrenceValue(self):
-
-        if isinstance(self.mValue, PyCalendarRecurrenceValue):
-            return self.mValue
-        else:
-            return None
-
-
     def getTextValue(self):
 
-        if isinstance(self.mValue, PyCalendarPlainTextValue):
+        if isinstance(self.mValue, PlainTextValue):
             return self.mValue
         else:
             return None
@@ -370,7 +193,7 @@
 
     def getURIValue(self):
 
-        if isinstance(self.mValue, PyCalendarURIValue):
+        if isinstance(self.mValue, URIValue):
             return self.mValue
         else:
             return None
@@ -378,70 +201,86 @@
 
     def getUTCOffsetValue(self):
 
-        if isinstance(self.mValue, PyCalendarUTCOffsetValue):
+        if isinstance(self.mValue, UTCOffsetValue):
             return self.mValue
         else:
             return None
 
 
     def parse(self, data):
-        # Look for attribute or value delimiter
+        # Look for parameter or value delimiter
         prop_name, txt = stringutils.strduptokenstr(data, ";:")
         if not prop_name:
-            raise PyCalendarInvalidProperty("Invalid property", data)
+            raise InvalidProperty("Invalid property", data)
 
         # We have the name
-        self.mName = prop_name
+        if self.sUsesGroup:
+            # Check for group prefix
+            splits = prop_name.split(".", 1)
+            if len(splits) == 2:
+                # We have both group and name
+                self.mGroup = splits[0]
+                self.mName = splits[1]
+            else:
+                # We have the name
+                self.mName = prop_name
+        else:
+            self.mName = prop_name
 
-        # TODO: Try to use static string for the name
-
         # Now loop getting data
+        txt = self.parseParameters(txt, data)
+        self.createValue(txt)
+
+        # We must have a value of some kind
+        if self.mValue is None:
+            raise InvalidProperty("Invalid property", data)
+
+        return True
+
+
+    def parseParameters(self, txt, data):
+        """
+        Parse parameters, return string point at value.
+        """
+
         try:
             while txt:
                 if txt[0] == ';':
-                    # Parse attribute
+                    # Parse parameter
 
                     # Move past delimiter
                     txt = txt[1:]
 
                     # Get quoted string or token
-                    attribute_name, txt = stringutils.strduptokenstr(txt, "=")
-                    if attribute_name is None:
-                        raise PyCalendarInvalidProperty("Invalid property", data)
+                    parameter_name, txt = stringutils.strduptokenstr(txt, "=")
+                    if parameter_name is None:
+                        raise InvalidProperty("Invalid property", data)
                     txt = txt[1:]
-                    attribute_value, txt = stringutils.strduptokenstr(txt, ":;,")
-                    if attribute_value is None:
-                        raise PyCalendarInvalidProperty("Invalid property", data)
+                    parameter_value, txt = stringutils.strduptokenstr(txt, ":;,")
+                    if parameter_value is None:
+                        raise InvalidProperty("Invalid property", data)
 
-                    # Now add attribute value (decode ^-escpaing)
-                    attrvalue = PyCalendarAttribute(name=attribute_name, value=decodeParameterValue(attribute_value))
-                    self.mAttributes.setdefault(attribute_name.upper(), []).append(attrvalue)
+                    # 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:]
-                        attribute_value2, txt = stringutils.strduptokenstr(txt, ":;,")
-                        if attribute_value2 is None:
-                            raise PyCalendarInvalidProperty("Invalid property", data)
-                        attrvalue.addValue(decodeParameterValue(attribute_value2))
+                        parameter_value2, txt = stringutils.strduptokenstr(txt, ":;,")
+                        if parameter_value2 is None:
+                            raise InvalidProperty("Invalid property", data)
+                        attrvalue.addValue(decodeParameterValue(parameter_value2))
                 elif txt[0] == ':':
-                    txt = txt[1:]
-                    self.createValue(txt)
-                    txt = None
+                    return txt[1:]
                 else:
                     # We should never get here but if we do we need to terminate the loop
-                    raise PyCalendarInvalidProperty("Invalid property", data)
+                    raise InvalidProperty("Invalid property", data)
 
         except IndexError:
-            raise PyCalendarInvalidProperty("Invalid property", data)
+            raise InvalidProperty("Invalid property", data)
 
-        # We must have a value of some kind
-        if self.mValue is None:
-            raise PyCalendarInvalidProperty("Invalid property", data)
 
-        return True
-
-
     def getText(self):
         os = StringIO.StringIO()
         self.generate(os)
@@ -465,15 +304,17 @@
     # Write out the actual property, possibly skipping the value
     def generateValue(self, os, novalue):
 
-        self.setupValueAttribute()
+        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 attributes
-        for key in sorted(self.mAttributes.keys()):
-            for attr in self.mAttributes[key]:
+        # Write all parameters
+        for key in sorted(self.mParameters.keys()):
+            for attr in self.mParameters[key]:
                 sout.write(";")
                 attr.generate(sout)
 
@@ -493,9 +334,10 @@
             # 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 + 74
+                offset = start + lineWrap
                 if offset >= len(temp):
                     line = temp[start:]
                     os.write(line)
@@ -509,6 +351,7 @@
                     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
 
@@ -521,7 +364,7 @@
         self.generateValueXML(node, namespace, False)
 
 
-    def generateFilteredXML(self, node, namespace, filter):
+    def writeXMLFiltered(self, node, namespace, filter):
 
         # Check for property in filter and whether value is written out
         test, novalue = filter.testPropertyValue(self.mName.upper())
@@ -532,14 +375,14 @@
     # Write out the actual property, possibly skipping the value
     def generateValueXML(self, node, namespace, novalue):
 
-        prop = XML.SubElement(node, xmldefs.makeTag(namespace, self.getName()))
+        prop = XML.SubElement(node, xmlutils.makeTag(namespace, self.getName()))
 
-        # Write all attributes
-        if len(self.mAttributes):
-            params = XML.SubElement(prop, xmldefs.makeTag(namespace, xmldefs.parameters))
-            for key in sorted(self.mAttributes.keys()):
-                for attr in self.mAttributes[key]:
-                    if attr.getName() != "VALUE":
+        # 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
@@ -547,90 +390,73 @@
             self.mValue.writeXML(prop, namespace)
 
 
-    def _init_PyCalendarProperty(self):
-        self.mName = ""
-        self.mAttributes = {}
-        self.mValue = None
-
-
     def createValue(self, data):
         # Tidy first
         self.mValue = None
 
         # Get value type from property name
-        type = PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_UNKNOWN)
+        value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN)
 
         # Check whether custom value is set
-        if definitions.cICalAttribute_VALUE in self.mAttributes:
-            type = PyCalendarProperty.sValueTypeMap.get(self.getAttributeValue(definitions.cICalAttribute_VALUE), type)
+        if self.sValue in self.mParameters:
+            attr = self.getParameterValue(self.sValue)
+            if attr != self.sText or self.mName.upper() not in self.sTextVariants:
+                value_type = self.sValueTypeMap.get(attr, value_type)
 
         # Check for multivalued
-        if self.mName.upper() in PyCalendarProperty.sMultiValues:
-            self.mValue = PyCalendarMultiValue(type)
+        if self.mName.upper() in self.sMultiValues:
+            self.mValue = MultiValue(value_type)
         else:
             # Create the type
-            self.mValue = PyCalendarValue.createFromType(type)
+            self.mValue = Value.createFromType(value_type)
 
         # Now parse the data
         try:
-            self.mValue.parse(data)
+            self.mValue.parse(data, self.sVariant)
         except ValueError:
-            raise PyCalendarInvalidProperty("Invalid property value", data)
+            raise InvalidProperty("Invalid property value", data)
 
-        # Special post-create for some types
-        if type in (PyCalendarValue.VALUETYPE_TIME, PyCalendarValue.VALUETYPE_DATETIME):
-            # Look for TZID attribute
-            tzid = None
-            if (self.hasAttribute(definitions.cICalAttribute_TZID)):
-                tzid = self.getAttributeValue(definitions.cICalAttribute_TZID)
+        self._postCreateValue(value_type)
 
-                if isinstance(self.mValue, PyCalendarDateTimeValue):
-                    self.mValue.getValue().setTimezoneID(tzid)
-                elif isinstance(self.mValue, PyCalendarMultiValue):
-                    for item in self.mValue.getValues():
-                        if isinstance(item, PyCalendarDateTimeValue):
-                            item.getValue().setTimezoneID(tzid)
 
+    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
-        type = PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT)
+        value_type = self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_TEXT)
 
         # Check whether custom value is set
-        if definitions.cICalAttribute_VALUE in self.mAttributes:
-            type = PyCalendarProperty.sValueTypeMap.get(self.getAttributeValue(definitions.cICalAttribute_VALUE), type)
+        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 PyCalendarProperty.sMultiValues:
-            self.mValue = PyCalendarMultiValue(type)
+        if self.mName.upper() in self.sMultiValues:
+            self.mValue = MultiValue(value_type)
         else:
             # Create the type
-            self.mValue = PyCalendarValue.createFromType(type)
+            self.mValue = Value.createFromType(value_type)
 
         self.mValue.setValue(value)
 
         # Special post-create for some types
-        if type in (PyCalendarValue.VALUETYPE_TIME, PyCalendarValue.VALUETYPE_DATETIME):
-            # Look for TZID attribute
-            tzid = None
-            if (self.hasAttribute(definitions.cICalAttribute_TZID)):
-                tzid = self.getAttributeValue(definitions.cICalAttribute_TZID)
+        self._postCreateValue(value_type)
 
-                if isinstance(self.mValue, PyCalendarDateTimeValue):
-                    self.mValue.getValue().setTimezoneID(tzid)
-                elif isinstance(self.mValue, PyCalendarMultiValue):
-                    for item in self.mValue.getValues():
-                        if isinstance(item, PyCalendarDateTimeValue):
-                            item.getValue().setTimezoneID(tzid)
 
+    def setupValueParameter(self):
+        if self.sValue in self.mParameters:
+            del self.mParameters[self.sValue]
 
-    def setupValueAttribute(self):
-        if definitions.cICalAttribute_VALUE in self.mAttributes:
-            del self.mAttributes[definitions.cICalAttribute_VALUE]
-
         # Only if we have a value right now
         if self.mValue is None:
             return
@@ -641,114 +467,41 @@
         actual_type = self.mValue.getType()
         if default_type is None or default_type != actual_type:
             actual_value = self.sTypeValueMap.get(actual_type)
-            if actual_value is not None and (default_type is not None or actual_type != PyCalendarValue.VALUETYPE_TEXT):
-                self.mAttributes.setdefault(definitions.cICalAttribute_VALUE, []).append(PyCalendarAttribute(name=definitions.cICalAttribute_VALUE, value=actual_value))
+            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 = PyCalendarIntegerValue(value=ival)
+        self.mValue = IntegerValue(value=ival)
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()
 
 
     def _init_attr_value_text(self, txt, value_type):
         # Value
-        self.mValue = PyCalendarValue.createFromType(value_type)
-        if isinstance(self.mValue, PyCalendarPlainTextValue) or isinstance(self.mValue, PyCalendarUnknownValue):
+        self.mValue = Value.createFromType(value_type)
+        if isinstance(self.mValue, PlainTextValue) or isinstance(self.mValue, UnknownValue):
             self.mValue.setValue(txt)
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()
 
 
-    def _init_attr_value_requeststatus(self, reqstatus):
-        # Value
-        self.mValue = PyCalendarRequestStatusValue(reqstatus)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
     def _init_attr_value_datetime(self, dt):
         # Value
-        self.mValue = PyCalendarDateTimeValue(value=dt)
+        self.mValue = DateTimeValue(value=dt)
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()
 
-        # Look for timezone
-        if not dt.isDateOnly() and dt.local():
-            if definitions.cICalAttribute_TZID in self.mAttributes:
-                del self.mAttributes[definitions.cICalAttribute_TZID]
-            self.mAttributes.setdefault(definitions.cICalAttribute_TZID, []).append(
-                    PyCalendarAttribute(name=definitions.cICalAttribute_TZID, value=dt.getTimezoneID()))
 
-
-    def _init_attr_value_datetimelist(self, dtl):
-        # Value
-        date_only = (len(dtl) > 0) and dtl[0].isDateOnly()
-        if date_only:
-            self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_DATE)
-        else:
-            self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_DATETIME)
-
-        for dt in dtl:
-            self.mValue.addValue(PyCalendarDateTimeValue(dt))
-
-        # Attributes
-        self.setupValueAttribute()
-
-        # Look for timezone
-        if ((len(dtl) > 0)
-                and not dtl[0].isDateOnly()
-                and dtl[0].local()):
-            if definitions.cICalAttribute_TZID in self.mAttributes:
-                del self.mAttributes[definitions.cICalAttribute_TZID]
-            self.mAttributes.setdefault(definitions.cICalAttribute_TZID, []).append(
-                    PyCalendarAttribute(name=definitions.cICalAttribute_TZID, value=dtl[0].getTimezoneID()))
-
-
-    def _init_attr_value_periodlist(self, periodlist):
-        # Value
-        self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_PERIOD)
-        for period in periodlist:
-            self.mValue.addValue(PyCalendarPeriodValue(period))
-
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_duration(self, du):
-        # Value
-        self.mValue = PyCalendarDurationValue(value=du)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_period(self, pe):
-        # Value
-        self.mValue = PyCalendarPeriodValue(value=pe)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_recur(self, recur):
-        # Value
-        self.mValue = PyCalendarRecurrenceValue(value=recur)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
     def _init_attr_value_utcoffset(self, utcoffset):
         # Value
-        self.mValue = PyCalendarUTCOffsetValue()
+        self.mValue = UTCOffsetValue()
         self.mValue.setValue(utcoffset.getValue())
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()

Deleted: PyCalendar/branches/json-2/src/pycalendar/recurrence.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/recurrence.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/recurrence.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,1637 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import xmldefs
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.valueutils import ValueMixin
-import cStringIO as StringIO
-import xml.etree.cElementTree as XML
-
-def WeekDayNumCompare_compare(w1, w2):
-
-    if w1[0] < w2[0]:
-        return -1
-    elif w1[0] > w2[0]:
-        return 1
-    elif w1[1] < w2[1]:
-        return -1
-    elif w1[1] > w2[1]:
-        return 1
-    else:
-        return 0
-
-
-
-def WeekDayNumSort_less_than(w1, w2):
-
-    return (w1[0] < w2[0]) or (w1[0] == w2[0]) and (w1[1] < w2[1])
-
-
-
-class PyCalendarRecurrence(ValueMixin):
-
-    cFreqMap = {
-        definitions.cICalValue_RECUR_SECONDLY : definitions.eRecurrence_SECONDLY,
-        definitions.cICalValue_RECUR_MINUTELY : definitions.eRecurrence_MINUTELY,
-        definitions.cICalValue_RECUR_HOURLY   : definitions.eRecurrence_HOURLY,
-        definitions.cICalValue_RECUR_DAILY    : definitions.eRecurrence_DAILY,
-        definitions.cICalValue_RECUR_WEEKLY   : definitions.eRecurrence_WEEKLY,
-        definitions.cICalValue_RECUR_MONTHLY  : definitions.eRecurrence_MONTHLY,
-        definitions.cICalValue_RECUR_YEARLY   : definitions.eRecurrence_YEARLY,
-    }
-
-    cFreqToXMLMap = {
-        definitions.eRecurrence_SECONDLY: xmldefs.recur_freq_secondly,
-        definitions.eRecurrence_MINUTELY: xmldefs.recur_freq_minutely,
-        definitions.eRecurrence_HOURLY: xmldefs.recur_freq_hourly,
-        definitions.eRecurrence_DAILY: xmldefs.recur_freq_daily,
-        definitions.eRecurrence_WEEKLY: xmldefs.recur_freq_weekly,
-        definitions.eRecurrence_MONTHLY: xmldefs.recur_freq_monthly,
-        definitions.eRecurrence_YEARLY: xmldefs.recur_freq_yearly,
-    }
-
-    cRecurMap = {
-        definitions.cICalValue_RECUR_FREQ       : definitions.eRecurrence_FREQ,
-        definitions.cICalValue_RECUR_UNTIL      : definitions.eRecurrence_UNTIL,
-        definitions.cICalValue_RECUR_COUNT      : definitions.eRecurrence_COUNT,
-        definitions.cICalValue_RECUR_INTERVAL   : definitions.eRecurrence_INTERVAL,
-        definitions.cICalValue_RECUR_BYSECOND   : definitions.eRecurrence_BYSECOND,
-        definitions.cICalValue_RECUR_BYMINUTE   : definitions.eRecurrence_BYMINUTE,
-        definitions.cICalValue_RECUR_BYHOUR     : definitions.eRecurrence_BYHOUR,
-        definitions.cICalValue_RECUR_BYDAY      : definitions.eRecurrence_BYDAY,
-        definitions.cICalValue_RECUR_BYMONTHDAY : definitions.eRecurrence_BYMONTHDAY,
-        definitions.cICalValue_RECUR_BYYEARDAY  : definitions.eRecurrence_BYYEARDAY,
-        definitions.cICalValue_RECUR_BYWEEKNO   : definitions.eRecurrence_BYWEEKNO,
-        definitions.cICalValue_RECUR_BYMONTH    : definitions.eRecurrence_BYMONTH,
-        definitions.cICalValue_RECUR_BYSETPOS   : definitions.eRecurrence_BYSETPOS,
-        definitions.cICalValue_RECUR_WKST       : definitions.eRecurrence_WKST,
-    }
-
-    cWeekdayMap = {
-        definitions.cICalValue_RECUR_WEEKDAY_SU : definitions.eRecurrence_WEEKDAY_SU,
-        definitions.cICalValue_RECUR_WEEKDAY_MO : definitions.eRecurrence_WEEKDAY_MO,
-        definitions.cICalValue_RECUR_WEEKDAY_TU : definitions.eRecurrence_WEEKDAY_TU,
-        definitions.cICalValue_RECUR_WEEKDAY_WE : definitions.eRecurrence_WEEKDAY_WE,
-        definitions.cICalValue_RECUR_WEEKDAY_TH : definitions.eRecurrence_WEEKDAY_TH,
-        definitions.cICalValue_RECUR_WEEKDAY_FR : definitions.eRecurrence_WEEKDAY_FR,
-        definitions.cICalValue_RECUR_WEEKDAY_SA : definitions.eRecurrence_WEEKDAY_SA,
-    }
-
-    cWeekdayRecurMap = dict([(v, k) for k, v in cWeekdayMap.items()])
-
-    cUnknownIndex = -1
-
-    def __init__(self):
-        self.init_PyCalendarRecurrence()
-
-
-    def duplicate(self):
-        other = PyCalendarRecurrence()
-
-        other.mFreq = self.mFreq
-
-        other.mUseCount = self.mUseCount
-        other.mCount = self.mCount
-        other.mUseUntil = self.mUseUntil
-        if other.mUseUntil:
-            other.mUntil = self.mUntil.duplicate()
-
-        other.mInterval = self.mInterval
-        if self.mBySeconds is not None:
-            other.mBySeconds = self.mBySeconds[:]
-        if self.mByMinutes is not None:
-            other.mByMinutes = self.mByMinutes[:]
-        if self.mByHours is not None:
-            other.mByHours = self.mByHours[:]
-        if self.mByDay is not None:
-            other.mByDay = self.mByDay[:]
-        if self.mByMonthDay is not None:
-            other.mByMonthDay = self.mByMonthDay[:]
-        if self.mByYearDay is not None:
-            other.mByYearDay = self.mByYearDay[:]
-        if self.mByWeekNo is not None:
-            other.mByWeekNo = self.mByWeekNo[:]
-        if self.mByMonth is not None:
-            other.mByMonth = self.mByMonth[:]
-        if self.mBySetPos is not None:
-            other.mBySetPos = self.mBySetPos[:]
-        other.mWeekstart = self.mWeekstart
-
-        other.mCached = self.mCached
-        if self.mCacheStart:
-            other.mCacheStart = self.mCacheStart.duplicate()
-        if self.mCacheUpto:
-            other.mCacheUpto = self.mCacheUpto.duplicate()
-        other.mFullyCached = self.mFullyCached
-        if self.mRecurrences:
-            other.mRecurrences = self.mRecurrences[:]
-
-        return other
-
-
-    def init_PyCalendarRecurrence(self):
-        self.mFreq = definitions.eRecurrence_YEARLY
-
-        self.mUseCount = False
-        self.mCount = 0
-
-        self.mUseUntil = False
-        self.mUntil = None
-
-        self.mInterval = 1
-        self.mBySeconds = None
-        self.mByMinutes = None
-        self.mByHours = None
-        self.mByDay = None
-        self.mByMonthDay = None
-        self.mByYearDay = None
-        self.mByWeekNo = None
-        self.mByMonth = None
-        self.mBySetPos = None
-        self.mWeekstart = definitions.eRecurrence_WEEKDAY_MO
-
-        self.mCached = False
-        self.mCacheStart = None
-        self.mCacheUpto = None
-        self.mFullyCached = False
-        self.mRecurrences = None
-
-
-    def __hash__(self):
-        return hash((
-            self.mFreq,
-            self.mUseCount,
-            self.mCount,
-            self.mUseUntil,
-            self.mUntil,
-            self.mInterval,
-            tuple(self.mBySeconds) if self.mBySeconds else None,
-            tuple(self.mByMinutes) if self.mByMinutes else None,
-            tuple(self.mByHours) if self.mByHours else None,
-            tuple(self.mByDay) if self.mByDay else None,
-            tuple(self.mByMonthDay) if self.mByMonthDay else None,
-            tuple(self.mByYearDay) if self.mByYearDay else None,
-            tuple(self.mByWeekNo) if self.mByWeekNo else None,
-            tuple(self.mByMonth) if self.mByMonth else None,
-            tuple(self.mBySetPos) if self.mBySetPos else None,
-            self.mWeekstart,
-        ))
-
-
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-
-    def __eq__(self, other):
-        if not isinstance(other, PyCalendarRecurrence):
-            return False
-        return self.equals(other)
-
-
-    def equals(self, comp):
-        return (self.mFreq == comp.mFreq) \
-                and (self.mUseCount == comp.mUseCount) and (self.mCount == comp.mCount) \
-                and (self.mUseUntil == comp.mUseUntil) and (self.mUntil == comp.mUntil) \
-                and (self.mInterval == comp.mInterval) \
-                and self.equalsNum(self.mBySeconds, comp.mBySeconds) \
-                and self.equalsNum(self.mByMinutes, comp.mByMinutes) \
-                and self.equalsNum(self.mByHours, comp.mByHours) \
-                and self.equalsDayNum(self.mByDay, comp.mByDay) \
-                and self.equalsNum(self.mByMonthDay, comp.mByMonthDay) \
-                and self.equalsNum(self.mByYearDay, comp.mByYearDay) \
-                and self.equalsNum(self.mByWeekNo, comp.mByWeekNo) \
-                and self.equalsNum(self.mByMonth, comp.mByMonth) \
-                and self.equalsNum(self.mBySetPos, comp.mBySetPos) \
-                and (self.mWeekstart == comp.mWeekstart)
-
-
-    def equalsNum(self, items1, items2):
-        # Check sizes first
-        if items1 is None:
-            items1 = []
-        if items2 is None:
-            items2 = []
-        if len(items1) != len(items2):
-            return False
-        elif len(items1) == 0:
-            return True
-
-        # Copy and sort each one for comparison
-        temp1 = items1[:]
-        temp2 = items2[:]
-        temp1.sort()
-        temp2.sort()
-
-        for i in range(0, len(temp1)):
-            if temp1[i] != temp2[i]:
-                return False
-        return True
-
-
-    def equalsDayNum(self, items1, items2):
-        # Check sizes first
-        if items1 is None:
-            items1 = []
-        if items2 is None:
-            items2 = []
-        if len(items1) != len(items2):
-            return False
-        elif len(items1) == 0:
-            return True
-
-        # Copy and sort each one for comparison
-        temp1 = items1[:]
-        temp2 = items2[:]
-        temp1.sort()
-        temp2.sort()
-
-        for i in range(0, len(temp1)):
-            if temp1[i] != temp2[i]:
-                return False
-        return True
-
-
-    def _setAndclearIfChanged(self, attr, value):
-        if getattr(self, attr) != value:
-            self.clear()
-            setattr(self, attr, value)
-
-
-    def getFreq(self):
-        return self.mFreq
-
-
-    def setFreq(self, freq):
-        self._setAndclearIfChanged("mFreq", freq)
-
-
-    def getUseUntil(self):
-        return self.mUseUntil
-
-
-    def setUseUntil(self, use_until):
-        self._setAndclearIfChanged("mUseUntil", use_until)
-
-
-    def getUntil(self):
-        return self.mUntil
-
-
-    def setUntil(self, until):
-        self._setAndclearIfChanged("mUntil", until)
-
-
-    def getUseCount(self):
-        return self.mUseCount
-
-
-    def setUseCount(self, use_count):
-        self._setAndclearIfChanged("mUseCount", use_count)
-
-
-    def getCount(self):
-        return self.mCount
-
-
-    def setCount(self, count):
-        self._setAndclearIfChanged("mCount", count)
-
-
-    def getInterval(self):
-        return self.mInterval
-
-
-    def setInterval(self, interval):
-        self._setAndclearIfChanged("mInterval", interval)
-
-
-    def getByMonth(self):
-        return self.mByMonth
-
-
-    def setByMonth(self, by):
-        self._setAndclearIfChanged("mByMonth", by[:])
-
-
-    def getByMonthDay(self):
-        return self.mByMonthDay
-
-
-    def setByMonthDay(self, by):
-        self._setAndclearIfChanged("mByMonthDay", by[:])
-
-
-    def getByYearDay(self):
-        return self.mByYearDay
-
-
-    def setByYearDay(self, by):
-        self._setAndclearIfChanged("mByYearDay", by[:])
-
-
-    def getByDay(self):
-        return self.mByDay
-
-
-    def setByDay(self, by):
-        self._setAndclearIfChanged("mByDay", by[:])
-
-
-    def getBySetPos(self):
-        return self.mBySetPos
-
-
-    def setBySetPos(self, by):
-        self._setAndclearIfChanged("mBySetPos", by[:])
-
-
-    def parse(self, data):
-        self.init_PyCalendarRecurrence()
-
-        # Tokenise using ''
-        tokens = data.split(";")
-        tokens.reverse()
-
-        if len(tokens) == 0:
-            raise ValueError("PyCalendarRecurrence: Invalid recurrence rule value")
-
-        while len(tokens) != 0:
-            # Get next token
-            token = tokens.pop()
-            try:
-                tname, tvalue = token.split("=")
-            except ValueError:
-                raise ValueError("PyCalendarRecurrence: Invalid token '%s'" % (token,))
-
-            # Determine token type
-            index = PyCalendarRecurrence.cRecurMap.get(tname, PyCalendarRecurrence.cUnknownIndex)
-            if index == PyCalendarRecurrence.cUnknownIndex:
-                raise ValueError("PyCalendarRecurrence: Invalid token '%s'" % (tname,))
-
-            # Parse remainder based on index
-            if index == definitions.eRecurrence_FREQ:
-                # Get the FREQ value
-                index = PyCalendarRecurrence.cFreqMap.get(tvalue, PyCalendarRecurrence.cUnknownIndex)
-                if index == PyCalendarRecurrence.cUnknownIndex:
-                    raise ValueError("PyCalendarRecurrence: Invalid FREQ value")
-                self.mFreq = index
-
-            elif index == definitions.eRecurrence_UNTIL:
-                if self.mUseCount:
-                    raise ValueError("PyCalendarRecurrence: Can't have both UNTIL and COUNT")
-                self.mUseUntil = True
-                if self.mUntil is None:
-                    self.mUntil = PyCalendarDateTime()
-                try:
-                    self.mUntil.parse(tvalue)
-                except ValueError:
-                    raise ValueError("PyCalendarRecurrence: Invalid UNTIL value")
-
-            elif index == definitions.eRecurrence_COUNT:
-                if self.mUseUntil:
-                    raise ValueError("PyCalendarRecurrence: Can't have both UNTIL and COUNT")
-                self.mUseCount = True
-                try:
-                    self.mCount = int(tvalue)
-                except ValueError:
-                    raise ValueError("PyCalendarRecurrence: Invalid COUNT value")
-
-                # Must not be less than one
-                if self.mCount < 1:
-                    raise ValueError("PyCalendarRecurrence: Invalid COUNT value")
-
-            elif index == definitions.eRecurrence_INTERVAL:
-                try:
-                    self.mInterval = int(tvalue)
-                except ValueError:
-                    raise ValueError("PyCalendarRecurrence: Invalid INTERVAL value")
-
-                # Must NOT be less than one
-                if self.mInterval < 1:
-                    raise ValueError("PyCalendarRecurrence: Invalid INTERVAL value")
-
-            elif index == definitions.eRecurrence_BYSECOND:
-                if self.mBySeconds is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYSECOND allowed")
-                self.mBySeconds = []
-                self.parseList(tvalue, self.mBySeconds, 0, 60, errmsg="PyCalendarRecurrence: Invalid BYSECOND value")
-
-            elif index == definitions.eRecurrence_BYMINUTE:
-                if self.mByMinutes is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYMINUTE allowed")
-                self.mByMinutes = []
-                self.parseList(tvalue, self.mByMinutes, 0, 59, errmsg="PyCalendarRecurrence: Invalid BYMINUTE value")
-
-            elif index == definitions.eRecurrence_BYHOUR:
-                if self.mByHours is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYHOUR allowed")
-                self.mByHours = []
-                self.parseList(tvalue, self.mByHours, 0, 23, errmsg="PyCalendarRecurrence: Invalid BYHOUR value")
-
-            elif index == definitions.eRecurrence_BYDAY:
-                if self.mByDay is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYDAY allowed")
-                self.mByDay = []
-                self.parseListDW(tvalue, self.mByDay, errmsg="PyCalendarRecurrence: Invalid BYDAY value")
-
-            elif index == definitions.eRecurrence_BYMONTHDAY:
-                if self.mByMonthDay is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYMONTHDAY allowed")
-                self.mByMonthDay = []
-                self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg="PyCalendarRecurrence: Invalid BYMONTHDAY value")
-
-            elif index == definitions.eRecurrence_BYYEARDAY:
-                if self.mByYearDay is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYYEARDAY allowed")
-                self.mByYearDay = []
-                self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg="PyCalendarRecurrence: Invalid BYYEARDAY value")
-
-            elif index == definitions.eRecurrence_BYWEEKNO:
-                if self.mByWeekNo is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYWEEKNO allowed")
-                self.mByWeekNo = []
-                self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg="PyCalendarRecurrence: Invalid BYWEEKNO value")
-
-            elif index == definitions.eRecurrence_BYMONTH:
-                if self.mByMonth is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYMONTH allowed")
-                self.mByMonth = []
-                self.parseList(tvalue, self.mByMonth, 1, 12, errmsg="PyCalendarRecurrence: Invalid BYMONTH value")
-
-            elif index == definitions.eRecurrence_BYSETPOS:
-                if self.mBySetPos is not None:
-                    raise ValueError("PyCalendarRecurrence: Only one BYSETPOS allowed")
-                self.mBySetPos = []
-                self.parseList(tvalue, self.mBySetPos, allowNegative=True, errmsg="PyCalendarRecurrence: Invalid BYSETPOS value")
-
-            elif index == definitions.eRecurrence_WKST:
-                index = PyCalendarRecurrence.cWeekdayMap.get(tvalue, PyCalendarRecurrence.cUnknownIndex)
-                if (index == PyCalendarRecurrence.cUnknownIndex):
-                    raise ValueError("PyCalendarRecurrence: Invalid WKST value")
-                self.mWeekstart = index
-
-
-    def parseList(self, txt, list, min=None, max=None, allowNegative=False, errmsg=""):
-
-        if "," in txt:
-            tokens = txt.split(",")
-        else:
-            tokens = (txt,)
-
-        for token in tokens:
-            value = int(token)
-            if not allowNegative and value < 0:
-                raise ValueError(errmsg)
-            avalue = abs(value)
-            if min is not None and avalue < min:
-                raise ValueError(errmsg)
-            if max is not None  and avalue > max:
-                raise ValueError(errmsg)
-            list.append(value)
-
-
-    def parseListDW(self, txt, list, errmsg=""):
-
-        if "," in txt:
-            tokens = txt.split(",")
-        else:
-            tokens = (txt,)
-
-        for token in tokens:
-            # Get number if present
-            num = 0
-            if (len(token) > 0) and token[0] in "+-1234567890":
-                offset = 0
-                while (offset < len(token)) and token[offset] in "+-1234567890":
-                    offset += 1
-
-                num = int(token[0:offset])
-                token = token[offset:]
-
-                anum = abs(num)
-                if anum < 1:
-                    raise ValueError(errmsg)
-                if anum > 53:
-                    raise ValueError(errmsg)
-
-            # Get day
-            index = PyCalendarRecurrence.cWeekdayMap.get(token, PyCalendarRecurrence.cUnknownIndex)
-            if (index == PyCalendarRecurrence.cUnknownIndex):
-                raise ValueError(errmsg)
-            wday = index
-
-            list.append((num, wday))
-
-
-    def generate(self, os):
-        try:
-            os.write(definitions.cICalValue_RECUR_FREQ)
-            os.write("=")
-
-            if self.mFreq == definitions.eRecurrence_SECONDLY:
-                os.write(definitions.cICalValue_RECUR_SECONDLY)
-
-            elif self.mFreq == definitions.eRecurrence_MINUTELY:
-                os.write(definitions.cICalValue_RECUR_MINUTELY)
-
-            elif self.mFreq == definitions.eRecurrence_HOURLY:
-                os.write(definitions.cICalValue_RECUR_HOURLY)
-
-            elif self.mFreq == definitions.eRecurrence_DAILY:
-                os.write(definitions.cICalValue_RECUR_DAILY)
-
-            elif self.mFreq == definitions.eRecurrence_WEEKLY:
-                os.write(definitions.cICalValue_RECUR_WEEKLY)
-
-            elif self.mFreq == definitions.eRecurrence_MONTHLY:
-                os.write(definitions.cICalValue_RECUR_MONTHLY)
-
-            elif self.mFreq == definitions.eRecurrence_YEARLY:
-                os.write(definitions.cICalValue_RECUR_YEARLY)
-
-            if self.mUseCount:
-                os.write(";")
-                os.write(definitions.cICalValue_RECUR_COUNT)
-                os.write("=")
-                os.write(str(self.mCount))
-            elif self.mUseUntil:
-                os.write(";")
-                os.write(definitions.cICalValue_RECUR_UNTIL)
-                os.write("=")
-                self.mUntil.generate(os)
-
-            if self.mInterval > 1:
-                os.write(";")
-                os.write(definitions.cICalValue_RECUR_INTERVAL)
-                os.write("=")
-                os.write(str(self.mInterval))
-
-            self.generateList(os, definitions.cICalValue_RECUR_BYSECOND, self.mBySeconds)
-            self.generateList(os, definitions.cICalValue_RECUR_BYMINUTE, self.mByMinutes)
-            self.generateList(os, definitions.cICalValue_RECUR_BYHOUR, self.mByHours)
-
-            if (self.mByDay is not None) and (len(self.mByDay) != 0):
-                os.write(";")
-                os.write(definitions.cICalValue_RECUR_BYDAY)
-                os.write("=")
-                comma = False
-                for iter in self.mByDay:
-                    if comma:
-                        os.write(",")
-                    comma = True
-
-                    if iter[0] != 0:
-                        os.write(str(iter[0]))
-
-                    if iter[1] == definitions.eRecurrence_WEEKDAY_SU:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_MO:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_TU:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_WE:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_TH:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_FR:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
-
-                    elif iter[1] == definitions.eRecurrence_WEEKDAY_SA:
-                        os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
-
-            self.generateList(os, definitions.cICalValue_RECUR_BYMONTHDAY, self.mByMonthDay)
-            self.generateList(os, definitions.cICalValue_RECUR_BYYEARDAY, self.mByYearDay)
-            self.generateList(os, definitions.cICalValue_RECUR_BYWEEKNO, self.mByWeekNo)
-            self.generateList(os, definitions.cICalValue_RECUR_BYMONTH, self.mByMonth)
-            self.generateList(os, definitions.cICalValue_RECUR_BYSETPOS, self.mBySetPos)
-
-            # MO is the default so we do not need it
-            if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
-                os.write(";")
-                os.write(definitions.cICalValue_RECUR_WKST)
-                os.write("=")
-
-                if self.mWeekstart == definitions.eRecurrence_WEEKDAY_SU:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_MO:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TU:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_WE:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TH:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_FR:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
-
-                elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_SA:
-                    os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
-
-        except:
-            pass
-
-
-    def generateList(self, os, title, items):
-
-        if (items is not None) and (len(items) != 0):
-            os.write(";")
-            os.write(title)
-            os.write("=")
-            comma = False
-            for e in items:
-                if comma:
-                    os.write(",")
-                comma = True
-                os.write(str(e))
-
-
-    def writeXML(self, node, namespace):
-
-        recur = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.value_recur))
-
-        freq = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_freq))
-        freq.text = self.cFreqToXMLMap[self.mFreq]
-
-        if self.mUseCount:
-            count = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_count))
-            count.text = str(self.mCount)
-        elif self.mUseUntil:
-            until = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_until))
-            self.mUntil.writeXML(until, namespace)
-
-        if self.mInterval > 1:
-            interval = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_interval))
-            interval.text = str(self.mInterval)
-
-        self.writeXMLList(recur, namespace, xmldefs.recur_bysecond, self.mBySeconds)
-        self.writeXMLList(recur, namespace, xmldefs.recur_byminute, self.mByMinutes)
-        self.writeXMLList(recur, namespace, xmldefs.recur_byhour, self.mByHours)
-
-        if self.mByDay is not None and len(self.mByDay) != 0:
-            for iter in self.mByDay:
-                byday = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_byday))
-                data = ""
-                if iter[0] != 0:
-                    data = str(iter[0])
-                data += self.cWeekdayRecurMap.get(iter[1], "")
-                byday.text = data
-
-        self.writeXMLList(recur, namespace, xmldefs.recur_bymonthday, self.mByMonthDay)
-        self.writeXMLList(recur, namespace, xmldefs.recur_byyearday, self.mByYearDay)
-        self.writeXMLList(recur, namespace, xmldefs.recur_byweekno, self.mByWeekNo)
-        self.writeXMLList(recur, namespace, xmldefs.recur_bymonth, self.mByMonth)
-        self.writeXMLList(recur, namespace, xmldefs.recur_bysetpos, self.mBySetPos)
-
-        # MO is the default so we do not need it
-        if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
-            wkst = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_wkst))
-            wkst.text = self.cWeekdayRecurMap.get(self.mWeekstart, definitions.cICalValue_RECUR_WEEKDAY_MO)
-
-
-    def writeXMLList(self, node, namespace, name, items):
-        if items is not None and len(items) != 0:
-            for item in items:
-                child = XML.SubElement(node, xmldefs.makeTag(namespace, name))
-                child.text = str(item)
-
-
-    def hasBy(self):
-        return (self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
-                or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
-                or (self.mByHours is not None) and (len(self.mByHours) != 0) \
-                or (self.mByDay is not None) and (len(self.mByDay) != 0) \
-                or (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0) \
-                or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
-                or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0) \
-                or (self.mByMonth is not None) and (len(self.mByMonth) != 0) \
-                or (self.mBySetPos is not None) and (len(self.mBySetPos) != 0)
-
-
-    def isSimpleRule(self):
-        # One that has no BYxxx rules
-        return not self.hasBy()
-
-
-    def isAdvancedRule(self):
-        # One that has BYMONTH,
-        # BYMONTHDAY (with no negative value),
-        # BYDAY (with multiple unnumbered, or numbered with all the same number
-        # (1..4, -2, -1)
-        # BYSETPOS with +1, or -1 only
-        # no others
-
-        # First checks the ones we do not handle at all
-        if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
-                or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
-                or (self.mByHours is not None) and (len(self.mByHours) != 0) \
-                or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
-                or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0)):
-            return False
-
-        # Check BYMONTHDAY numbers (we can handle -7...-1, 1..31)
-        if self.mByMonthDay is not None:
-            for iter in self.mByMonthDay:
-                if (iter < -7) or (iter > 31) or (iter == 0):
-                    return False
-
-        # Check BYDAY numbers
-        if self.mByDay is not None:
-            number = 0
-            first = True
-            for iter in self.mByDay:
-
-                # Get the first number
-                if (first):
-                    number = iter[0]
-                    first = False
-
-                    # Check number range
-                    if (number > 4) or (number < -2):
-                        return False
-
-                # If current differs from last, then we have an error
-                elif number != iter[0]:
-                    return False
-
-        # Check BYSETPOS numbers
-        if self.mBySetPos is not None:
-            if len(self.mBySetPos) > 1:
-                return False
-            if (len(self.mBySetPos) == 1) and (self.mBySetPos[0] != -1) and (self.mBySetPos[0] != 1):
-                return False
-
-        # If we get here it must be OK
-        return True
-
-
-    def getUIDescription(self):
-        try:
-            # For now just use iCal item
-            sout = StringIO()
-            self.generate(sout)
-            result = sout.getvalue()
-        except:
-            result = ""
-
-        return result
-
-
-    def expand(self, start, range, items, float_offset=0):
-
-        # Must have recurrence list at this point
-        if self.mRecurrences is None:
-            self.mRecurrences = []
-
-        # Wipe cache if start is different
-        if self.mCached and (start != self.mCacheStart):
-            self.mCached = False
-            self.mFullyCached = False
-            self.mRecurrences = []
-
-        # Is the current cache complete or does it extend past the requested
-        # range end
-        if not self.mCached or not self.mFullyCached \
-                and (self.mCacheUpto is None or self.mCacheUpto < range.getEnd()):
-            cache_range = range.duplicate()
-
-            # If partially cached just cache from previous cache end up to new
-            # end
-            if self.mCached:
-                cache_range = PyCalendarPeriod(self.mCacheUpto, range.getEnd())
-
-            # Simple expansion is one where there is no BYXXX rule part
-            if not self.hasBy():
-                self.mFullyCached = self.simpleExpand(start, cache_range, self.mRecurrences, float_offset)
-            else:
-                self.mFullyCached = self.complexExpand(start, cache_range, self.mRecurrences, float_offset)
-
-            # Set cache values
-            self.mCached = True
-            self.mCacheStart = start
-            self.mCacheUpto = range.getEnd()
-
-        # Just return the cached items in the requested range
-        limited = not self.mFullyCached
-        for iter in self.mRecurrences:
-            if range.isDateWithinPeriod(iter):
-                items.append(iter)
-            else:
-                limited = True
-        return limited
-
-
-    def simpleExpand(self, start, range, items, float_offset):
-        start_iter = start.duplicate()
-        ctr = 0
-
-        if self.mUseUntil:
-            float_until = self.mUntil.duplicate()
-            if start.floating():
-                float_until.setTimezoneID(0)
-                float_until.offsetSeconds(float_offset)
-
-        while True:
-            # Exit if after period we want
-            if range.isDateAfterPeriod(start_iter):
-                return False
-
-            # Add current one to list
-            items.append(start_iter.duplicate())
-
-            # Get next item
-            start_iter.recur(self.mFreq, self.mInterval)
-
-            # Check limits
-            if self.mUseCount:
-                # Bump counter and exit if over
-                ctr += 1
-                if ctr >= self.mCount:
-                    return True
-            elif self.mUseUntil:
-                # Exit if next item is after until (its OK if its the same as
-                # UNTIL as UNTIL is inclusive)
-                if start_iter > float_until:
-                    return True
-
-
-    def complexExpand(self, start, range, items, float_offset):
-        start_iter = start.duplicate()
-        ctr = 0
-
-        if self.mUseUntil:
-            float_until = self.mUntil.duplicate()
-            if start.floating():
-                float_until.setTimezoneID(None)
-                float_until.offsetSeconds(float_offset)
-
-        # Always add the initial instance DTSTART
-        if self.mUseCount:
-            # Bump counter and exit if over
-            ctr += 1
-            if ctr >= self.mCount:
-                return True
-
-        # Need to re-initialise start based on BYxxx rules
-        while True:
-            # Behaviour is based on frequency
-            set_items = []
-
-            if self.mFreq == definitions.eRecurrence_SECONDLY:
-                self.generateSecondlySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_MINUTELY:
-                self.generateMinutelySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_HOURLY:
-                self.generateHourlySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_DAILY:
-                self.generateDailySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_WEEKLY:
-                self.generateWeeklySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_MONTHLY:
-                self.generateMonthlySet(start_iter, set_items)
-
-            elif self.mFreq == definitions.eRecurrence_YEARLY:
-                self.generateYearlySet(start_iter, set_items)
-
-            # Always sort the set as BYxxx rules may not be sorted
-            #set_items.sort(cmp=PyCalendarDateTime.sort)
-            set_items.sort(key=lambda x: x.getPosixTime())
-
-            # Process each one in the generated set
-            for iter in set_items:
-
-                # Ignore if it is before the actual start - we need this
-                # because the expansion
-                # can go back in time from the real start, but we must exclude
-                # those when counting
-                # even if they are not within the requested range
-                if iter < start:
-                    continue
-
-                # Exit if after period we want
-                if range.isDateAfterPeriod(iter):
-                    return False
-
-                # Exit if beyond the UNTIL limit
-                if self.mUseUntil:
-                    # Exit if next item is after until (its OK if its the same
-                    # as UNTIL as UNTIL is inclusive)
-                    if iter > float_until:
-                        return True
-
-                # Special for start instance
-                if (ctr == 1) and (start == iter):
-                    continue
-
-                # Add current one to list
-                items.append(iter)
-
-                # Check limits
-                if self.mUseCount:
-                    # Bump counter and exit if over
-                    ctr += 1
-                    if ctr >= self.mCount:
-                        return True
-
-            # Exit if after period we want
-            if range.isDateAfterPeriod(start_iter):
-                return False
-
-            # Get next item
-            start_iter.recur(self.mFreq, self.mInterval)
-
-
-    def clear(self):
-        self.mCached = False
-        self.mFullyCached = False
-        if self.mRecurrences is not None:
-            self.mRecurrences = []
-
-
-    # IMPORTANT ExcludeFutureRecurrence assumes mCacheStart is setup with the
-    # owning VEVENT's DTSTART
-    # Currently this method is only called when a recurrence is being removed
-    # so
-    # the recurrence data should be cached
-
-    # Exclude dates on or after the chosen one
-    def excludeFutureRecurrence(self, exclude):
-        # Expand the rule up to the exclude date
-        items = []
-        period = PyCalendarPeriod()
-        period.init(self.mCacheStart, exclude)
-        self.expand(self.mCacheStart, period, items)
-
-        # Adjust UNTIL or add one if no COUNT
-        if self.getUseUntil() or not self.getUseCount():
-            # The last one is just less than the exclude date
-            if len(items) != 0:
-                # Now use the data as the UNTIL
-                self.mUseUntil = True
-                self.mUntil = items[-1]
-
-        # Adjust COUNT
-        elif self.getUseCount():
-            # The last one is just less than the exclude date
-            self.mUseCount = True
-            self.mCount = len(items)
-
-        # Now clear out the cached set after making changes
-        self.clear()
-
-
-    def generateYearlySet(self, start, items):
-        # All possible BYxxx are valid, though some combinations are not
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            items[:] = self.byMonthExpand(items)
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoExpand(items)
-
-        if (self.mByYearDay is not None) and (len(self.mByYearDay) != 0):
-            items[:] = self.byYearDayExpand(items)
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayExpand(items)
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            # BYDAY is complicated:
-            # if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
-            # contracts the recurrence set
-            # else it expands it, but the expansion depends on the frequency
-            # and other BYxxx periodicities
-
-            if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
-                    or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
-                items[:] = self.byDayLimit(items)
-            elif (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-                items[:] = self.byDayExpandWeekly(items)
-            elif (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-                items[:] = self.byDayExpandMonthly(items)
-            else:
-                items[:] = self.byDayExpandYearly(items)
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourExpand(items)
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteExpand(items)
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondExpand(items)
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateMonthlySet(self, start, items):
-        # Cannot have BYYEARDAY and BYWEEKNO
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYWEEKNO
-
-        # No BYYEARDAY
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayExpand(items)
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            # BYDAY is complicated:
-            # if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
-            # contracts the recurrence set
-            # else it expands it, but the expansion depends on the frequency
-            # and other BYxxx periodicities
-
-            if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
-                    or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
-                items[:] = self.byDayLimit(items)
-            else:
-                items[:] = self.byDayExpandMonthly(items)
-
-        if ((self.mByHours is not None) and (len(self.mByHours) != 0)):
-            items[:] = self.byHourExpand(items)
-
-        if ((self.mByMinutes is not None) and (len(self.mByMinutes) != 0)):
-            items[:] = self.byMinuteExpand(items)
-
-        if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0)):
-            items[:] = self.bySecondExpand(items)
-
-        if ((self.mBySetPos is not None) and (len(self.mBySetPos) != 0)):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateWeeklySet(self, start, items):
-        # Cannot have BYYEARDAY and BYMONTHDAY
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYYEARDAY
-
-        # No BYMONTHDAY
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            items[:] = self.byDayExpandWeekly(items)
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourExpand(items)
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteExpand(items)
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondExpand(items)
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateDailySet(self, start, items):
-        # Cannot have BYYEARDAY
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYYEARDAY
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            items[:] = self.byDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourExpand(items)
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteExpand(items)
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondExpand(items)
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateHourlySet(self, start, items):
-        # Cannot have BYYEARDAY
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYYEARDAY
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            items[:] = self.byDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteExpand(items)
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondExpand(items)
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateMinutelySet(self, start, items):
-        # Cannot have BYYEARDAY
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYYEARDAY
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            items[:] = self.byDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondExpand(items)
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def generateSecondlySet(self, start, items):
-        # Cannot have BYYEARDAY
-
-        # Start with initial date-time
-        items.append(start.duplicate())
-
-        if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
-            # BYMONTH limits the range of possible values
-            items[:] = self.byMonthLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
-            items[:] = self.byWeekNoLimit(items)
-            if (len(items) == 0):
-                return
-
-        # No BYYEARDAY
-
-        if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
-            items[:] = self.byMonthDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByDay is not None) and (len(self.mByDay) != 0):
-            items[:] = self.byDayLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByHours is not None) and (len(self.mByHours) != 0):
-            items[:] = self.byHourLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
-            items[:] = self.byMinuteLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
-            items[:] = self.bySecondLimit(items)
-            if (len(items) == 0):
-                return
-
-        if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
-            items[:] = self.bySetPosLimit(items)
-
-
-    def byMonthExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMONTH and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByMonth:
-                temp = iter1.duplicate()
-                temp.setMonth(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byWeekNoExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYWEEKNO and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByWeekNo:
-                temp = iter1.duplicate()
-                temp.setWeekNo(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byYearDayExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYYEARDAY and generating a new date-time for it
-            # and insert into output
-            for iter2 in self.mByYearDay:
-                temp = iter1.duplicate()
-                temp.setYearDay(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byMonthDayExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMONTHDAY and generating a new date-time for it
-            # and insert into output
-            for iter2 in self.mByMonthDay:
-                temp = iter1.duplicate()
-                temp.setMonthDay(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byDayExpandYearly(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYDAY and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByDay:
-                # Numeric value means specific instance
-                if iter2[0] != 0:
-                    temp = iter1.duplicate()
-                    temp.setDayOfWeekInYear(iter2[0], iter2[1])
-                    output.append(temp)
-                else:
-                    # Every matching day in the year
-                    for  i in range(1, 54):
-                        temp = iter1.duplicate()
-                        temp.setDayOfWeekInYear(i, iter2[1])
-                        if temp.getYear() == (iter1).getYear():
-                            output.append(temp)
-
-        return output
-
-
-    def byDayExpandMonthly(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYDAY and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByDay:
-                # Numeric value means specific instance
-                if iter2[0] != 0:
-                    temp = iter1.duplicate()
-                    temp.setDayOfWeekInMonth(iter2[0], iter2[1])
-                    output.append(temp)
-                else:
-                    # Every matching day in the month
-                    for i in range(1, 7):
-                        temp = iter1.duplicate()
-                        temp.setDayOfWeekInMonth(i, iter2[1])
-                        if temp.getMonth() == iter1.getMonth():
-                            output.append(temp)
-
-        return output
-
-
-    def byDayExpandWeekly(self, dates):
-        # Must take into account the WKST value
-
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYDAY and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByDay:
-                # Numeric values are meaningless so ignore them
-                if iter2[0] == 0:
-                    temp = iter1.duplicate()
-
-                    # Determine amount of offset to apply to temp to shift it
-                    # to the start of the week (backwards)
-                    week_start_offset = self.mWeekstart - temp.getDayOfWeek()
-                    if week_start_offset > 0:
-                        week_start_offset -= 7
-
-                    # Determine amount of offset from the start of the week to
-                    # the day we want (forwards)
-                    day_in_week_offset = iter2[1] - self.mWeekstart
-                    if day_in_week_offset < 0:
-                        day_in_week_offset += 7
-
-                    # Apply offsets
-                    temp.offsetDay(week_start_offset + day_in_week_offset)
-                    output.append(temp)
-
-        return output
-
-
-    def byHourExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYHOUR and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByHours:
-                temp = iter1.duplicate()
-                temp.setHours(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byMinuteExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMINUTE and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mByMinutes:
-                temp = iter1.duplicate()
-                temp.setMinutes(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def bySecondExpand(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYSECOND and generating a new date-time for it and
-            # insert into output
-            for iter2 in self.mBySeconds:
-                temp = iter1.duplicate()
-                temp.setSeconds(iter2)
-                output.append(temp)
-
-        return output
-
-
-    def byMonthLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMONTH and indicate keep if input month matches
-            keep = False
-            for iter2 in self.mByMonth:
-                keep = (iter1.getMonth() == iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def byWeekNoLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYWEEKNO and indicate keep if input month matches
-            keep = False
-            for iter2 in self.mByWeekNo:
-                keep = iter1.isWeekNo(iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def byMonthDayLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMONTHDAY and indicate keep if input month
-            # matches
-            keep = False
-            for iter2 in self.mByMonthDay:
-                keep = iter1.isMonthDay(iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def byDayLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYDAY and indicate keep if input month matches
-            keep = False
-            for iter2 in self.mByDay:
-                keep = iter1.isDayOfWeekInMonth(iter2[0], iter2[1])
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def byHourLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYHOUR and indicate keep if input hour matches
-            keep = False
-            for iter2 in self.mByHours:
-                keep = (iter1.getHours() == iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def byMinuteLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYMINUTE and indicate keep if input minute matches
-            keep = False
-            for iter2 in self.mByMinutes:
-                keep = (iter1.getMinutes() == iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def bySecondLimit(self, dates):
-        # Loop over all input items
-        output = []
-        for iter1 in dates:
-            # Loop over each BYSECOND and indicate keep if input second matches
-            keep = False
-            for iter2 in self.mBySeconds:
-                keep = (iter1.getSeconds() == iter2)
-                if keep:
-                    break
-
-            if keep:
-                output.append(iter1)
-
-        return output
-
-
-    def bySetPosLimit(self, dates):
-        # The input dates MUST be sorted in order for this to work properly
-        #dates.sort(cmp=PyCalendarDateTime.sort)
-        dates.sort(key=lambda x: x.getPosixTime())
-
-        # Loop over each BYSETPOS and extract the relevant component from the
-        # input array and add to the output
-        output = []
-        input_size = len(dates)
-        for iter in self.mBySetPos:
-            if iter > 0:
-                # Positive values are offset from the start
-                if iter <= input_size:
-                    output.append(dates[iter - 1])
-            elif iter < 0:
-                # Negative values are offset from the end
-                if -iter <= input_size:
-                    output.append(dates[input_size + iter])
-
-        return output

Deleted: PyCalendar/branches/json-2/src/pycalendar/recurrenceset.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/recurrenceset.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/recurrenceset.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,312 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.utils import set_difference
-
-class PyCalendarRecurrenceSet(object):
-
-    def __init__(self):
-        self.mRrules = []
-        self.mExrules = []
-        self.mRdates = []
-        self.mExdates = []
-        self.mRperiods = []
-        self.mExperiods = []
-
-
-    def duplicate(self):
-        other = PyCalendarRecurrenceSet()
-        other.mRrules = [i.duplicate() for i in self.mRrules]
-        other.mExrules = [i.duplicate() for i in self.mExrules]
-        other.mRdates = [i.duplicate() for i in self.mRdates]
-        other.mExdates = [i.duplicate() for i in self.mExdates]
-        other.mRperiods = [i.duplicate() for i in self.mRperiods]
-        other.mExperiods = [i.duplicate() for i in self.mExperiods]
-        return other
-
-
-    def hasRecurrence(self):
-        return ((len(self.mRrules) != 0) or (len(self.mRdates) != 0) or (len(self.mRperiods) != 0)
-                    or (len(self.mExrules) != 0) or (len(self.mExdates) != 0)
-                    or (len(self.mExperiods) != 0))
-
-
-    def equals(self, comp):
-        # Look at RRULEs
-        if not self.equalsRules(self.mRrules, comp.self.mRrules):
-            return False
-
-        # Look at EXRULEs
-        if not self.equalsRules(self.mExrules, comp.self.mExrules):
-            return False
-
-        # Look at RDATEs
-        if not self.equalsDates(self.mRdates, comp.self.mRdates):
-            return False
-        if not self.equalsPeriods(self.mRperiods, comp.self.mRperiods):
-            return False
-
-        # Look at EXDATEs
-        if not self.equalsDates(self.mExdates, comp.self.mExdates):
-            return False
-        if not self.equalsPeriods(self.mExperiods, comp.self.mExperiods):
-            return False
-
-        # If we get here they match
-        return True
-
-
-    def equalsRules(self, rules1, rules2):
-        # Check sizes first
-        if len(rules1) != len(rules2):
-            return False
-        elif len(rules1) == 0:
-            return True
-
-        # Do sledge hammer O(n^2) approach as its not easy to sort these things
-        # for a smarter test.
-        # In most cases there will only be one rule anyway, so this should not
-        # be too painful.
-
-        temp2 = rules2[:]
-
-        for r1 in rules1:
-            found = False
-            for r2 in temp2:
-                if r1.equals(r2):
-                    # Remove the one found so it is not tested again
-                    temp2.remove(r2)
-                    found = True
-                    break
-
-            if not found:
-                return False
-
-        return True
-
-
-    def equalsDates(self, dates1, dates2):
-        # Check sizes first
-        if len(dates1) != len(dates2):
-            return False
-        elif len(dates1) == 0:
-            return True
-
-        # Copy each and sort for comparison
-        dt1 = dates1[:]
-        dt2 = dates2[:]
-
-        dt1.sort(key=lambda x: x.getPosixTime())
-        dt2.sort(key=lambda x: x.getPosixTime())
-
-        return dt1.equal(dt2)
-
-
-    def equalsPeriods(self, periods1, periods2):
-        # Check sizes first
-        if len(periods1) != len(periods2):
-            return False
-        elif len(periods1) == 0:
-            return True
-
-        # Copy each and sort for comparison
-        p1 = periods1[:]
-        p2 = periods2[:]
-
-        p1.sort()
-        p2.sort()
-
-        return p1.equal(p2)
-
-
-    def addRule(self, rule):
-        self.mRrules.append(rule)
-
-
-    def subtractRule(self, rule):
-        self.mExrules.append(rule)
-
-
-    def addDT(self, dt):
-        self.mRdates.append(dt)
-
-
-    def subtractDT(self, dt):
-        self.mExdates.append(dt)
-
-
-    def addPeriod(self, p):
-        self.mRperiods.append(p)
-
-
-    def subtractPeriod(self, p):
-        self.mExperiods.append(p)
-
-
-    def getRules(self):
-        return self.mRrules
-
-
-    def getExrules(self):
-        return self.mExrules
-
-
-    def getDates(self):
-        return self.mRdates
-
-
-    def getExdates(self):
-        return self.mExdates
-
-
-    def getPeriods(self):
-        return self.mRperiods
-
-
-    def getExperiods(self):
-        return self.mExperiods
-
-
-    def expand(self, start, range, items, float_offset=0):
-        # Need to return whether the limit was applied or not
-        limited = False
-
-        # Now create list of items to include
-        include = []
-
-        # Always include the initial DTSTART if within the range
-        if range.isDateWithinPeriod(start):
-            include.append(start)
-        else:
-            limited = True
-
-        # RRULES
-        for iter in self.mRrules:
-            if iter.expand(start, range, include, float_offset=float_offset):
-                limited = True
-
-        # RDATES
-        for iter in self.mRdates:
-            if range.isDateWithinPeriod(iter):
-                include.append(iter)
-            else:
-                limited = True
-        for iter in self.mRperiods:
-            if range.isPeriodOverlap(iter):
-                include.append(iter.getStart())
-            else:
-                limited = True
-
-        # Make sure the list is unique
-        include = [x for x in set(include)]
-        include.sort(key=lambda x: x.getPosixTime())
-
-        # Now create list of items to exclude
-        exclude = []
-
-        # EXRULES
-        for iter in self.mExrules:
-            iter.expand(start, range, exclude, float_offset=float_offset)
-
-        # EXDATES
-        for iter in self.mExdates:
-            if range.isDateWithinPeriod(iter):
-                exclude.append(iter)
-        for iter in self.mExperiods:
-            if range.isPeriodOverlap(iter):
-                exclude.append(iter.getStart())
-
-        # Make sure the list is unique
-        exclude = [x for x in set(exclude)]
-        exclude.sort(key=lambda x: x.getPosixTime())
-
-        # Add difference between to the two sets (include - exclude) to the
-        # results
-        items.extend(set_difference(include, exclude))
-        return limited
-
-
-    def changed(self):
-        # RRULES
-        for iter in self.mRrules:
-            iter.clear()
-
-        # EXRULES
-        for iter in self.mExrules:
-            iter.clear()
-
-
-    def excludeFutureRecurrence(self, exclude):
-        # Adjust RRULES to end before start
-        for iter in self.mRrules:
-            iter.excludeFutureRecurrence(exclude)
-
-        # Remove RDATES on or after start
-        self.mRdates.removeOnOrAfter(exclude)
-        for iter in self.mRperiods:
-            if iter > exclude:
-                self.mRperiods.remove(iter)
-
-
-    # UI operations
-    def isSimpleUI(self):
-        # Right now the Event dialog only handles a single RRULE (but we allow
-        # any number of EXDATES as deleted
-        # instances will appear as EXDATES)
-        if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
-                or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
-            return False
-
-        # Also, check the rule iteself
-        elif len(self.mRrules) == 1:
-            return self.mRrules.firstElement().isSimpleRule()
-        else:
-            return True
-
-
-    def isAdvancedUI(self):
-        # Right now the Event dialog only handles a single RRULE
-        if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
-                or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
-            return False
-
-        # Also, check the rule iteself
-        elif len(self.mRrules) == 1:
-            return self.mRrules.firstElement().isAdvancedRule()
-        else:
-            return True
-
-
-    def getUIRecurrence(self):
-        if len(self.mRrules) == 1:
-            return self.mRrules[0]
-        else:
-            return None
-
-
-    def getUIDescription(self):
-        # Check for anything
-        if not self.hasRecurrence():
-            return "No Recurrence"
-
-        # Look for a single RRULE and return its descriptor
-        if ((len(self.mRrules) == 1) and (len(self.mExrules) == 0) and (len(self.mRdates) == 0)
-                and (len(self.mExdates) == 0) and (len(self.mRperiods) == 0)
-                and (len(self.mExperiods) == 0)):
-            return self.mRrules.firstElement().getUIDescription()
-
-        # Indicate some form of complex recurrence
-        return "Multiple recurrence rules, dates or exclusions"

Deleted: PyCalendar/branches/json-2/src/pycalendar/recurrencevalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/recurrencevalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/recurrencevalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,54 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import xmldefs
-from pycalendar.recurrence import PyCalendarRecurrence
-from pycalendar.value import PyCalendarValue
-
-class PyCalendarRecurrenceValue(PyCalendarValue):
-
-    def __init__(self, value=None):
-        self.mValue = value if value is not None else PyCalendarRecurrence()
-
-
-    def duplicate(self):
-        return PyCalendarRecurrenceValue(self.mValue.duplicate())
-
-
-    def getType(self):
-        return PyCalendarValue.VALUETYPE_RECUR
-
-
-    def parse(self, data):
-        self.mValue.parse(data)
-
-
-    def generate(self, os):
-        self.mValue.generate(os)
-
-
-    def writeXML(self, node, namespace):
-        self.mValue.writeXML(node, namespace)
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_RECUR, PyCalendarRecurrenceValue, xmldefs.value_recur)

Deleted: PyCalendar/branches/json-2/src/pycalendar/requeststatusvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/requeststatusvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/requeststatusvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,102 +0,0 @@
-##
-#    Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-# iCalendar REQUEST-STATUS value
-
-from pycalendar import utils, xmldefs
-from pycalendar.parser import ParserContext
-from pycalendar.value import PyCalendarValue
-import xml.etree.cElementTree as XML
-
-class PyCalendarRequestStatusValue(PyCalendarValue):
-    """
-    The value is a list of strings (either 2 or 3 items)
-    """
-
-    def __init__(self, value=None):
-        self.mValue = value if value is not None else ["2.0", "Success"]
-
-
-    def __hash__(self):
-        return hash(tuple(self.mValue))
-
-
-    def duplicate(self):
-        return PyCalendarRequestStatusValue(self.mValue[:])
-
-
-    def getType(self):
-        return PyCalendarValue.VALUETYPE_REQUEST_STATUS
-
-
-    def parse(self, data):
-
-        result = utils.parseTextList(data, always_list=True)
-        if len(result) == 1:
-            if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
-                if ";" in result[0]:
-                    code, desc = result[0].split(";", 1)
-                else:
-                    code = result[0]
-                    desc = ""
-                rest = None
-            else:
-                raise ValueError
-        elif len(result) == 2:
-            code, desc = result
-            rest = None
-        elif len(result) == 3:
-            code, desc, rest = result
-        else:
-            if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
-                code, desc, rest = result[:3]
-            else:
-                raise ValueError
-
-        if "\\" in code and ParserContext.INVALID_REQUEST_STATUS_VALUE in (ParserContext.PARSER_IGNORE, ParserContext.PARSER_FIX):
-            code = code.replace("\\", "")
-        elif ParserContext.INVALID_REQUEST_STATUS_VALUE == ParserContext.PARSER_RAISE:
-            raise ValueError
-
-        # Decoding required
-        self.mValue = [code, desc, rest, ] if rest else [code, desc, ]
-
-
-    # os - StringIO object
-    def generate(self, os):
-        utils.generateTextList(os, self.mValue if len(self.mValue) < 3 or self.mValue[2] else self.mValue[:2])
-
-
-    def writeXML(self, node, namespace):
-        code = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_code))
-        code.text = self.mValue[0]
-
-        description = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_description))
-        description.text = self.mValue[1]
-
-        if len(self.mValue) == 3 and self.mValue[2]:
-            data = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_data))
-            data.text = self.mValue[1]
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def setValue(self, value):
-        self.mValue = value
-
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_REQUEST_STATUS, PyCalendarRequestStatusValue, None)

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_adr.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_adr.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_adr.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,50 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.adr import Adr
-import unittest
-
-class TestAdrValue(unittest.TestCase):
-
-    def testInit(self):
-        data = (
-            (
-                ("pobox", "extended", "street", "locality", "region", "postalcode", "country"),
-                "pobox;extended;street;locality;region;postalcode;country",
-            ),
-            (
-                (("pobox",), ("extended",), ("street1", "street2",), "locality", "region", (), "country"),
-                "pobox;extended;street1,street2;locality;region;;country",
-            ),
-        )
-
-        for args, result in data:
-            a = Adr(*args)
-
-            self.assertEqual(
-                a.getValue(),
-                args,
-            )
-
-            self.assertEqual(
-                a.getText(),
-                result,
-            )
-
-            self.assertEqual(
-                a.duplicate().getText(),
-                result,
-            )

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_adrvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_adrvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_adrvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,62 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.adrvalue import AdrValue
-from pycalendar.vcard.property import Property
-import unittest
-
-class TestAdrValue(unittest.TestCase):
-
-    def testParseValue(self):
-
-        items = (
-            ("", ";;;;;;"),
-            (";", ";;;;;;"),
-            (";;;;;;", ";;;;;;"),
-            (";;123 Main Street;Any Town;CA;91921-1234", ";;123 Main Street;Any Town;CA;91921-1234;"),
-            (";;;;;;USA", ";;;;;;USA"),
-            ("POB1", "POB1;;;;;;"),
-            (";EXT", ";EXT;;;;;"),
-            (";;123 Main Street,The Cards;Any Town;CA;91921-1234", ";;123 Main Street,The Cards;Any Town;CA;91921-1234;"),
-            (";;123 Main\, Street,The Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The Cards;Any Town;CA;91921-1234;"),
-            (";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234;"),
-        )
-
-        for item, result in items:
-            req = AdrValue()
-            req.parse(item)
-            test = req.getText()
-            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
-
-
-    def testParseProperty(self):
-
-        items = (
-            ("ADR:", "ADR:;;;;;;"),
-            ("ADR:;", "ADR:;;;;;;"),
-            ("ADR:;;;;;;", "ADR:;;;;;;"),
-            ("ADR:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
-            ("ADR:;;;;;;USA", "ADR:;;;;;;USA"),
-            ("ADR:POB1", "ADR:POB1;;;;;;"),
-            ("ADR:;EXT", "ADR:;EXT;;;;;"),
-            ("ADR;VALUE=TEXT:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
-        )
-
-        for item, result in items:
-            prop = Property()
-            prop.parse(item)
-            test = prop.getText()
-            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_calendar.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_calendar.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_calendar.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,854 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.exceptions import PyCalendarInvalidData
-from pycalendar.parser import ParserContext
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.property import PyCalendarProperty
-import cStringIO as StringIO
-import difflib
-import unittest
-
-class TestCalendar(unittest.TestCase):
-
-    data = (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-X-WR-CALNAME:PayDay
-BEGIN:VTIMEZONE
-TZID:US/Eastern
-LAST-MODIFIED:20040110T032845Z
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:DC3D0301C7790B38631F1FBB at ninevah.local
-DTSTART;VALUE=DATE:20040227
-DTSTAMP:20050211T173501Z
-RRULE:FREQ=MONTHLY;BYDAY=-1MO,-1TU,-1WE,-1TH,-1FR;BYSETPOS=-1
-SUMMARY:PAY DAY
-BEGIN:VALARM
-ACTION:DISPLAY
-DESCRIPTION:Alarm for Organizer!
-TRIGGER;RELATED=START:-PT15M
-END:VALARM
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:12345-67890-3
-DTSTART:20071114T000000Z
-ATTENDEE:mailto:user2 at example.com
-EXDATE:20081114T000000Z
-ORGANIZER:mailto:user1 at example.com
-RRULE:FREQ=YEARLY
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-ATTACH:http://example.com/test.jpg
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-ATTACH;ENCODING=BASE64;VALUE=BINARY:dGVzdA==
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Example Inc.//Example Calendar//EN
-BEGIN:VTIMEZONE
-TZID:America/Montreal
-LAST-MODIFIED:20040110T032845Z
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VAVAILABILITY
-UID:20061005T133225Z-00001-availability at example.com
-DTSTART;TZID=America/Montreal:20060101T000000
-DTEND;TZID=America/Montreal:20060108T000000
-DTSTAMP:20061005T133225Z
-ORGANIZER:mailto:bernard at example.com
-BEGIN:AVAILABLE
-UID:20061005T133225Z-00001-A-availability at example.com
-DTSTART;TZID=America/Montreal:20060102T090000
-DTEND;TZID=America/Montreal:20060102T120000
-DTSTAMP:20061005T133225Z
-RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR
-SUMMARY:Monday\\, Wednesday and Friday from 9:00 to 12:00
-END:AVAILABLE
-BEGIN:AVAILABLE
-UID:20061005T133225Z-00001-A-availability at example.com
-RECURRENCE-ID;TZID=America/Montreal:20060106T090000
-DTSTART;TZID=America/Montreal:20060106T120000
-DTEND;TZID=America/Montreal:20060106T170000
-DTSTAMP:20061005T133225Z
-SUMMARY:Friday override from 12:00 to 17:00
-END:AVAILABLE
-END:VAVAILABILITY
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Example Inc.//Example Calendar//EN
-BEGIN:VTODO
-UID:event1 at ninevah.local
-CREATED:20060101T150000Z
-DTSTAMP:20051222T205953Z
-SUMMARY:event 1
-END:VTODO
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-BEGIN:X-COMPONENT
-UID:1234
-END:X-COMPONENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Apple Inc.//iCal 4.0.1//EN
-BEGIN:VTIMEZONE
-TZID:US/Pacific
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:PST
-TZOFFSETFROM:-0700
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:uid4
-DTSTART;TZID=US/Pacific:20100207T170000
-DTEND;TZID=US/Pacific:20100207T173000
-CREATED:20100203T013849Z
-DTSTAMP:20100203T013909Z
-SEQUENCE:3
-SUMMARY:New Event
-TRANSP:OPAQUE
-BEGIN:VALARM
-ACTION:AUDIO
-ATTACH:Basso
-TRIGGER:-PT20M
-X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
-END:VALARM
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-ATTACH:http://example.com/test.jpg
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-X-APPLE-STRUCTURED-LOCATION:geo:123.123,123.123
-X-Test:Some\, text.
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-ATTACH:http://example.com/test.jpg
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123
-X-Test:Some\, text.
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-)
-    data2 = (
-                (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:12345-67890-3
-DTSTART:20071114T000000Z
-ATTENDEE:mailto:user2 at example.com
-EXDATE:20081114T000000Z
-ORGANIZER:mailto:user1 at example.com
-RRULE:FREQ=YEARLY
-END:VEVENT
-X-TEST:Testing
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-X-TEST:Testing
-BEGIN:VEVENT
-UID:12345-67890-3
-DTSTART:20071114T000000Z
-ATTENDEE:mailto:user2 at example.com
-EXDATE:20081114T000000Z
-ORGANIZER:mailto:user1 at example.com
-RRULE:FREQ=YEARLY
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                ),
-)
-
-
-    def testRoundtrip(self):
-
-
-        def _doRoundtrip(caldata, resultdata=None):
-            test1 = resultdata if resultdata is not None else caldata
-
-            cal = PyCalendar()
-            cal.parse(StringIO.StringIO(caldata))
-
-            s = StringIO.StringIO()
-            cal.generate(s)
-            test2 = s.getvalue()
-
-            self.assertEqual(
-                test1,
-                test2,
-                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
-            )
-
-        for item in self.data:
-            _doRoundtrip(item)
-
-        for item1, item2 in self.data2:
-            _doRoundtrip(item1, item2)
-
-
-    def testRoundtripDuplicate(self):
-
-
-        def _doDuplicateRoundtrip(caldata):
-            cal = PyCalendar()
-            cal.parse(StringIO.StringIO(caldata))
-            cal = cal.duplicate()
-
-            s = StringIO.StringIO()
-            cal.generate(s)
-            self.assertEqual(caldata, s.getvalue())
-
-        for item in self.data:
-            _doDuplicateRoundtrip(item)
-
-
-    def testEquality(self):
-
-
-        def _doEquality(caldata):
-            cal1 = PyCalendar()
-            cal1.parse(StringIO.StringIO(caldata))
-
-            cal2 = PyCalendar()
-            cal2.parse(StringIO.StringIO(caldata))
-
-            self.assertEqual(cal1, cal2, "%s\n\n%s" % (cal1, cal2,))
-
-
-        def _doNonEquality(caldata):
-            cal1 = PyCalendar()
-            cal1.parse(StringIO.StringIO(caldata))
-
-            cal2 = PyCalendar()
-            cal2.parse(StringIO.StringIO(caldata))
-            cal2.addProperty(PyCalendarProperty("X-FOO", "BAR"))
-
-            self.assertNotEqual(cal1, cal2)
-
-        for item in self.data:
-            _doEquality(item)
-            _doNonEquality(item)
-
-
-    def testParseComponent(self):
-
-        data1 = """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n")
-
-        data2 = """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//Example Inc.//Example Calendar//EN
-BEGIN:VTIMEZONE
-TZID:America/Montreal
-LAST-MODIFIED:20040110T032845Z
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""".replace("\n", "\r\n")
-
-        result = """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VTIMEZONE
-TZID:America/Montreal
-LAST-MODIFIED:20040110T032845Z
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n")
-
-        cal = PyCalendar()
-        cal.parse(StringIO.StringIO(data1))
-        cal.parseComponent(StringIO.StringIO(data2))
-        self.assertEqual(str(cal), result)
-
-
-    def testParseFail(self):
-
-        data = (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCARD
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-VERSION:2.0
-END:VCARD
-""".replace("\n", "\r\n"),
-
-"""BOGUS
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BOGUS
-
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-BOGUS
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-
-BOGUS
-""".replace("\n", "\r\n"),
-
-        )
-
-        for item in data:
-            self.assertRaises(PyCalendarInvalidData, PyCalendar.parseText, item)
-
-
-    def testParseBlank(self):
-
-        data = (
-"""
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""
-
-BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-
-
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-
-
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-        )
-
-        save = ParserContext.BLANK_LINES_IN_DATA
-        for item in data:
-            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
-            self.assertRaises(PyCalendarInvalidData, PyCalendar.parseText, item)
-
-            ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
-            lines = item.split("\r\n")
-            result = "\r\n".join([line for line in lines if line]) + "\r\n"
-            self.assertEqual(str(PyCalendar.parseText(item)), result)
-
-        ParserContext.BLANK_LINES_IN_DATA = save
-
-
-    def testGetVEvents(self):
-
-        data = (
-            (
-                "Non-recurring match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20110601
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (PyCalendarDateTime(2011, 6, 1),),
-            ),
-            (
-                "Non-recurring no-match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20110501
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (),
-            ),
-            (
-                "Recurring match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20110601
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (
-                    PyCalendarDateTime(2011, 6, 1),
-                    PyCalendarDateTime(2011, 6, 2),
-                ),
-            ),
-            (
-                "Recurring no match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20110501
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (),
-            ),
-            (
-                "Recurring with override match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART:20110601T120000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-RECURRENCE-ID;VALUE=DATE:20110602T120000
-DTSTART;VALUE=DATE:20110602T130000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (
-                    PyCalendarDateTime(2011, 6, 1, 12, 0, 0),
-                    PyCalendarDateTime(2011, 6, 2, 13, 0, 0),
-                ),
-            ),
-            (
-                "Recurring with override no match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART:20110501T120000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-RECURRENCE-ID;VALUE=DATE:20110502T120000
-DTSTART;VALUE=DATE:20110502T130000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (),
-            ),
-            (
-                "Recurring partial match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20110531
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (
-                    PyCalendarDateTime(2011, 6, 1),
-                ),
-            ),
-            (
-                "Recurring with override partial match",
-                """BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART:20110531T120000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=DAILY;COUNT=2
-SUMMARY:New Year's Day
-END:VEVENT
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-RECURRENCE-ID;VALUE=DATE:20110601T120000
-DTSTART;VALUE=DATE:20110601T130000
-DURATION:P1D
-DTSTAMP:20020101T000000Z
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-                (
-                    PyCalendarDateTime(2011, 6, 1, 13, 0, 0),
-                ),
-            ),
-        )
-
-        for title, caldata, result in data:
-            calendar = PyCalendar.parseText(caldata)
-            instances = []
-            calendar.getVEvents(
-                PyCalendarPeriod(
-                    start=PyCalendarDateTime(2011, 6, 1),
-                    end=PyCalendarDateTime(2011, 7, 1),
-                ),
-                instances
-            )
-            instances = tuple([instance.getInstanceStart() for instance in instances])
-            self.assertEqual(instances, result, "Failed in %s: got %s, expected %s" % (title, instances, result))

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_componentrecur.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_componentrecur.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_componentrecur.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,66 +0,0 @@
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.calendar import PyCalendar
-import cStringIO as StringIO
-import unittest
-
-class TestCalendar(unittest.TestCase):
-
-    def testDuplicateWithRecurrenceChange(self):
-
-        data = (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;COUNT=400
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-)
-
-        cal1 = PyCalendar()
-        cal1.parse(StringIO.StringIO(data[0]))
-        cal2 = cal1.duplicate()
-        vevent = cal2.getComponents()[0]
-        rrules = vevent.getRecurrenceSet()
-        for rrule in rrules.getRules():
-            rrule.setUseCount(True)
-            rrule.setCount(400)
-            rrules.changed()
-
-        self.assertEqual(data[0], str(cal1))
-        self.assertEqual(data[1], str(cal2))

Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_datetime.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_datetime.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_datetime.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,9 +14,9 @@
 #    limitations under the License.
 ##
 
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.timezone import PyCalendarTimezone
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.timezone import Timezone
 import unittest
 
 class TestDateTime(unittest.TestCase):
@@ -25,16 +25,16 @@
 
         items = (
             (
-                PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
-                PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                DateTime(2011, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+                DateTime(2011, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
             ),
             (
-                PyCalendarDateTime(2011, 1, 1, 0, 0, 0),
-                PyCalendarDateTime(2011, 1, 1, 0, 0, 0),
+                DateTime(2011, 1, 1, 0, 0, 0),
+                DateTime(2011, 1, 1, 0, 0, 0),
             ),
             (
-                PyCalendarDateTime(2011, 1, 1),
-                PyCalendarDateTime(2011, 1, 1),
+                DateTime(2011, 1, 1),
+                DateTime(2011, 1, 1),
             )
         )
 
@@ -47,13 +47,13 @@
 
         s = set(
             (
-                PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
-                PyCalendarDateTime(2011, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
+                DateTime(2011, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)),
+                DateTime(2011, 1, 2, 0, 0, 0, tzid=Timezone(utc=True)),
             )
         )
 
-        self.assertTrue(PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)) in s)
-        self.assertFalse(PyCalendarDateTime(2011, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)) in s)
+        self.assertTrue(DateTime(2011, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)) in s)
+        self.assertFalse(DateTime(2011, 1, 3, 0, 0, 0, tzid=Timezone(utc=True)) in s)
 
 
     def testRoundtrip(self):
@@ -85,11 +85,11 @@
         )
 
         for item in data1:
-            dt = PyCalendarDateTime.parseText(item, False)
+            dt = DateTime.parseText(item, False)
             self.assertEqual(dt.getText(), item, "Failed on: %s" % (item,))
 
         for item, result in data2:
-            dt = PyCalendarDateTime.parseText(item, True)
+            dt = DateTime.parseText(item, True)
             self.assertEqual(dt.getText(), result, "Failed on: %s" % (item,))
 
 
@@ -122,16 +122,16 @@
         )
 
         for item in data1:
-            self.assertRaises(ValueError, PyCalendarDateTime.parseText, item, False)
+            self.assertRaises(ValueError, DateTime.parseText, item, False)
 
         for item in data2:
-            self.assertRaises(ValueError, PyCalendarDateTime.parseText, item, False)
+            self.assertRaises(ValueError, DateTime.parseText, item, False)
 
 
     def testCachePreserveOnAdjustment(self):
 
         # UTC first
-        dt = PyCalendarDateTime(2012, 6, 7, 12, 0, 0, PyCalendarTimezone(tzid="utc"))
+        dt = DateTime(2012, 6, 7, 12, 0, 0, Timezone(tzid="utc"))
         dt.getPosixTime()
 
         # check existing cache is complete
@@ -301,9 +301,9 @@
 END:VCALENDAR
 """.replace("\n", "\r\n")
 
-        PyCalendar.parseText(tzdata)
+        Calendar.parseText(tzdata)
 
-        dt = PyCalendarDateTime(2012, 6, 7, 12, 0, 0, PyCalendarTimezone(tzid="America/Pittsburgh"))
+        dt = DateTime(2012, 6, 7, 12, 0, 0, Timezone(tzid="America/Pittsburgh"))
         dt.getPosixTime()
 
         # check existing cache is complete
@@ -326,47 +326,47 @@
 
     def testSetWeekNo(self):
 
-        dt = PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2013, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 1)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2013, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)
 
-        dt = PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2013, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 1)
         dt.setWeekNo(2)
-        self.assertEqual(dt, PyCalendarDateTime(2013, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2013, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 2)
 
-        dt = PyCalendarDateTime(2013, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2013, 1, 8, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 2)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2013, 1, 1, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)
 
-        dt = PyCalendarDateTime(2014, 1, 7, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2014, 1, 7, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 2)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2013, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2013, 12, 31, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)
 
-        dt = PyCalendarDateTime(2012, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2012, 12, 31, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 1)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2012, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2012, 12, 31, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)
 
-        dt = PyCalendarDateTime(2016, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2016, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 53)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2016, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)
         dt.setWeekNo(2)
-        self.assertEqual(dt, PyCalendarDateTime(2016, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2016, 1, 15, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 2)
 
-        dt = PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
+        dt = DateTime(2016, 1, 8, 0, 0, 0, tzid=Timezone(utc=True))
         self.assertEqual(dt.getWeekNo(), 1)
         dt.setWeekNo(1)
-        self.assertEqual(dt, PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
+        self.assertEqual(dt, DateTime(2016, 1, 8, 0, 0, 0, tzid=Timezone(utc=True)))
         self.assertEqual(dt.getWeekNo(), 1)

Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_duration.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_duration.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_duration.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,7 +15,7 @@
 ##
 
 from cStringIO import StringIO
-from pycalendar.duration import PyCalendarDuration
+from pycalendar.duration import Duration
 from pycalendar.parser import ParserContext
 import unittest
 
@@ -56,13 +56,13 @@
             self.assertEqual(os.getvalue(), result)
 
         for seconds, result in TestDuration.test_data:
-            _doTest(PyCalendarDuration(duration=seconds), result)
+            _doTest(Duration(duration=seconds), result)
 
 
     def testParse(self):
 
         for seconds, result in TestDuration.test_data:
-            duration = PyCalendarDuration().parseText(result)
+            duration = Duration().parseText(result)
             self.assertEqual(duration.getTotalSeconds(), seconds)
 
 
@@ -81,7 +81,7 @@
             "P12DT1SA",
         )
         for data in test_bad_data:
-            self.assertRaises(ValueError, PyCalendarDuration.parseText, data)
+            self.assertRaises(ValueError, Duration.parseText, data)
 
 
     def testRelaxedBad(self):
@@ -94,8 +94,8 @@
         for text, seconds, result in test_relaxed_data:
 
             ParserContext.INVALID_DURATION_VALUE = ParserContext.PARSER_FIX
-            self.assertEqual(PyCalendarDuration.parseText(text).getTotalSeconds(), seconds)
-            self.assertEqual(PyCalendarDuration.parseText(text).getText(), result)
+            self.assertEqual(Duration.parseText(text).getTotalSeconds(), seconds)
+            self.assertEqual(Duration.parseText(text).getText(), result)
 
             ParserContext.INVALID_DURATION_VALUE = ParserContext.PARSER_RAISE
-            self.assertRaises(ValueError, PyCalendarDuration.parseText, text)
+            self.assertRaises(ValueError, Duration.parseText, text)

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_i18n.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_i18n.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_i18n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,74 +0,0 @@
-# coding: utf-8
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.calendar import PyCalendar
-import cStringIO as StringIO
-import unittest
-
-class TestCalendar(unittest.TestCase):
-
-    def testAddCN(self):
-
-        data = (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-ORGANIZER:user01 at example.com
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-    "まだ",
-
-"""BEGIN:VCALENDAR
-VERSION:2.0
-CALSCALE:GREGORIAN
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
-DTSTART;VALUE=DATE:20020101
-DTEND;VALUE=DATE:20020102
-DTSTAMP:20020101T000000Z
-RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
-ORGANIZER;CN=まだ:user01 at example.com
-SUMMARY:New Year's Day
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-        )
-
-        cal1 = PyCalendar()
-        cal1.parse(StringIO.StringIO(data[0]))
-
-        vevent = cal1.getComponents("VEVENT")[0]
-        organizer = vevent.getProperties("ORGANIZER")[0]
-        organizer.addAttribute(PyCalendarAttribute("CN", data[1]))
-
-        cal2 = PyCalendar()
-        cal2.parse(StringIO.StringIO(data[2]))
-
-        self.assertEqual(str(cal1), str(cal2))

Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_multivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_multivalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_multivalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,8 +15,8 @@
 ##
 
 import unittest
-from pycalendar.multivalue import PyCalendarMultiValue
-from pycalendar.value import PyCalendarValue
+from pycalendar.multivalue import MultiValue
+from pycalendar.value import Value
 
 class TestMultiValue(unittest.TestCase):
 
@@ -29,8 +29,8 @@
         )
 
         for item, result, count in items:
-            req = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_TEXT)
-            req.parse(item)
+            req = MultiValue(Value.VALUETYPE_TEXT)
+            req.parse(item, "icalendar")
             test = req.getText()
             self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
             self.assertEqual(len(req.mValues), count, "Failed to parse and re-generate '%s'" % (item,))
@@ -38,8 +38,8 @@
 
     def testSetValue(self):
 
-        req = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_TEXT)
-        req.parse("Example1, Example2")
+        req = MultiValue(Value.VALUETYPE_TEXT)
+        req.parse("Example1, Example2", "icalendar")
         req.setValue(("Example3", "Example4",))
         test = req.getText()
         self.assertEqual(test, "Example3,Example4")

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_n.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_n.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,92 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.n import N
-import unittest
-
-class TestAdrValue(unittest.TestCase):
-
-    def testInit(self):
-
-        data = (
-            (
-                ("last", "first", "middle", "prefix", "suffix"),
-                "last;first;middle;prefix;suffix",
-                "prefix first middle last suffix",
-            ),
-            (
-                ("last", ("first",), ("middle1", "middle2",), (), ("suffix",)),
-                "last;first;middle1,middle2;;suffix",
-                "first middle1 middle2 last suffix",
-            ),
-        )
-
-        for args, result, fullName in data:
-            n = N(*args)
-
-            self.assertEqual(
-                n.getValue(),
-                args,
-            )
-
-            self.assertEqual(
-                n.getText(),
-                result,
-            )
-
-            self.assertEqual(
-                n.getFullName(),
-                fullName,
-            )
-
-            self.assertEqual(
-                n.duplicate().getText(),
-                result,
-            )
-
-
-    def testInitWithKeywords(self):
-
-        data = (
-            (
-                {"first": "first", "last": "last", "middle": "middle", "prefix": "prefix", "suffix": "suffix"},
-                "last;first;middle;prefix;suffix",
-                "prefix first middle last suffix",
-            ),
-            (
-                {"first": ("first",), "last": "last", "middle": ("middle1", "middle2",), "prefix": (), "suffix": ("suffix",)},
-                "last;first;middle1,middle2;;suffix",
-                "first middle1 middle2 last suffix",
-            ),
-        )
-
-        for kwargs, result, fullName in data:
-            n = N(**kwargs)
-
-            self.assertEqual(
-                n.getText(),
-                result,
-            )
-
-            self.assertEqual(
-                n.getFullName(),
-                fullName,
-            )
-
-            self.assertEqual(
-                n.duplicate().getText(),
-                result,
-            )

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_nvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_nvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_nvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,59 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.nvalue import NValue
-from pycalendar.vcard.property import Property
-import unittest
-
-class TestNValue(unittest.TestCase):
-
-    def testParseValue(self):
-
-        items = (
-            ("", ";;;;"),
-            (";", ";;;;"),
-            (";;;;", ";;;;"),
-            ("Cyrus;Daboo;;Dr.", "Cyrus;Daboo;;Dr.;"),
-            (";;;;PhD.", ";;;;PhD."),
-            ("Cyrus", "Cyrus;;;;"),
-            (";Daboo", ";Daboo;;;"),
-        )
-
-        for item, result in items:
-            req = NValue()
-            req.parse(item)
-            test = req.getText()
-            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
-
-
-    def testParseProperty(self):
-
-        items = (
-            ("N:", "N:;;;;"),
-            ("N:;", "N:;;;;"),
-            ("N:;;;;", "N:;;;;"),
-            ("N:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
-            ("N:;;;;PhD.", "N:;;;;PhD."),
-            ("N:Cyrus", "N:Cyrus;;;;"),
-            ("N:;Daboo", "N:;Daboo;;;"),
-            ("N;VALUE=TEXT:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
-        )
-
-        for item, result in items:
-            prop = Property()
-            prop.parse(item)
-            test = prop.getText()
-            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_orgvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_orgvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_orgvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,53 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.orgvalue import OrgValue
-from pycalendar.vcard.property import Property
-import unittest
-
-class TestNValue(unittest.TestCase):
-
-    def testParseValue(self):
-
-        items = (
-            ("", ""),
-            ("Example", "Example"),
-            ("Example\, Inc.", "Example\, Inc."),
-            ("Example\; Inc;Dept. of Silly Walks", "Example\; Inc;Dept. of Silly Walks"),
-        )
-
-        for item, result in items:
-            req = OrgValue()
-            req.parse(item)
-            test = req.getText()
-            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
-
-
-    def testParseProperty(self):
-
-        items = (
-            ("ORG:", "ORG:"),
-            ("ORG:Example", "ORG:Example"),
-            ("ORG:Example\, Inc.", "ORG:Example\, Inc."),
-            ("ORG:Example\; Inc;Dept. of Silly Walks", "ORG:Example\; Inc;Dept. of Silly Walks"),
-            ("ORG;VALUE=TEXT:Example", "ORG:Example"),
-        )
-
-        for item, result in items:
-            prop = Property()
-            prop.parse(item)
-            test = prop.getText()
-            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_property.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,189 +0,0 @@
-##
-#    Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.exceptions import PyCalendarInvalidProperty
-from pycalendar.parser import ParserContext
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
-import unittest
-
-class TestProperty(unittest.TestCase):
-
-    test_data = (
-        # Different value types
-        "ATTACH;VALUE=BINARY:VGVzdA==",
-        "attach;VALUE=BINARY:VGVzdA==",
-        "ORGANIZER:mailto:jdoe at example.com",
-        "DTSTART;TZID=US/Eastern:20060226T120000",
-        "DTSTART;VALUE=DATE:20060226",
-        "DTSTART:20060226T130000Z",
-        "X-FOO:BAR",
-        "DURATION:PT10M",
-        "duraTION:PT10M",
-        "SEQUENCE:1",
-        "RDATE:20060226T120000Z,20060227T120000Z",
-        "FREEBUSY:20060226T120000Z/20060227T120000Z",
-        "SUMMARY:Some \\ntext",
-        "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
-        "REQUEST-STATUS:2.0;Success",
-        "URI:http://www.example.com",
-        "TZOFFSETFROM:-0500",
-        "X-Test:Some\, text.",
-        "X-Test:Some:, text.",
-        "X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123",
-        "X-CALENDARSERVER-PRIVATE-COMMENT:This\\ntest\\nis\\, here.\\n",
-
-        # Various parameters
-        "DTSTART;TZID=\"Somewhere, else\":20060226T120000",
-        "ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:jdoe at example.com",
-        "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-APPLE-ABUID=ab\\://Work;X-TITLE=\"10\\n XX S. XXX Dr.\\nSuite XXX\\nXX XX XXXXX\\nUnited States\":\"geo:11.111111,-11.111111\"",
-
-        # Parameter escaping
-        "ATTENDEE;CN=My ^'Test^' Name;ROLE=CHAIR:mailto:jdoe at example.com",
-    )
-
-
-    def testParseGenerate(self):
-
-        for data in TestProperty.test_data:
-            prop = PyCalendarProperty()
-            prop.parse(data)
-            propstr = str(prop).replace("\r\n ", "")
-            self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
-
-
-    def testEquality(self):
-
-        for data in TestProperty.test_data:
-            prop1 = PyCalendarProperty()
-            prop1.parse(data)
-            prop2 = PyCalendarProperty()
-            prop2.parse(data)
-            self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
-
-
-    def testParseBad(self):
-
-        test_bad_data = (
-            "DTSTART;TZID=US/Eastern:abc",
-            "DTSTART;VALUE=DATE:20060226T",
-            "DTSTART:20060226T120000A",
-            "X-FOO;:BAR",
-            "DURATION:A",
-            "SEQUENCE:b",
-            "RDATE:20060226T120000Z;20060227T120000Z",
-            "FREEBUSY:20060226T120000Z/ABC",
-            "SUMMARY:Some \\qtext",
-            "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,VE;BYSETPOS=-1",
-            "TZOFFSETFROM:-050",
-            """ATTENDEE;CN="\\";CUTYPE=INDIVIDUAL;PARTSTAT=X-UNDELIVERABLE:invalid:nomai
- l""",
-        )
-        save = ParserContext.INVALID_ESCAPE_SEQUENCES
-        for data in test_bad_data:
-            ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
-            prop = PyCalendarProperty()
-            self.assertRaises(PyCalendarInvalidProperty, prop.parse, data)
-        ParserContext.INVALID_ESCAPE_SEQUENCES = save
-
-
-    def testHash(self):
-
-        hashes = []
-        for item in TestProperty.test_data:
-            prop = PyCalendarProperty()
-            prop.parse(item)
-            hashes.append(hash(prop))
-        hashes.sort()
-        for i in range(1, len(hashes)):
-            self.assertNotEqual(hashes[i - 1], hashes[i])
-
-
-    def testDefaultValueCreate(self):
-
-        test_data = (
-            ("ATTENDEE", "mailto:attendee at example.com", "ATTENDEE:mailto:attendee at example.com\r\n"),
-            ("attendee", "mailto:attendee at example.com", "attendee:mailto:attendee at example.com\r\n"),
-            ("ORGANIZER", "mailto:organizer at example.com", "ORGANIZER:mailto:organizer at example.com\r\n"),
-            ("ORGANizer", "mailto:organizer at example.com", "ORGANizer:mailto:organizer at example.com\r\n"),
-            ("URL", "http://example.com/tz1", "URL:http://example.com/tz1\r\n"),
-            ("TZURL", "http://example.com/tz2", "TZURL:http://example.com/tz2\r\n"),
-        )
-        for propname, propvalue, result in test_data:
-            prop = PyCalendarProperty(name=propname, value=propvalue)
-            self.assertEqual(str(prop), result)
-
-
-    def testGEOValueRoundtrip(self):
-
-        data = "GEO:123.456,789.101"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(str(prop), data + "\r\n")
-
-
-    def testUnknownValueRoundtrip(self):
-
-        data = "X-FOO:Text, not escaped"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(str(prop), data + "\r\n")
-
-        prop = PyCalendarProperty("X-FOO", "Text, not escaped")
-        self.assertEqual(str(prop), data + "\r\n")
-
-        data = "X-FOO:Text\\, escaped\\n"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(str(prop), data + "\r\n")
-
-        prop = PyCalendarProperty("X-FOO", "Text\\, escaped\\n")
-        self.assertEqual(str(prop), data + "\r\n")
-
-
-    def testNewRegistrationValueRoundtrip(self):
-
-        PyCalendarProperty.registerDefaultValue("X-SPECIAL-REGISTRATION", PyCalendarValue.VALUETYPE_TEXT)
-
-        data = "X-SPECIAL-REGISTRATION:Text\\, escaped\\n"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
-
-        prop = PyCalendarProperty("X-SPECIAL-REGISTRATION", "Text, escaped\n")
-        self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
-
-
-    def testParameterEncodingDecoding(self):
-
-        prop = PyCalendarProperty("X-FOO", "Test")
-        prop.addAttribute(PyCalendarAttribute("X-BAR", "\"Check\""))
-        self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^':Test\r\n")
-
-        prop.addAttribute(PyCalendarAttribute("X-BAR2", "Check\nThis\tOut\n"))
-        self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
-
-        data = "X-FOO;X-BAR=^'Check^':Test"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
-
-        data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
-        prop = PyCalendarProperty()
-        prop.parse(data)
-        self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
-        self.assertEqual(prop.getAttributeValue("X-BAR2"), "Check\nThis\tOut\n")

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_recurrence.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_recurrence.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_recurrence.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,161 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.recurrence import PyCalendarRecurrence
-import unittest
-
-class TestRecurrence(unittest.TestCase):
-
-    items = (
-        "FREQ=DAILY",
-        "FREQ=YEARLY;COUNT=400",
-        "FREQ=MONTHLY;UNTIL=20110102",
-        "FREQ=MONTHLY;UNTIL=20110102T090000",
-        "FREQ=MONTHLY;UNTIL=20110102T100000Z",
-        "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
-
-        # These are from RFC5545 examples
-        "FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
-        "FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4",
-        "FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4",
-        "FREQ=YEARLY;BYDAY=2SU;BYMONTH=3",
-        "FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
-        "FREQ=DAILY;INTERVAL=2",
-        "FREQ=DAILY;COUNT=5;INTERVAL=10",
-        "FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1",
-        "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
-        "FREQ=WEEKLY;UNTIL=19971007T000000Z;BYDAY=TU,TH;WKST=SU",
-        "FREQ=WEEKLY;COUNT=10;BYDAY=TU,TH;WKST=SU",
-        "FREQ=WEEKLY;UNTIL=19971224T000000Z;INTERVAL=2;BYDAY=MO,WE,FR;WKST=SU",
-        "FREQ=WEEKLY;COUNT=8;INTERVAL=2;BYDAY=TU,TH;WKST=SU",
-        "FREQ=MONTHLY;BYMONTHDAY=-3",
-        "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
-        "FREQ=MONTHLY;COUNT=10;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,15",
-        "FREQ=YEARLY;COUNT=10;INTERVAL=3;BYYEARDAY=1,100,200",
-        "FREQ=YEARLY;BYDAY=MO;BYWEEKNO=20",
-        "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
-        "FREQ=DAILY;BYMINUTE=0,20,40;BYHOUR=9,10,11,12,13,14,15,16",
-    )
-
-
-    def testParse(self):
-
-        for item in TestRecurrence.items:
-            recur = PyCalendarRecurrence()
-            recur.parse(item)
-            self.assertEqual(recur.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
-
-
-    def testParseInvalid(self):
-
-        items = (
-            "",
-            "FREQ=",
-            "FREQ=MICROSECONDLY",
-            "FREQ=YEARLY;COUNT=ABC",
-            "FREQ=YEARLY;COUNT=123;UNTIL=20110102",
-            "FREQ=MONTHLY;UNTIL=20110102T",
-            "FREQ=MONTHLY;UNTIL=20110102t090000",
-            "FREQ=MONTHLY;UNTIL=20110102T100000z",
-            "FREQ=MONTHLY;UNTIL=20110102TAABBCCz",
-            "FREQ=MONTHLY;BYDAY=A",
-            "FREQ=MONTHLY;BYDAY=+1,3MO",
-            "FREQ=MONTHLY;BYHOUR=A",
-            "FREQ=MONTHLY;BYHOUR=54",
-        )
-
-        for item in items:
-            self.assertRaises(ValueError, PyCalendarRecurrence().parse, item)
-
-
-    def testEquality(self):
-
-        recur1 = PyCalendarRecurrence()
-        recur1.parse("FREQ=YEARLY;COUNT=400")
-        recur2 = PyCalendarRecurrence()
-        recur2.parse("COUNT=400;FREQ=YEARLY")
-
-        self.assertEqual(recur1, recur2)
-
-
-    def testInequality(self):
-
-        recur1 = PyCalendarRecurrence()
-        recur1.parse("FREQ=YEARLY;COUNT=400")
-        recur2 = PyCalendarRecurrence()
-        recur2.parse("COUNT=400;FREQ=YEARLY;BYMONTH=1")
-
-        self.assertNotEqual(recur1, recur2)
-
-
-    def testHash(self):
-
-        hashes = []
-        for item in TestRecurrence.items:
-            recur = PyCalendarRecurrence()
-            recur.parse(item)
-            hashes.append(hash(recur))
-        hashes.sort()
-        for i in range(1, len(hashes)):
-            self.assertNotEqual(hashes[i - 1], hashes[i])
-
-
-    def testByWeekNoExpand(self):
-
-        recur = PyCalendarRecurrence()
-        recur.parse("FREQ=YEARLY;BYWEEKNO=1,2")
-        start = PyCalendarDateTime(2013, 1, 1, 0, 0, 0)
-        end = PyCalendarDateTime(2017, 1, 1, 0, 0, 0)
-        items = []
-        range = PyCalendarPeriod(start, end)
-        recur.expand(start, range, items)
-        self.assertEqual(
-            items,
-            [
-                PyCalendarDateTime(2013, 1, 1, 0, 0, 0),
-                PyCalendarDateTime(2013, 1, 8, 0, 0, 0),
-                PyCalendarDateTime(2014, 1, 1, 0, 0, 0),
-                PyCalendarDateTime(2014, 1, 8, 0, 0, 0),
-                PyCalendarDateTime(2015, 1, 1, 0, 0, 0),
-                PyCalendarDateTime(2015, 1, 8, 0, 0, 0),
-                PyCalendarDateTime(2016, 1, 8, 0, 0, 0),
-                PyCalendarDateTime(2016, 1, 15, 0, 0, 0),
-            ],
-        )
-        print items
-
-
-    def testClearOnChange(self):
-
-        recur = PyCalendarRecurrence()
-        recur.parse("FREQ=DAILY")
-
-        start = PyCalendarDateTime(2013, 1, 1, 0, 0, 0)
-        end = PyCalendarDateTime(2017, 1, 1, 0, 0, 0)
-        range = PyCalendarPeriod(start, end)
-        items = []
-        recur.expand(start, range, items)
-        self.assertTrue(recur.mCached)
-        self.assertTrue(len(items) > 100)
-
-        recur.setUseCount(True)
-        recur.setCount(10)
-        self.assertFalse(recur.mCached)
-        items = []
-        recur.expand(start, range, items)
-        self.assertEqual(len(items), 10)

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_requeststatus.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_requeststatus.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_requeststatus.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,95 +0,0 @@
-##
-#    Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.parser import ParserContext
-from pycalendar.property import PyCalendarProperty
-from pycalendar.requeststatusvalue import PyCalendarRequestStatusValue
-import unittest
-
-class TestRequestStatus(unittest.TestCase):
-
-    def testParseValue(self):
-
-        items = (
-            "2.0;Success",
-            "2.0;Success\;here",
-            "2.0;Success;Extra",
-            "2.0;Success\;here;Extra",
-            "2.0;Success;Extra\;here",
-            "2.0;Success\;here;Extra\;here too",
-        )
-
-        for item in items:
-            req = PyCalendarRequestStatusValue()
-            req.parse(item)
-            self.assertEqual(req.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
-
-
-    def testBadValue(self):
-
-        bad_value = "2.0\;Success"
-        ok_value = "2.0;Success"
-
-        # Fix the value
-        oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
-        req = PyCalendarRequestStatusValue()
-        req.parse(bad_value)
-        self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
-
-        # Raise the value
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
-        req = PyCalendarRequestStatusValue()
-        self.assertRaises(ValueError, req.parse, bad_value)
-
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
-
-
-    def testTruncatedValue(self):
-
-        bad_value = "2.0"
-        ok_value = "2.0;"
-
-        # Fix the value
-        oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
-        req = PyCalendarRequestStatusValue()
-        req.parse(bad_value)
-        self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
-
-        # Raise the value
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
-        req = PyCalendarRequestStatusValue()
-        self.assertRaises(ValueError, req.parse, bad_value)
-
-        ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
-
-
-    def testParseProperty(self):
-
-        items = (
-            "REQUEST-STATUS:2.0;Success",
-            "REQUEST-STATUS:2.0;Success\;here",
-            "REQUEST-STATUS:2.0;Success;Extra",
-            "REQUEST-STATUS:2.0;Success\;here;Extra",
-            "REQUEST-STATUS:2.0;Success;Extra\;here",
-            "REQUEST-STATUS:2.0;Success\;here;Extra\;here too",
-        )
-
-        for item in items:
-            req = PyCalendarProperty()
-            req.parse(item)
-            self.assertEqual(req.getText(), item + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_timezone.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_timezone.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_timezone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,235 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
-import unittest
-
-class TestCalendar(unittest.TestCase):
-
-    def testOffsets(self):
-
-        data = (
-                    ("""BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:America/New_York
-X-LIC-LOCATION:America/New_York
-BEGIN:STANDARD
-DTSTART:18831118T120358
-RDATE:18831118T120358
-TZNAME:EST
-TZOFFSETFROM:-045602
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19180331T020000
-RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19181027T020000
-RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19200101T000000
-RDATE:19200101T000000
-RDATE:19420101T000000
-RDATE:19460101T000000
-RDATE:19670101T000000
-TZNAME:EST
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19200328T020000
-RDATE:19200328T020000
-RDATE:19740106T020000
-RDATE:19750223T020000
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19201031T020000
-RDATE:19201031T020000
-RDATE:19450930T020000
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19210424T020000
-RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19210925T020000
-RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19420209T020000
-RDATE:19420209T020000
-TZNAME:EWT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19450814T190000
-RDATE:19450814T190000
-TZNAME:EPT
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19460428T020000
-RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19460929T020000
-RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:STANDARD
-DTSTART:19551030T020000
-RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19670430T020000
-RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:19671029T020000
-RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-BEGIN:DAYLIGHT
-DTSTART:19760425T020000
-RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:19870405T020000
-RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""",
-                (
-                    (PyCalendarDateTime(1942, 2, 8), -5),
-                    (PyCalendarDateTime(1942, 2, 10), -4),
-                    (PyCalendarDateTime(2011, 1, 1), -5),
-                    (PyCalendarDateTime(2011, 4, 1), -4),
-                    (PyCalendarDateTime(2011, 10, 24), -4),
-                    (PyCalendarDateTime(2011, 11, 8), -5),
-                    (PyCalendarDateTime(2006, 1, 1), -5),
-                    (PyCalendarDateTime(2006, 4, 1), -5),
-                    (PyCalendarDateTime(2006, 5, 1), -4),
-                    (PyCalendarDateTime(2006, 10, 1), -4),
-                    (PyCalendarDateTime(2006, 10, 24), -4),
-                    (PyCalendarDateTime(2006, 11, 8), -5),
-                )
-            ),
-                    ("""BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//calendarserver.org//Zonal//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-TZID:Etc/GMT+8
-X-LIC-LOCATION:Etc/GMT+8
-BEGIN:STANDARD
-DTSTART:18000101T000000
-RDATE:18000101T000000
-TZNAME:GMT+8
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
-END:VCALENDAR
-""",
-                (
-                    (PyCalendarDateTime(1942, 2, 8), -8),
-                    (PyCalendarDateTime(1942, 2, 10), -8),
-                    (PyCalendarDateTime(2011, 1, 1), -8),
-                    (PyCalendarDateTime(2011, 4, 1), -8),
-                )
-            ),
-        )
-
-        for tzdata, offsets in data:
-
-            cal = PyCalendar.parseText(tzdata.replace("\n", "\r\n"))
-            tz = cal.getComponents()[0]
-
-            for dt, offset in offsets:
-                tzoffset = tz.getTimezoneOffsetSeconds(dt)
-                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching" % (tz.getID(), dt,))
-            for dt, offset in reversed(offsets):
-                tzoffset = tz.getTimezoneOffsetSeconds(dt)
-                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching, reversed" % (tz.getID(), dt,))
-
-            for dt, offset in offsets:
-                tz.mCachedExpandAllMax = None
-                tzoffset = tz.getTimezoneOffsetSeconds(dt)
-                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching" % (tz.getID(), dt,))
-            for dt, offset in reversed(offsets):
-                tz.mCachedExpandAllMax = None
-                tzoffset = tz.getTimezoneOffsetSeconds(dt)
-                self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching, reversed" % (tz.getID(), dt,))

Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_urivalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,11 +15,11 @@
 ##
 
 from pycalendar.parser import ParserContext
-from pycalendar.urivalue import PyCalendarURIValue
-from pycalendar.vcard.property import Property
+from pycalendar.urivalue import URIValue
+from pycalendar.icalendar.property import Property
 import unittest
 
-class TestNValue(unittest.TestCase):
+class TestURIValue(unittest.TestCase):
 
     def testParseValue(self):
 
@@ -35,8 +35,8 @@
         )
 
         for item, result in items:
-            req = PyCalendarURIValue()
-            req.parse(item)
+            req = URIValue()
+            req.parse(item, "icalendar")
             test = req.getText()
             self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
 
@@ -48,8 +48,8 @@
         )
 
         for item, result in items:
-            req = PyCalendarURIValue()
-            req.parse(item)
+            req = URIValue()
+            req.parse(item, "icalendar")
             test = req.getText()
             self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
 

Modified: PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_validation.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-from pycalendar.property import PyCalendarProperty
+from pycalendar.icalendar.property import Property
 from pycalendar.validation import partial, PropertyValueChecks
 import unittest
 
@@ -40,7 +40,7 @@
         )
 
         for prop, test, result in props:
-            property = PyCalendarProperty()
+            property = Property()
             property.parse(prop)
             self.assertEqual(PropertyValueChecks.stringValue(test, property), result)
 
@@ -55,7 +55,7 @@
         )
 
         for prop, result in props:
-            property = PyCalendarProperty()
+            property = Property()
             property.parse(prop)
             self.assertEqual(PropertyValueChecks.alwaysUTC(property), result)
 
@@ -72,7 +72,7 @@
         )
 
         for prop, low, high, result in props:
-            property = PyCalendarProperty()
+            property = Property()
             property.parse(prop)
             self.assertEqual(PropertyValueChecks.numericRange(low, high, property), result)
 
@@ -87,6 +87,6 @@
         )
 
         for prop, result in props:
-            property = PyCalendarProperty()
+            property = Property()
             property.parse(prop)
             self.assertEqual(PropertyValueChecks.positiveIntegerOrZero(property), result)

Deleted: PyCalendar/branches/json-2/src/pycalendar/tests/test_xml.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/tests/test_xml.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/tests/test_xml.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,104 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar.calendar import PyCalendar
-import cStringIO as StringIO
-import difflib
-import unittest
-
-class TestCalendar(unittest.TestCase):
-
-    data = (
-                (
-"""BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//mulberrymail.com//Mulberry v4.0//EN
-BEGIN:VEVENT
-UID:12345-67890-3
-DTSTART:20071114T000000Z
-ATTENDEE:mailto:user2 at example.com
-EXDATE:20081114T000000Z
-ORGANIZER:mailto:user1 at example.com
-RRULE:FREQ=YEARLY
-END:VEVENT
-X-TEST:Testing
-END:VCALENDAR
-""".replace("\n", "\r\n"),
-
-"""<?xml version="1.0" encoding="utf-8"?>
-<ns0:icalendar xmlns:ns0="urn:ietf:params:xml:ns:icalendar-2.0">
-  <ns0:vcalendar>
-    <ns0:properties>
-      <ns0:version>
-        <ns0:text>2.0</ns0:text>
-      </ns0:version>
-      <ns0:prodid>
-        <ns0:text>-//mulberrymail.com//Mulberry v4.0//EN</ns0:text>
-      </ns0:prodid>
-      <ns0:x-test>
-        <ns0:unknown>Testing</ns0:unknown>
-      </ns0:x-test>
-    </ns0:properties>
-    <ns0:components>
-      <ns0:vevent>
-        <ns0:properties>
-          <ns0:uid>
-            <ns0:text>12345-67890-3</ns0:text>
-          </ns0:uid>
-          <ns0:dtstart>
-            <ns0:date-time>2007-11-14T00:00:00Z</ns0:date-time>
-          </ns0:dtstart>
-          <ns0:attendee>
-            <ns0:cal-address>mailto:user2 at example.com</ns0:cal-address>
-          </ns0:attendee>
-          <ns0:exdate>
-            <ns0:date-time>2008-11-14T00:00:00Z</ns0:date-time>
-          </ns0:exdate>
-          <ns0:organizer>
-            <ns0:cal-address>mailto:user1 at example.com</ns0:cal-address>
-          </ns0:organizer>
-          <ns0:rrule>
-            <ns0:recur>
-              <ns0:freq>YEARLY</ns0:freq>
-            </ns0:recur>
-          </ns0:rrule>
-        </ns0:properties>
-      </ns0:vevent>
-    </ns0:components>
-  </ns0:vcalendar>
-</ns0:icalendar>
-""",
-                ),
-)
-
-    def testGenerateXML(self):
-
-        def _doRoundtrip(caldata, resultdata=None):
-            test1 = resultdata if resultdata is not None else caldata
-
-            cal = PyCalendar()
-            cal.parse(StringIO.StringIO(caldata))
-
-            test2 = cal.getTextXML()
-
-            self.assertEqual(
-                test1,
-                test2,
-                "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
-            )
-
-        for item1, item2 in self.data:
-            _doRoundtrip(item1, item2)

Modified: PyCalendar/branches/json-2/src/pycalendar/textvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/textvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/textvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,17 +16,17 @@
 
 # iCalendar UTC Offset value
 
-from pycalendar import utils, xmldefs
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.value import PyCalendarValue
+from pycalendar import utils, xmldefinitions
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.value import Value
 
-class PyCalendarTextValue(PyCalendarPlainTextValue):
+class TextValue(PlainTextValue):
 
     def getType(self):
-        return PyCalendarValue.VALUETYPE_TEXT
+        return Value.VALUETYPE_TEXT
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
         # Decoding required
         self.mValue = utils.decodeTextValue(data)
 
@@ -39,4 +39,4 @@
         except:
             pass
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_TEXT, PyCalendarTextValue, xmldefs.value_text)
+Value.registerType(Value.VALUETYPE_TEXT, TextValue, xmldefinitions.value_text)

Modified: PyCalendar/branches/json-2/src/pycalendar/timezone.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/timezone.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/timezone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,9 +15,9 @@
 ##
 
 from pycalendar import stringutils
-from pycalendar.timezonedb import PyCalendarTimezoneDatabase
+from pycalendar.timezonedb import TimezoneDatabase
 
-class PyCalendarTimezone(object):
+class Timezone(object):
     """
     Wrapper around a timezone specification. There are three options:
 
@@ -41,13 +41,13 @@
             self.mTimezone = None
 
             # Copy default timezone if it exists
-            if PyCalendarTimezone.sDefaultTimezone is not None:
-                self.mUTC = PyCalendarTimezone.sDefaultTimezone.mUTC
-                self.mTimezone = PyCalendarTimezone.sDefaultTimezone.mTimezone
+            if Timezone.sDefaultTimezone is not None:
+                self.mUTC = Timezone.sDefaultTimezone.mUTC
+                self.mTimezone = Timezone.sDefaultTimezone.mTimezone
 
 
     def duplicate(self):
-        return PyCalendarTimezone(self.mUTC, self.mTimezone)
+        return Timezone(self.mUTC, self.mTimezone)
 
 
     def equals(self, comp):
@@ -63,7 +63,7 @@
     @staticmethod
     def same(utc1, tzid1, utc2, tzid2):
         # Always match if any one of them is 'floating'
-        if PyCalendarTimezone.is_float(utc1, tzid1) or PyCalendarTimezone.is_float(utc2, tzid2):
+        if Timezone.is_float(utc1, tzid1) or Timezone.is_float(utc2, tzid2):
             return True
         elif utc1 != utc2:
             return False
@@ -104,19 +104,19 @@
         if self.mUTC:
             return 0
         elif self.mTimezone is None:
-            return PyCalendarTimezoneDatabase.getTimezoneOffsetSeconds(PyCalendarTimezone.sDefaultTimezone.getTimezoneID(), dt)
+            return TimezoneDatabase.getTimezoneOffsetSeconds(Timezone.sDefaultTimezone.getTimezoneID(), dt)
         elif isinstance(self.mTimezone, int):
             return self.mTimezone
         else:
             # Look up timezone and resolve date using default timezones
-            return PyCalendarTimezoneDatabase.getTimezoneOffsetSeconds(self.mTimezone, dt)
+            return TimezoneDatabase.getTimezoneOffsetSeconds(self.mTimezone, dt)
 
 
     def timeZoneDescriptor(self, dt):
         if self.mUTC:
             return "(UTC)"
         elif self.mTimezone is None:
-            return PyCalendarTimezoneDatabase.getTimezoneDescriptor(PyCalendarTimezone.sDefaultTimezone.getTimezoneID(), dt)
+            return TimezoneDatabase.getTimezoneDescriptor(Timezone.sDefaultTimezone.getTimezoneID(), dt)
         elif isinstance(self.mTimezone, int):
             sign = "-" if self.mTimezone < 0 else "+"
             hours = abs(self.mTimezone) / 3600
@@ -124,6 +124,6 @@
             return "%s%02d%02d" % (sign, hours, minutes,)
         else:
             # Look up timezone and resolve date using default timezones
-            return PyCalendarTimezoneDatabase.getTimezoneDescriptor(self.mTimezone, dt)
+            return TimezoneDatabase.getTimezoneDescriptor(self.mTimezone, dt)
 
-PyCalendarTimezone.sDefaultTimezone = PyCalendarTimezone()
+Timezone.sDefaultTimezone = Timezone()

Modified: PyCalendar/branches/json-2/src/pycalendar/timezonedb.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/timezonedb.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/timezonedb.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,11 +14,11 @@
 #    limitations under the License.
 ##
 
-from pycalendar.exceptions import PyCalendarNoTimezoneInDatabase, \
-    PyCalendarInvalidData
+from pycalendar.exceptions import NoTimezoneInDatabase, \
+    InvalidData
 import os
 
-class PyCalendarTimezoneDatabase(object):
+class TimezoneDatabase(object):
     """
     On demand timezone database cache. This scans a TZdb directory for .ics files matching a
     TZID and caches the component data in a calendar from whence the actual component is returned.
@@ -29,20 +29,20 @@
 
     @staticmethod
     def createTimezoneDatabase(dbpath):
-        PyCalendarTimezoneDatabase.sTimezoneDatabase = PyCalendarTimezoneDatabase()
-        PyCalendarTimezoneDatabase.sTimezoneDatabase.setPath(dbpath)
+        TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
+        TimezoneDatabase.sTimezoneDatabase.setPath(dbpath)
 
 
     @staticmethod
     def clearTimezoneDatabase():
-        if PyCalendarTimezoneDatabase.sTimezoneDatabase is not None:
-            PyCalendarTimezoneDatabase.sTimezoneDatabase.clear()
+        if TimezoneDatabase.sTimezoneDatabase is not None:
+            TimezoneDatabase.sTimezoneDatabase.clear()
 
 
     def __init__(self):
-        from pycalendar.calendar import PyCalendar
+        from pycalendar.icalendar.calendar import Calendar
         self.dbpath = None
-        self.calendar = PyCalendar()
+        self.calendar = Calendar()
 
 
     def setPath(self, dbpath):
@@ -50,27 +50,27 @@
 
 
     def clear(self):
-        from pycalendar.calendar import PyCalendar
-        self.calendar = PyCalendar()
+        from pycalendar.icalendar.calendar import Calendar
+        self.calendar = Calendar()
 
 
     @staticmethod
     def getTimezoneDatabase():
-        if PyCalendarTimezoneDatabase.sTimezoneDatabase is None:
-            PyCalendarTimezoneDatabase.sTimezoneDatabase = PyCalendarTimezoneDatabase()
-        return PyCalendarTimezoneDatabase.sTimezoneDatabase
+        if TimezoneDatabase.sTimezoneDatabase is None:
+            TimezoneDatabase.sTimezoneDatabase = TimezoneDatabase()
+        return TimezoneDatabase.sTimezoneDatabase
 
 
     @staticmethod
     def getTimezone(tzid):
 
         # Check whether current cached
-        tzdb = PyCalendarTimezoneDatabase.getTimezoneDatabase()
+        tzdb = TimezoneDatabase.getTimezoneDatabase()
         tz = tzdb.calendar.getTimezone(tzid)
         if tz is None:
             try:
                 tzdb.cacheTimezone(tzid)
-            except PyCalendarNoTimezoneInDatabase:
+            except NoTimezoneInDatabase:
                 pass
             tz = tzdb.calendar.getTimezone(tzid)
 
@@ -83,10 +83,10 @@
         Return a VTIMEZONE inside a valid VCALENDAR
         """
 
-        tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
+        tz = TimezoneDatabase.getTimezone(tzid)
         if tz is not None:
-            from pycalendar.calendar import PyCalendar
-            cal = PyCalendar()
+            from pycalendar.icalendar.calendar import Calendar
+            cal = Calendar()
             cal.addComponent(tz.duplicate(cal))
             return cal
         else:
@@ -96,7 +96,7 @@
     @staticmethod
     def getTimezoneOffsetSeconds(tzid, dt):
         # Cache it first
-        tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
+        tz = TimezoneDatabase.getTimezone(tzid)
         if tz is not None:
             return tz.getTimezoneOffsetSeconds(dt)
         else:
@@ -106,7 +106,7 @@
     @staticmethod
     def getTimezoneDescriptor(tzid, dt):
         # Cache it first
-        tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
+        tz = TimezoneDatabase.getTimezone(tzid)
         if tz is not None:
             return tz.getTimezoneDescriptor(dt)
         else:
@@ -123,10 +123,10 @@
         if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath):
             try:
                 self.calendar.parseComponent(open(tzpath))
-            except (IOError, PyCalendarInvalidData):
-                raise PyCalendarNoTimezoneInDatabase(self.dbpath, tzid)
+            except (IOError, InvalidData):
+                raise NoTimezoneInDatabase(self.dbpath, tzid)
         else:
-            raise PyCalendarNoTimezoneInDatabase(self.dbpath, tzid)
+            raise NoTimezoneInDatabase(self.dbpath, tzid)
 
 
     def addTimezone(self, tz):
@@ -140,7 +140,7 @@
         Merge each timezone from other calendar.
         """
 
-        tzdb = PyCalendarTimezoneDatabase.getTimezoneDatabase()
+        tzdb = TimezoneDatabase.getTimezoneDatabase()
 
         # Not if our own calendar
         if cal is tzdb.calendar:

Modified: PyCalendar/branches/json-2/src/pycalendar/unknownvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/unknownvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/unknownvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,13 +16,13 @@
 
 # iCalendar Unknown value - one whose default type we don't know about
 
-from pycalendar import xmldefs
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.value import Value
 
-class PyCalendarUnknownValue(PyCalendarPlainTextValue):
+class UnknownValue(PlainTextValue):
 
     def getType(self):
-        return PyCalendarUnknownValue.VALUETYPE_UNKNOWN
+        return UnknownValue.VALUETYPE_UNKNOWN
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_UNKNOWN, PyCalendarUnknownValue, xmldefs.value_unknown)
+Value.registerType(Value.VALUETYPE_UNKNOWN, UnknownValue, xmldefinitions.value_unknown)

Modified: PyCalendar/branches/json-2/src/pycalendar/urivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/urivalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/urivalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,5 +1,5 @@
 ##
-#    Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License");
 #    you may not use this file except in compliance with the License.
@@ -16,18 +16,18 @@
 
 # iCalendar URI value
 
-from pycalendar import xmldefs, utils
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions, utils
+from pycalendar.plaintextvalue import PlainTextValue
+from pycalendar.value import Value
 from pycalendar.parser import ParserContext
 
-class PyCalendarURIValue(PyCalendarPlainTextValue):
+class URIValue(PlainTextValue):
 
     def getType(self):
-        return PyCalendarURIValue.VALUETYPE_URI
+        return URIValue.VALUETYPE_URI
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
 
         if ParserContext.BACKSLASH_IN_URI_VALUE == ParserContext.PARSER_FIX:
             # Decoding required
@@ -50,6 +50,6 @@
             except:
                 pass
         else:
-            super(PyCalendarURIValue, self).generate(os)
+            super(URIValue, self).generate(os)
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_URI, PyCalendarURIValue, xmldefs.value_uri)
+Value.registerType(Value.VALUETYPE_URI, URIValue, xmldefinitions.value_uri)

Modified: PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -17,27 +17,30 @@
 # iCalendar UTC Offset value
 
 from cStringIO import StringIO
-from pycalendar import xmldefs
-from pycalendar.value import PyCalendarValue
+from pycalendar import xmldefinitions
+from pycalendar.value import Value
 
-class PyCalendarUTCOffsetValue(PyCalendarValue):
+class UTCOffsetValue(Value):
 
     def __init__(self, value=0):
         self.mValue = value
 
 
     def duplicate(self):
-        return PyCalendarUTCOffsetValue(self.mValue)
+        return UTCOffsetValue(self.mValue)
 
 
     def getType(self):
-        return PyCalendarValue.VALUETYPE_UTC_OFFSET
+        return Value.VALUETYPE_UTC_OFFSET
 
 
-    def parse(self, data):
+    def parse(self, data, variant):
+
+        fullISO = (variant == "vcard")
+
         # Must be of specific lengths
         datalen = len(data)
-        if datalen not in (5, 7):
+        if datalen not in ((6, 8,) if fullISO else (5, 7,)):
             self.mValue = 0
             raise ValueError
 
@@ -50,12 +53,13 @@
         hours = int(data[1:3])
 
         # Get minutes
-        mins = int(data[3:5])
+        index = 4 if fullISO else 3
+        mins = int(data[index:index + 2])
 
         # Get seconds if present
         secs = 0
-        if datalen == 7 :
-            secs = int(data[5:])
+        if datalen > 6:
+            secs = int(data[index + 2:])
 
         self.mValue = ((hours * 60) + mins) * 60 + secs
         if not plus:
@@ -92,7 +96,7 @@
 
     def writeXML(self, node, namespace):
 
-        os = StringIO.StringIO()
+        os = StringIO()
         self.generate(os)
         text = os.getvalue()
         text = text[:-2] + ":" + text[-2:]
@@ -108,4 +112,4 @@
     def setValue(self, value):
         self.mValue = value
 
-PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_UTC_OFFSET, PyCalendarUTCOffsetValue, xmldefs.value_utc_offset)
+Value.registerType(Value.VALUETYPE_UTC_OFFSET, UTCOffsetValue, xmldefinitions.value_utc_offset)

Modified: PyCalendar/branches/json-2/src/pycalendar/utils.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/utils.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/utils.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -438,17 +438,17 @@
 
 # Display elements
 def getMonthTable(month, year, weekstart, table, today_index):
-    from pycalendar.datetime import PyCalendarDateTime
+    from pycalendar.datetime import DateTime
 
     # Get today
-    today = PyCalendarDateTime.getToday(None)
+    today = DateTime.getToday(None)
     today_index = [-1, -1]
 
     # Start with empty table
     table = []
 
     # Determine first weekday in month
-    temp = PyCalendarDateTime(year, month, 1, 0)
+    temp = DateTime(year, month, 1, 0)
     row = -1
     initial_col = temp.getDayOfWeek() - weekstart
     if initial_col < 0:

Deleted: PyCalendar/branches/json-2/src/pycalendar/valarm.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/valarm.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/valarm.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,705 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.component import PyCalendarComponent
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.duration import PyCalendarDuration
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
-
-class PyCalendarVAlarm(PyCalendarComponent):
-
-    sActionMap = {
-        definitions.cICalProperty_ACTION_AUDIO: definitions.eAction_VAlarm_Audio,
-        definitions.cICalProperty_ACTION_DISPLAY: definitions.eAction_VAlarm_Display,
-        definitions.cICalProperty_ACTION_EMAIL: definitions.eAction_VAlarm_Email,
-        definitions.cICalProperty_ACTION_PROCEDURE: definitions.eAction_VAlarm_Procedure,
-        definitions.cICalProperty_ACTION_URI: definitions.eAction_VAlarm_URI,
-        definitions.cICalProperty_ACTION_NONE: definitions.eAction_VAlarm_None,
-    }
-
-    sActionValueMap = {
-        definitions.eAction_VAlarm_Audio: definitions.cICalProperty_ACTION_AUDIO,
-        definitions.eAction_VAlarm_Display: definitions.cICalProperty_ACTION_DISPLAY,
-        definitions.eAction_VAlarm_Email: definitions.cICalProperty_ACTION_EMAIL,
-        definitions.eAction_VAlarm_Procedure: definitions.cICalProperty_ACTION_PROCEDURE,
-        definitions.eAction_VAlarm_URI: definitions.cICalProperty_ACTION_URI,
-        definitions.eAction_VAlarm_None: definitions.cICalProperty_ACTION_NONE,
-    }
-
-    # Classes for each action encapsulating action-specific data
-    class PyCalendarVAlarmAction(object):
-
-        propertyCardinality_1 = ()
-        propertyCardinality_1_Fix_Empty = ()
-        propertyCardinality_0_1 = ()
-        propertyCardinality_1_More = ()
-
-        def __init__(self, type):
-            self.mType = type
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmAction(self.mType)
-
-        def load(self, valarm):
-            pass
-
-        def add(self, valarm):
-            pass
-
-        def remove(self, valarm):
-            pass
-
-        def getType(self):
-            return self.mType
-
-
-    class PyCalendarVAlarmAudio(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-            definitions.cICalProperty_TRIGGER,
-        )
-
-        propertyCardinality_0_1 = (
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_REPEAT,
-            definitions.cICalProperty_ATTACH,
-            definitions.cICalProperty_ACKNOWLEDGED,
-        )
-
-        def __init__(self, speak=None):
-            super(PyCalendarVAlarm.PyCalendarVAlarmAudio, self).__init__(type=definitions.eAction_VAlarm_Audio)
-            self.mSpeakText = speak
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmAudio(self.mSpeakText)
-
-        def load(self, valarm):
-            # Get properties
-            self.mSpeakText = valarm.loadValueString(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
-
-        def add(self, valarm):
-            # Delete existing then add
-            self.remove(valarm)
-
-            prop = PyCalendarProperty(definitions.cICalProperty_ACTION_X_SPEAKTEXT, self.mSpeakText)
-            valarm.addProperty(prop)
-
-        def remove(self, valarm):
-            valarm.removeProperties(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
-
-        def isSpeakText(self):
-            return len(self.mSpeakText) != 0
-
-        def getSpeakText(self):
-            return self.mSpeakText
-
-
-    class PyCalendarVAlarmDisplay(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-            definitions.cICalProperty_TRIGGER,
-        )
-
-        propertyCardinality_1_Fix_Empty = (
-            definitions.cICalProperty_DESCRIPTION,
-        )
-
-        propertyCardinality_0_1 = (
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_REPEAT,
-            definitions.cICalProperty_ACKNOWLEDGED,
-        )
-
-        def __init__(self, description=None):
-            super(PyCalendarVAlarm.PyCalendarVAlarmDisplay, self).__init__(type=definitions.eAction_VAlarm_Display)
-            self.mDescription = description
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmDisplay(self.mDescription)
-
-        def load(self, valarm):
-            # Get properties
-            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
-
-        def add(self, valarm):
-            # Delete existing then add
-            self.remove(valarm)
-
-            prop = PyCalendarProperty(definitions.cICalProperty_DESCRIPTION, self.mDescription)
-            valarm.addProperty(prop)
-
-        def remove(self, valarm):
-            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
-
-        def getDescription(self):
-            return self.mDescription
-
-
-    class PyCalendarVAlarmEmail(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-            definitions.cICalProperty_TRIGGER,
-        )
-
-        propertyCardinality_1_Fix_Empty = (
-            definitions.cICalProperty_DESCRIPTION,
-            definitions.cICalProperty_SUMMARY,
-        )
-
-        propertyCardinality_0_1 = (
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_REPEAT,
-            definitions.cICalProperty_ACKNOWLEDGED,
-        )
-
-        propertyCardinality_1_More = (
-            definitions.cICalProperty_ATTENDEE,
-        )
-
-        def __init__(self, description=None, summary=None, attendees=None):
-            super(PyCalendarVAlarm.PyCalendarVAlarmEmail, self).__init__(type=definitions.eAction_VAlarm_Email)
-            self.mDescription = description
-            self.mSummary = summary
-            self.mAttendees = attendees
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmEmail(self.mDescription, self.mSummary, self.mAttendees)
-
-        def load(self, valarm):
-            # Get properties
-            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
-            self.mSummary = valarm.loadValueString(definitions.cICalProperty_SUMMARY)
-
-            self.mAttendees = []
-            if valarm.hasProperty(definitions.cICalProperty_ATTENDEE):
-                # Get each attendee
-                range = valarm.getProperties().get(definitions.cICalProperty_ATTENDEE, ())
-                for iter in range:
-                    # Get the attendee value
-                    attendee = iter.getCalAddressValue()
-                    if attendee is not None:
-                        self.mAttendees.append(attendee.getValue())
-
-        def add(self, valarm):
-            # Delete existing then add
-            self.remove(valarm)
-
-            prop = PyCalendarProperty(definitions.cICalProperty_DESCRIPTION, self.mDescription)
-            valarm.addProperty(prop)
-
-            prop = PyCalendarProperty(definitions.cICalProperty_SUMMARY, self.mSummary)
-            valarm.addProperty(prop)
-
-            for iter in self.mAttendees:
-                prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, iter, PyCalendarValue.VALUETYPE_CALADDRESS)
-                valarm.addProperty(prop)
-
-        def remove(self, valarm):
-            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
-            valarm.removeProperties(definitions.cICalProperty_SUMMARY)
-            valarm.removeProperties(definitions.cICalProperty_ATTENDEE)
-
-        def getDescription(self):
-            return self.mDescription
-
-        def getSummary(self):
-            return self.mSummary
-
-        def getAttendees(self):
-            return self.mAttendees
-
-
-    class PyCalendarVAlarmUnknown(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-            definitions.cICalProperty_TRIGGER,
-        )
-
-        propertyCardinality_0_1 = (
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_REPEAT,
-            definitions.cICalProperty_ACKNOWLEDGED,
-        )
-
-        def __init__(self):
-            super(PyCalendarVAlarm.PyCalendarVAlarmUnknown, self).__init__(type=definitions.eAction_VAlarm_Unknown)
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmUnknown()
-
-
-    class PyCalendarVAlarmURI(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-            definitions.cICalProperty_TRIGGER,
-            definitions.cICalProperty_URL,
-        )
-
-        propertyCardinality_0_1 = (
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_REPEAT,
-            definitions.cICalProperty_ACKNOWLEDGED,
-        )
-
-        def __init__(self, uri=None):
-            super(PyCalendarVAlarm.PyCalendarVAlarmURI, self).__init__(type=definitions.eAction_VAlarm_URI)
-            self.mURI = uri
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmURI(self.mURI)
-
-        def load(self, valarm):
-            # Get properties
-            self.mURI = valarm.loadValueString(definitions.cICalProperty_URL)
-
-        def add(self, valarm):
-            # Delete existing then add
-            self.remove(valarm)
-
-            prop = PyCalendarProperty(definitions.cICalProperty_URL, self.mURI)
-            valarm.addProperty(prop)
-
-        def remove(self, valarm):
-            valarm.removeProperties(definitions.cICalProperty_URL)
-
-        def getURI(self):
-            return self.mURI
-
-
-    class PyCalendarVAlarmNone(PyCalendarVAlarmAction):
-
-        propertyCardinality_1 = (
-            definitions.cICalProperty_ACTION,
-        )
-
-        def __init__(self):
-            super(PyCalendarVAlarm.PyCalendarVAlarmNone, self).__init__(type=definitions.eAction_VAlarm_None)
-
-        def duplicate(self):
-            return PyCalendarVAlarm.PyCalendarVAlarmNone()
-
-
-    def getMimeComponentName(self):
-        # Cannot be sent as a separate MIME object
-        return None
-
-    sActionToAlarmMap = {
-        definitions.eAction_VAlarm_Audio: PyCalendarVAlarmAudio,
-        definitions.eAction_VAlarm_Display: PyCalendarVAlarmDisplay,
-        definitions.eAction_VAlarm_Email: PyCalendarVAlarmEmail,
-        definitions.eAction_VAlarm_URI: PyCalendarVAlarmURI,
-        definitions.eAction_VAlarm_None: PyCalendarVAlarmNone,
-    }
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-
-        super(PyCalendarVAlarm, self).__init__(parent=parent)
-
-        self.mAction = definitions.eAction_VAlarm_Display
-        self.mTriggerAbsolute = False
-        self.mTriggerOnStart = True
-        self.mTriggerOn = PyCalendarDateTime()
-
-        # Set duration default to 1 hour
-        self.mTriggerBy = PyCalendarDuration()
-        self.mTriggerBy.setDuration(60 * 60)
-
-        # Does not repeat by default
-        self.mRepeats = 0
-        self.mRepeatInterval = PyCalendarDuration()
-        self.mRepeatInterval.setDuration(5 * 60) # Five minutes
-
-        # Status
-        self.mStatusInit = False
-        self.mAlarmStatus = definitions.eAlarm_Status_Pending
-        self.mLastTrigger = PyCalendarDateTime()
-        self.mNextTrigger = PyCalendarDateTime()
-        self.mDoneCount = 0
-
-        # Create action data
-        self.mActionData = PyCalendarVAlarm.PyCalendarVAlarmDisplay("")
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVAlarm, self).duplicate(parent=parent)
-        other.mAction = self.mAction
-        other.mTriggerAbsolute = self.mTriggerAbsolute
-        other.mTriggerOn = self.mTriggerOn.duplicate()
-        other.mTriggerBy = self.mTriggerBy.duplicate()
-        other.mTriggerOnStart = self.mTriggerOnStart
-
-        other.mRepeats = self.mRepeats
-        other.mRepeatInterval = self.mRepeatInterval.duplicate()
-
-        other.mAlarmStatus = self.mAlarmStatus
-        if self.mLastTrigger is not None:
-            other.mLastTrigger = self.mLastTrigger.duplicate()
-        if self.mNextTrigger is not None:
-            other.mNextTrigger = self.mNextTrigger.duplicate()
-        other.mDoneCount = self.mDoneCount
-
-        other.mActionData = self.mActionData.duplicate()
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VALARM
-
-
-    def getAction(self):
-        return self.mAction
-
-
-    def getActionData(self):
-        return self.mActionData
-
-
-    def isTriggerAbsolute(self):
-        return self.mTriggerAbsolute
-
-
-    def getTriggerOn(self):
-        return self.mTriggerOn
-
-
-    def getTriggerDuration(self):
-        return self.mTriggerBy
-
-
-    def isTriggerOnStart(self):
-        return self.mTriggerOnStart
-
-
-    def getRepeats(self):
-        return self.mRepeats
-
-
-    def getInterval(self):
-        return self.mRepeatInterval
-
-
-    def added(self):
-        # Added to calendar so add to calendar notifier
-        # calstore::CCalendarNotifier::sCalendarNotifier.AddAlarm(this)
-
-        # Do inherited
-        super(PyCalendarVAlarm, self).added()
-
-
-    def removed(self):
-        # Removed from calendar so add to calendar notifier
-        # calstore::CCalendarNotifier::sCalendarNotifier.RemoveAlarm(this)
-
-        # Do inherited
-        super(PyCalendarVAlarm, self).removed()
-
-
-    def changed(self):
-        # Always force recalc of trigger status
-        self.mStatusInit = False
-
-        # Changed in calendar so change in calendar notifier
-        # calstore::CCalendarNotifier::sCalendarNotifier.ChangedAlarm(this)
-
-        # Do not do inherited as this is always a sub-component and we do not
-        # do top-level component changes
-        # super.changed()
-
-
-    def finalise(self):
-        # Do inherited
-        super(PyCalendarVAlarm, self).finalise()
-
-        # Get the ACTION
-        temp = self.loadValueString(definitions.cICalProperty_ACTION)
-        if temp is not None:
-            self.mAction = PyCalendarVAlarm.sActionMap.get(temp, definitions.eAction_VAlarm_Unknown)
-            self.loadAction()
-
-        # Get the trigger
-        if self.hasProperty(definitions.cICalProperty_TRIGGER):
-            # Determine the type of the value
-            temp1 = self.loadValueDateTime(definitions.cICalProperty_TRIGGER)
-            temp2 = self.loadValueDuration(definitions.cICalProperty_TRIGGER)
-            if temp1 is not None:
-                self.mTriggerAbsolute = True
-                self.mTriggerOn = temp1
-            elif temp2 is not None:
-                self.mTriggerAbsolute = False
-                self.mTriggerBy = temp2
-
-                # Get the property
-                prop = self.findFirstProperty(definitions.cICalProperty_TRIGGER)
-
-                # Look for RELATED attribute
-                if prop.hasAttribute(definitions.cICalAttribute_RELATED):
-                    temp = prop.getAttributeValue(definitions.cICalAttribute_RELATED)
-                    if temp == definitions.cICalAttribute_RELATED_START:
-                        self.mTriggerOnStart = True
-                    elif temp == definitions.cICalAttribute_RELATED_END:
-                        self.mTriggerOnStart = False
-                else:
-                    self.mTriggerOnStart = True
-
-        # Get repeat & interval
-        temp = self.loadValueInteger(definitions.cICalProperty_REPEAT)
-        if temp is not None:
-            self.mRepeats = temp
-        temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
-        if temp is not None:
-            self.mRepeatInterval = temp
-
-        # Set a map key for sorting
-        self.mMapKey = "%s:%s" % (self.mAction, self.mTriggerOn if self.mTriggerAbsolute else self.mTriggerBy,)
-
-        # Alarm status - private to Mulberry
-        status = self.loadValueString(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
-        if status is not None:
-            if status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING:
-                self.mAlarmStatus = definitions.eAlarm_Status_Pending
-            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED:
-                self.mAlarmStatus = definitions.eAlarm_Status_Completed
-            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED:
-                self.mAlarmStatus = definitions.eAlarm_Status_Disabled
-            else:
-                self.mAlarmStatus = definitions.eAlarm_Status_Pending
-
-        # Last trigger time - private to Mulberry
-        temp = self.loadValueDateTime(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
-        if temp is not None:
-            self.mLastTrigger = temp
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        # Validate using action specific constraints
-        self.propertyCardinality_1 = self.mActionData.propertyCardinality_1
-        self.propertyCardinality_1_Fix_Empty = self.mActionData.propertyCardinality_1_Fix_Empty
-        self.propertyCardinality_0_1 = self.mActionData.propertyCardinality_0_1
-        self.propertyCardinality_1_More = self.mActionData.propertyCardinality_1_More
-
-        fixed, unfixed = super(PyCalendarVAlarm, self).validate(doFix)
-
-        # Extra constraint: both DURATION and REPEAT must be present togethe
-        if self.hasProperty(definitions.cICalProperty_DURATION) ^ self.hasProperty(definitions.cICalProperty_REPEAT):
-            # Cannot fix this
-            logProblem = "[%s] Properties must be present together: %s, %s" % (
-                self.getType(),
-                definitions.cICalProperty_DURATION,
-                definitions.cICalProperty_REPEAT,
-            )
-            unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    def editStatus(self, status):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
-
-        # Updated cached values
-        self.mAlarmStatus = status
-
-        # Add new
-        status_txt = ""
-        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
-            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
-        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
-            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
-        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
-            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status_txt))
-
-
-    def editAction(self, action, data):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_ACTION)
-        self.mActionData.remove(self)
-        self.mActionData = None
-
-        # Updated cached values
-        self.mAction = action
-        self.mActionData = data
-
-        # Add new properties to alarm
-        action_txt = PyCalendarVAlarm.sActionValueMap.get(self.mAction, definitions.cICalProperty_ACTION_PROCEDURE)
-
-        prop = PyCalendarProperty(definitions.cICalProperty_ACTION, action_txt)
-        self.addProperty(prop)
-
-        self.mActionData.add(self)
-
-
-    def editTriggerOn(self, dt):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_TRIGGER)
-
-        # Updated cached values
-        self.mTriggerAbsolute = True
-        self.mTriggerOn = dt
-
-        # Add new
-        prop = PyCalendarProperty(definitions.cICalProperty_TRIGGER, dt)
-        self.addProperty(prop)
-
-
-    def editTriggerBy(self, duration, trigger_start):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_TRIGGER)
-
-        # Updated cached values
-        self.mTriggerAbsolute = False
-        self.mTriggerBy = duration
-        self.mTriggerOnStart = trigger_start
-
-        # Add new (with attribute)
-        prop = PyCalendarProperty(definitions.cICalProperty_TRIGGER, duration)
-        attr = PyCalendarAttribute(definitions.cICalAttribute_RELATED,
-                 (definitions.cICalAttribute_RELATED_START,
-                  definitions.cICalAttribute_RELATED_END)[not trigger_start])
-        prop.addAttribute(attr)
-        self.addProperty(prop)
-
-
-    def editRepeats(self, repeat, interval):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_REPEAT)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-
-        # Updated cached values
-        self.mRepeats = repeat
-        self.mRepeatInterval = interval
-
-        # Add new
-        if self.mRepeats > 0:
-            self.addProperty(PyCalendarProperty(definitions.cICalProperty_REPEAT, repeat))
-            self.addProperty(PyCalendarProperty(definitions.cICalProperty_DURATION, interval))
-
-
-    def getAlarmStatus(self):
-        return self.mAlarmStatus
-
-
-    def getNextTrigger(self, dt):
-        if not self.mStatusInit:
-            self.initNextTrigger()
-        dt.copy(self.mNextTrigger)
-
-
-    def alarmTriggered(self, dt):
-        # Remove existing
-        self.removeProperties(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
-        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
-
-        # Updated cached values
-        self.mLastTrigger.copy(dt)
-
-        if self.mDoneCount < self.mRepeats:
-            self.mNextTrigger = self.mLastTrigger + self.mRepeatInterval
-            dt.copy(self.mNextTrigger)
-            self.mDoneCount += 1
-            self.mAlarmStatus = definitions.eAlarm_Status_Pending
-        else:
-            self.mAlarmStatus = definitions.eAlarm_Status_Completed
-
-        # Add new
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_LASTTRIGGER, dt))
-        status = ""
-        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
-            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
-        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
-            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
-        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
-            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
-        self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status))
-
-        # Now update dt to the next alarm time
-        return self.mAlarmStatus == definitions.eAlarm_Status_Pending
-
-
-    def loadAction(self):
-        # Delete current one
-        self.mActionData = None
-        self.mActionData = PyCalendarVAlarm.sActionToAlarmMap.get(self.mAction, PyCalendarVAlarm.PyCalendarVAlarmUnknown)()
-        self.mActionData.load(self)
-
-
-    def initNextTrigger(self):
-        # Do not bother if its completed
-        if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
-            return
-        self.mStatusInit = True
-
-        # Look for trigger immediately preceeding or equal to utc now
-        nowutc = PyCalendarDateTime.getNowUTC()
-
-        # Init done counter
-        self.mDoneCount = 0
-
-        # Determine the first trigger
-        trigger = PyCalendarDateTime()
-        self.getFirstTrigger(trigger)
-
-        while self.mDoneCount < self.mRepeats:
-            # See if next trigger is later than now
-            next_trigger = trigger + self.mRepeatInterval
-            if next_trigger > nowutc:
-                break
-            self.mDoneCount += 1
-            trigger = next_trigger
-
-        # Check for completion
-        if trigger == self.mLastTrigger or (nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
-            if self.mDoneCount == self.mRepeats:
-                self.mAlarmStatus = definitions.eAlarm_Status_Completed
-                return
-            else:
-                trigger = trigger + self.mRepeatInterval
-                self.mDoneCount += 1
-
-        self.mNextTrigger = trigger
-
-
-    def getFirstTrigger(self, dt):
-        # If absolute trigger, use that
-        if self.isTriggerAbsolute():
-            # Get the trigger on
-            dt.copy(self.getTriggerOn())
-        else:
-            # Get the parent embedder class (must be CICalendarComponentRecur type)
-            owner = self.getEmbedder()
-            if owner is not None:
-                # Determine time at which alarm will trigger
-                trigger = (owner.getStart(), owner.getEnd())[not self.isTriggerOnStart()]
-
-                # Offset by duration
-                dt.copy(trigger + self.getTriggerDuration())

Modified: PyCalendar/branches/json-2/src/pycalendar/validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/validation.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/validation.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
+from pycalendar.plaintextvalue import PlainTextValue
 
 # Grabbed from http://docs.python.org/library/functools.html since we need to support Python 2.5
 def partial(func, *args, **keywords):
@@ -35,7 +35,7 @@
     def stringValue(text, property):
 
         value = property.getValue()
-        if value and isinstance(value, PyCalendarPlainTextValue):
+        if value and isinstance(value, PlainTextValue):
             value = value.getValue()
             return value.lower() == text.lower()
 

Modified: PyCalendar/branches/json-2/src/pycalendar/validator.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/validator.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/validator.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,8 +15,8 @@
 #    limitations under the License.
 ##
 
-from pycalendar.calendar import PyCalendar
-from pycalendar.exceptions import PyCalendarError
+from pycalendar.icalendar.calendar import Calendar
+from pycalendar.exceptions import ErrorBase
 from pycalendar.parser import ParserContext
 from pycalendar.vcard.card import Card
 import os
@@ -33,14 +33,14 @@
 
     if data.find("BEGIN:VCALENDAR") != -1:
         try:
-            cal = PyCalendar.parseText(data)
-        except PyCalendarError, e:
+            cal = Calendar.parseText(data)
+        except ErrorBase, e:
             print "Failed to parse iCalendar: %r" % (e,)
             sys.exit(1)
     elif data.find("BEGIN:VCARD") != -1:
         try:
             cal = Card.parseText(data)
-        except PyCalendarError, e:
+        except ErrorBase, e:
             print "Failed to parse vCard: %r" % (e,)
             sys.exit(1)
     else:

Modified: PyCalendar/branches/json-2/src/pycalendar/value.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/value.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/value.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,11 +16,11 @@
 
 # ICalendar Value class
 
+from pycalendar import xmlutils
 from pycalendar.valueutils import ValueMixin
-from pycalendar import xmldefs
 import xml.etree.cElementTree as XML
 
-class PyCalendarValue(ValueMixin):
+class Value(ValueMixin):
 
     (
         VALUETYPE_ADR,
@@ -61,7 +61,7 @@
 
 
     def __eq__(self, other):
-        if not isinstance(other, PyCalendarValue):
+        if not isinstance(other, Value):
             return False
         return self.getType() == other.getType() and self.getValue() == other.getValue()
 
@@ -73,13 +73,13 @@
 
 
     @classmethod
-    def createFromType(clz, type):
-        # Create the type
-        created = clz._typeMap.get(type, None)
+    def createFromType(clz, value_type):
+        # Create the value type
+        created = clz._typeMap.get(value_type, None)
         if created:
             return created()
         else:
-            return clz._typeMap.get(PyCalendarValue.VALUETYPE_UNKNOWN)(type)
+            return clz._typeMap.get(Value.VALUETYPE_UNKNOWN)(value_type)
 
 
     def getType(self):
@@ -98,9 +98,13 @@
         raise NotImplementedError
 
 
+    def parse(self, data, variant):
+        raise NotImplementedError
+
+
     def writeXML(self, node, namespace):
         raise NotImplementedError
 
 
     def getXMLNode(self, node, namespace):
-        return XML.SubElement(node, xmldefs.makeTag(namespace, self._xmlMap[self.getType()]))
+        return XML.SubElement(node, xmlutils.makeTag(namespace, self._xmlMap[self.getType()]))

Modified: PyCalendar/branches/json-2/src/pycalendar/valueutils.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/valueutils.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/valueutils.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -19,6 +19,9 @@
 from cStringIO import StringIO
 
 class ValueMixin(object):
+    """
+    Mix-in for operations common to Value's and value-specific classes.
+    """
 
     def __str__(self):
         return self.getText()
@@ -47,3 +50,38 @@
 
     def writeXML(self, node, namespace):
         raise NotImplementedError
+
+
+
+class WrapperValue(object):
+    """
+    Mix-in for Value derived classes that wrap a value-specific class.
+    """
+
+    def duplicate(self):
+        return self.__class__(self.mValue.duplicate())
+
+
+    def getType(self):
+        raise NotImplementedError
+
+
+    def parse(self, data, variant):
+        self.mValue.parse(data)
+
+
+    def generate(self, os):
+        self.mValue.generate(os)
+
+
+    def writeXML(self, node, namespace):
+        value = self.getXMLNode(node, namespace)
+        value.text = self.mValue.writeXML()
+
+
+    def getValue(self):
+        return self.mValue
+
+
+    def setValue(self, value):
+        self.mValue = value

Deleted: PyCalendar/branches/json-2/src/pycalendar/vavailability.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vavailability.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vavailability.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,107 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import itipdefinitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-
-class PyCalendarVAvailability(PyCalendarComponent):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_BUSYTYPE,
-        definitions.cICalProperty_CLASS,
-        definitions.cICalProperty_CREATED,
-        definitions.cICalProperty_DESCRIPTION,
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_ORGANIZER,
-        definitions.cICalProperty_SEQUENCE,
-        definitions.cICalProperty_SUMMARY,
-        definitions.cICalProperty_URL,
-        definitions.cICalProperty_RECURRENCE_ID,
-        definitions.cICalProperty_DTEND,
-        definitions.cICalProperty_DURATION,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarVAvailability, self).__init__(parent=parent)
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarVAvailability, self).duplicate(parent=parent)
-
-
-    def getType(self):
-        return definitions.cICalComponent_VAVAILABILITY
-
-
-    def getMimeComponentName(self):
-        return itipdefinitions.cICalMIMEComponent_VAVAILABILITY
-
-
-    def finalise(self):
-        super(PyCalendarVAvailability, self).finalise()
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        fixed, unfixed = super(PyCalendarVAvailability, self).validate(doFix)
-
-        # Extra constraint: only one of DTEND or DURATION
-        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
-            # Fix by removing the DTEND
-            logProblem = "[%s] Properties must not both be present: %s, %s" % (
-                self.getType(),
-                definitions.cICalProperty_DTEND,
-                definitions.cICalProperty_DURATION,
-            )
-            if doFix:
-                self.removeProperties(definitions.cICalProperty_DTEND)
-                fixed.append(logProblem)
-            else:
-                unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    def addComponent(self, comp):
-        # We can embed the available components only
-        if comp.getType() == definitions.cICalComponent_AVAILABLE:
-            super(PyCalendarVAvailability, self).addComponent(comp)
-        else:
-            raise ValueError
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_DTSTART,
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_DTEND,
-        )

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/adr.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,127 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard ADR value
+
+from pycalendar import utils
+from pycalendar.valueutils import ValueMixin
+
+class Adr(ValueMixin):
+    """
+    mValue is a tuple of seven str or tuples of str
+    """
+
+    (
+        POBOX,
+        EXTENDED,
+        STREET,
+        LOCALITY,
+        REGION,
+        POSTALCODE,
+        COUNTRY,
+        MAXITEMS
+    ) = range(8)
+
+    def __init__(self, pobox="", extended="", street="", locality="", region="", postalcode="", country=""):
+        self.mValue = (pobox, extended, street, locality, region, postalcode, country)
+
+
+    def duplicate(self):
+        return Adr(*self.mValue)
+
+
+    def __hash__(self):
+        return hash(self.mValue)
+
+
+    def __repr__(self):
+        return "ADR %s" % (self.getText(),)
+
+
+    def __eq__(self, comp):
+        return self.mValue == comp.mValue
+
+
+    def getPobox(self):
+        return self.mValue[Adr.POBOX]
+
+
+    def setPobox(self, value):
+        self.mValue[Adr.POBOX] = value
+
+
+    def getExtended(self):
+        return self.mValue[Adr.EXTENDED]
+
+
+    def setExtended(self, value):
+        self.mValue[Adr.EXTENDED] = value
+
+
+    def getStreet(self):
+        return self.mValue[Adr.STREET]
+
+
+    def setStreet(self, value):
+        self.mValue[Adr.STREET] = value
+
+
+    def getLocality(self):
+        return self.mValue[Adr.LOCALITY]
+
+
+    def setLocality(self, value):
+        self.mValue[Adr.LOCALITY] = value
+
+
+    def getRegion(self):
+        return self.mValue[Adr.REGION]
+
+
+    def setRegion(self, value):
+        self.mValue[Adr.REGION] = value
+
+
+    def getPostalCode(self):
+        return self.mValue[Adr.POSTALCODE]
+
+
+    def setPostalCode(self, value):
+        self.mValue[Adr.POSTALCODE] = value
+
+
+    def getCountry(self):
+        return self.mValue[Adr.COUNTRY]
+
+
+    def setCountry(self, value):
+        self.mValue[Adr.COUNTRY] = value
+
+
+    def parse(self, data):
+        self.mValue = utils.parseDoubleNestedList(data, Adr.MAXITEMS)
+
+
+    def generate(self, os):
+        utils.generateDoubleNestedList(os, self.mValue)
+
+
+    def getValue(self):
+        return self.mValue
+
+
+    def setValue(self, value):
+        self.mValue = value

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/adrvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,32 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard ADR value
+
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
+from pycalendar.vcard.adr import Adr
+
+class AdrValue(WrapperValue, Value):
+
+    def __init__(self, value=None):
+        self.mValue = value if value is not None else Adr()
+
+
+    def getType(self):
+        return Value.VALUETYPE_ADR
+
+Value.registerType(Value.VALUETYPE_ADR, AdrValue, None)

Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/card.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/card.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,9 +15,9 @@
 ##
 
 from cStringIO import StringIO
-from pycalendar.componentbase import PyCalendarComponentBase
-from pycalendar.exceptions import PyCalendarInvalidData, \
-    PyCalendarValidationError
+from pycalendar.componentbase import ComponentBase
+from pycalendar.exceptions import InvalidData, \
+    ValidationError
 from pycalendar.parser import ParserContext
 from pycalendar.utils import readFoldedLine
 from pycalendar.vcard import definitions
@@ -26,11 +26,14 @@
 from pycalendar.vcard.property import Property
 from pycalendar.vcard.validation import VCARD_VALUE_CHECKS
 
-class Card(PyCalendarComponentBase):
+class Card(ComponentBase):
 
     sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
     sDomain = "mulberrymail.com"
 
+    sComponentType = None
+    sPropertyType = Property
+
     @staticmethod
     def setPRODID(prodid):
         Card.sProdID = prodid
@@ -88,7 +91,7 @@
         # Optional raise behavior
         fixed, unfixed = super(Card, self).validate(doFix)
         if doRaise and unfixed:
-            raise PyCalendarValidationError(";".join(unfixed))
+            raise ValidationError(";".join(unfixed))
         return fixed, unfixed
 
 
@@ -129,11 +132,11 @@
                 elif len(line) == 0:
                     # Raise if requested, otherwise just ignore
                     if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("vCard data has blank lines")
+                        raise InvalidData("vCard data has blank lines")
 
                 # Unrecognized data
                 else:
-                    raise PyCalendarInvalidData("vCard data not recognized", line)
+                    raise InvalidData("vCard data not recognized", line)
 
             elif state == GET_PROPERTY:
 
@@ -145,7 +148,7 @@
 
                     # Validate some things
                     if not card.hasProperty("VERSION"):
-                        raise PyCalendarInvalidData("vCard missing VERSION", "")
+                        raise InvalidData("vCard missing VERSION", "")
 
                     results.append(card)
 
@@ -157,24 +160,24 @@
                 elif len(line) == 0:
                     # Raise if requested, otherwise just ignore
                     if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("vCard data has blank lines")
+                        raise InvalidData("vCard data has blank lines")
 
                 # Must be a property
                 else:
 
-                    # Parse attribute/value for top-level calendar item
+                    # Parse parameter/value for top-level calendar item
                     prop = Property()
                     if prop.parse(line):
 
                         # Check for valid property
                         if not card.validProperty(prop):
-                            raise PyCalendarInvalidData("Invalid property", str(prop))
+                            raise InvalidData("Invalid property", str(prop))
                         else:
                             card.addProperty(prop)
 
         # Check for truncated data
         if state != LOOK_FOR_VCARD:
-            raise PyCalendarInvalidData("vCard data not complete")
+            raise InvalidData("vCard data not complete")
 
         return results
 
@@ -220,11 +223,11 @@
                 elif len(line) == 0:
                     # Raise if requested, otherwise just ignore
                     if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("vCard data has blank lines")
+                        raise InvalidData("vCard data has blank lines")
 
                 # Unrecognized data
                 else:
-                    raise PyCalendarInvalidData("vCard data not recognized", line)
+                    raise InvalidData("vCard data not recognized", line)
 
             elif state == GET_PROPERTY:
 
@@ -241,19 +244,19 @@
                 elif len(line) == 0:
                     # Raise if requested, otherwise just ignore
                     if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
-                        raise PyCalendarInvalidData("vCard data has blank lines")
+                        raise InvalidData("vCard data has blank lines")
 
                 # Must be a property
                 else:
 
-                    # Parse attribute/value for top-level calendar item
+                    # Parse parameter/value for top-level calendar item
                     prop = Property()
                     try:
                         if prop.parse(line):
 
                             # Check for valid property
                             if not self.validProperty(prop):
-                                raise PyCalendarInvalidData("Invalid property", str(prop))
+                                raise InvalidData("Invalid property", str(prop))
                             else:
                                 self.addProperty(prop)
                     except IndexError:
@@ -261,11 +264,11 @@
 
         # Check for truncated data
         if state != LOOK_FOR_VCARD:
-            raise PyCalendarInvalidData("vCard data not complete", "")
+            raise InvalidData("vCard data not complete", "")
 
         # Validate some things
         if result and not self.hasProperty("VERSION"):
-            raise PyCalendarInvalidData("vCard missing VERSION", "")
+            raise InvalidData("vCard missing VERSION", "")
 
         return result
 

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/n.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/n.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/n.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,124 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard ADR value
+
+from pycalendar import utils
+from pycalendar.valueutils import ValueMixin
+
+class N(ValueMixin):
+    """
+    mValue is a tuple of seven str or tuples of str
+    """
+
+    (
+        LAST,
+        FIRST,
+        MIDDLE,
+        PREFIX,
+        SUFFIX,
+        MAXITEMS
+    ) = range(6)
+
+    def __init__(self, last="", first="", middle="", prefix="", suffix=""):
+        self.mValue = (last, first, middle, prefix, suffix)
+
+
+    def duplicate(self):
+        return N(*self.mValue)
+
+
+    def __hash__(self):
+        return hash(self.mValue)
+
+
+    def __repr__(self):
+        return "N %s" % (self.getText(),)
+
+
+    def __eq__(self, comp):
+        return self.mValue == comp.mValue
+
+
+    def getFirst(self):
+        return self.mValue[N.FIRST]
+
+
+    def setFirst(self, value):
+        self.mValue[N.FIRST] = value
+
+
+    def getLast(self):
+        return self.mValue[N.LAST]
+
+
+    def setLast(self, value):
+        self.mValue[N.LAST] = value
+
+
+    def getMiddle(self):
+        return self.mValue[N.MIDDLE]
+
+
+    def setMiddle(self, value):
+        self.mValue[N.MIDDLE] = value
+
+
+    def getPrefix(self):
+        return self.mValue[N.PREFIX]
+
+
+    def setPrefix(self, value):
+        self.mValue[N.PREFIX] = value
+
+
+    def getSuffix(self):
+        return self.mValue[N.SUFFIX]
+
+
+    def setSuffix(self, value):
+        self.mValue[N.SUFFIX] = value
+
+
+    def getFullName(self):
+
+
+        def _stringOrList(item):
+            return item if isinstance(item, basestring) else " ".join(item)
+
+        results = []
+        for i in (N.PREFIX, N.FIRST, N.MIDDLE, N.LAST, N.SUFFIX):
+            result = _stringOrList(self.mValue[i])
+            if result:
+                results.append(result)
+
+        return " ".join(results)
+
+
+    def parse(self, data):
+        self.mValue = utils.parseDoubleNestedList(data, N.MAXITEMS)
+
+
+    def generate(self, os):
+        utils.generateDoubleNestedList(os, self.mValue)
+
+
+    def getValue(self):
+        return self.mValue
+
+
+    def setValue(self, value):
+        self.mValue = value

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/nvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,32 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard N value
+
+from pycalendar.value import Value
+from pycalendar.valueutils import WrapperValue
+from pycalendar.vcard.n import N
+
+class NValue(WrapperValue, Value):
+
+    def __init__(self, value=None):
+        self.mValue = value if value is not None else N()
+
+
+    def getType(self):
+        return Value.VALUETYPE_N
+
+Value.registerType(Value.VALUETYPE_N, NValue, None)

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/orgvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,54 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard ORG value
+
+from pycalendar import utils
+from pycalendar.value import Value
+
+class OrgValue(Value):
+    """
+    mValue is a str or tuple of str
+    """
+
+    def __init__(self, value=None):
+        self.mValue = value
+
+
+    def duplicate(self):
+        return OrgValue(self.mValue)
+
+
+    def getType(self):
+        return Value.VALUETYPE_ORG
+
+
+    def parse(self, data, variant="vcard"):
+        self.mValue = utils.parseTextList(data, ';')
+
+
+    def generate(self, os):
+        utils.generateTextList(os, self.mValue, ';')
+
+
+    def getValue(self):
+        return self.mValue
+
+
+    def setValue(self, value):
+        self.mValue = value
+
+Value.registerType(Value.VALUETYPE_ORG, OrgValue, None)

Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/property.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,113 +15,109 @@
 ##
 
 from pycalendar import stringutils
-from pycalendar.adr import Adr
-from pycalendar.adrvalue import AdrValue
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.datetimevalue import PyCalendarDateTimeValue
-from pycalendar.exceptions import PyCalendarInvalidProperty
-from pycalendar.integervalue import PyCalendarIntegerValue
-from pycalendar.multivalue import PyCalendarMultiValue
-from pycalendar.n import N
-from pycalendar.nvalue import NValue
-from pycalendar.orgvalue import OrgValue
+from pycalendar.parameter import Parameter
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidProperty
 from pycalendar.parser import ParserContext
-from pycalendar.plaintextvalue import PyCalendarPlainTextValue
-from pycalendar.unknownvalue import PyCalendarUnknownValue
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
+from pycalendar.property import PropertyBase
+from pycalendar.utcoffsetvalue import UTCOffsetValue
 from pycalendar.utils import decodeParameterValue
-from pycalendar.value import PyCalendarValue
+from pycalendar.value import Value
 from pycalendar.vcard import definitions
+from pycalendar.vcard.adr import Adr
+from pycalendar.vcard.adrvalue import AdrValue
+from pycalendar.vcard.n import N
+from pycalendar.vcard.nvalue import NValue
+from pycalendar.vcard.orgvalue import OrgValue
 import cStringIO as StringIO
 
 handleOptions = ("allow", "ignore", "fix", "raise")
 missingParameterValues = "fix"
 
-class Property(object):
+class Property(PropertyBase):
 
     sDefaultValueTypeMap = {
 
         #     2425 Properties
-        definitions.Property_SOURCE  : PyCalendarValue.VALUETYPE_URI,
-        definitions.Property_NAME    : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_PROFILE : PyCalendarValue.VALUETYPE_TEXT,
+        definitions.Property_SOURCE  : Value.VALUETYPE_URI,
+        definitions.Property_NAME    : Value.VALUETYPE_TEXT,
+        definitions.Property_PROFILE : Value.VALUETYPE_TEXT,
 
         #     2426 vCard Properties
 
         #     2426 Section 3.1
-        definitions.Property_FN       : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_N        : PyCalendarValue.VALUETYPE_N,
-        definitions.Property_NICKNAME : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_PHOTO    : PyCalendarValue.VALUETYPE_BINARY,
-        definitions.Property_BDAY     : PyCalendarValue.VALUETYPE_DATE,
+        definitions.Property_FN       : Value.VALUETYPE_TEXT,
+        definitions.Property_N        : Value.VALUETYPE_N,
+        definitions.Property_NICKNAME : Value.VALUETYPE_TEXT,
+        definitions.Property_PHOTO    : Value.VALUETYPE_BINARY,
+        definitions.Property_BDAY     : Value.VALUETYPE_DATE,
 
         #     2426 Section 3.2
-        definitions.Property_ADR   : PyCalendarValue.VALUETYPE_ADR,
-        definitions.Property_LABEL : PyCalendarValue.VALUETYPE_TEXT,
+        definitions.Property_ADR   : Value.VALUETYPE_ADR,
+        definitions.Property_LABEL : Value.VALUETYPE_TEXT,
 
         #     2426 Section 3.3
-        definitions.Property_TEL    : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_EMAIL  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_MAILER : PyCalendarValue.VALUETYPE_TEXT,
+        definitions.Property_TEL    : Value.VALUETYPE_TEXT,
+        definitions.Property_EMAIL  : Value.VALUETYPE_TEXT,
+        definitions.Property_MAILER : Value.VALUETYPE_TEXT,
 
         #     2426 Section 3.4
-        definitions.Property_TZ  : PyCalendarValue.VALUETYPE_UTC_OFFSET,
-        definitions.Property_GEO : PyCalendarValue.VALUETYPE_GEO,
+        definitions.Property_TZ  : Value.VALUETYPE_UTC_OFFSET,
+        definitions.Property_GEO : Value.VALUETYPE_GEO,
 
         #     2426 Section 3.5
-        definitions.Property_TITLE : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_ROLE  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_LOGO  : PyCalendarValue.VALUETYPE_BINARY,
-        definitions.Property_AGENT : PyCalendarValue.VALUETYPE_VCARD,
-        definitions.Property_ORG   : PyCalendarValue.VALUETYPE_ORG,
+        definitions.Property_TITLE : Value.VALUETYPE_TEXT,
+        definitions.Property_ROLE  : Value.VALUETYPE_TEXT,
+        definitions.Property_LOGO  : Value.VALUETYPE_BINARY,
+        definitions.Property_AGENT : Value.VALUETYPE_VCARD,
+        definitions.Property_ORG   : Value.VALUETYPE_ORG,
 
         #     2426 Section 3.6
-        definitions.Property_CATEGORIES  : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_NOTE        : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_PRODID      : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_REV         : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.Property_SORT_STRING : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_SOUND       : PyCalendarValue.VALUETYPE_BINARY,
-        definitions.Property_UID         : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_URL         : PyCalendarValue.VALUETYPE_URI,
-        definitions.Property_VERSION     : PyCalendarValue.VALUETYPE_TEXT,
+        definitions.Property_CATEGORIES  : Value.VALUETYPE_TEXT,
+        definitions.Property_NOTE        : Value.VALUETYPE_TEXT,
+        definitions.Property_PRODID      : Value.VALUETYPE_TEXT,
+        definitions.Property_REV         : Value.VALUETYPE_DATETIME,
+        definitions.Property_SORT_STRING : Value.VALUETYPE_TEXT,
+        definitions.Property_SOUND       : Value.VALUETYPE_BINARY,
+        definitions.Property_UID         : Value.VALUETYPE_TEXT,
+        definitions.Property_URL         : Value.VALUETYPE_URI,
+        definitions.Property_VERSION     : Value.VALUETYPE_TEXT,
 
         #     2426 Section 3.7
-        definitions.Property_CLASS       : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Property_KEY         : PyCalendarValue.VALUETYPE_BINARY,
+        definitions.Property_CLASS       : Value.VALUETYPE_TEXT,
+        definitions.Property_KEY         : Value.VALUETYPE_BINARY,
     }
 
     sValueTypeMap = {
-        definitions.Value_BINARY      : PyCalendarValue.VALUETYPE_BINARY,
-        definitions.Value_BOOLEAN     : PyCalendarValue.VALUETYPE_BOOLEAN,
-        definitions.Value_DATE        : PyCalendarValue.VALUETYPE_DATE,
-        definitions.Value_DATE_TIME   : PyCalendarValue.VALUETYPE_DATETIME,
-        definitions.Value_FLOAT       : PyCalendarValue.VALUETYPE_FLOAT,
-        definitions.Value_INTEGER     : PyCalendarValue.VALUETYPE_INTEGER,
-        definitions.Value_TEXT        : PyCalendarValue.VALUETYPE_TEXT,
-        definitions.Value_TIME        : PyCalendarValue.VALUETYPE_TIME,
-        definitions.Value_URI         : PyCalendarValue.VALUETYPE_URI,
-        definitions.Value_UTCOFFSET   : PyCalendarValue.VALUETYPE_UTC_OFFSET,
-        definitions.Value_VCARD       : PyCalendarValue.VALUETYPE_VCARD,
+        definitions.Value_BINARY      : Value.VALUETYPE_BINARY,
+        definitions.Value_BOOLEAN     : Value.VALUETYPE_BOOLEAN,
+        definitions.Value_DATE        : Value.VALUETYPE_DATE,
+        definitions.Value_DATE_TIME   : Value.VALUETYPE_DATETIME,
+        definitions.Value_FLOAT       : Value.VALUETYPE_FLOAT,
+        definitions.Value_INTEGER     : Value.VALUETYPE_INTEGER,
+        definitions.Value_TEXT        : Value.VALUETYPE_TEXT,
+        definitions.Value_TIME        : Value.VALUETYPE_TIME,
+        definitions.Value_URI         : Value.VALUETYPE_URI,
+        definitions.Value_UTCOFFSET   : Value.VALUETYPE_UTC_OFFSET,
+        definitions.Value_VCARD       : Value.VALUETYPE_VCARD,
     }
 
     sTypeValueMap = {
-        PyCalendarValue.VALUETYPE_ADR        : definitions.Value_TEXT,
-        PyCalendarValue.VALUETYPE_BINARY     : definitions.Value_BINARY,
-        PyCalendarValue.VALUETYPE_BOOLEAN    : definitions.Value_BOOLEAN,
-        PyCalendarValue.VALUETYPE_DATE       : definitions.Value_DATE,
-        PyCalendarValue.VALUETYPE_DATETIME   : definitions.Value_DATE_TIME,
-        PyCalendarValue.VALUETYPE_FLOAT      : definitions.Value_FLOAT,
-        PyCalendarValue.VALUETYPE_GEO        : definitions.Value_FLOAT,
-        PyCalendarValue.VALUETYPE_INTEGER    : definitions.Value_INTEGER,
-        PyCalendarValue.VALUETYPE_N          : definitions.Value_TEXT,
-        PyCalendarValue.VALUETYPE_ORG        : definitions.Value_TEXT,
-        PyCalendarValue.VALUETYPE_TEXT       : definitions.Value_TEXT,
-        PyCalendarValue.VALUETYPE_TIME       : definitions.Value_TIME,
-        PyCalendarValue.VALUETYPE_URI        : definitions.Value_URI,
-        PyCalendarValue.VALUETYPE_UTC_OFFSET : definitions.Value_UTCOFFSET,
-        PyCalendarValue.VALUETYPE_VCARD      : definitions.Value_VCARD,
+        Value.VALUETYPE_ADR        : definitions.Value_TEXT,
+        Value.VALUETYPE_BINARY     : definitions.Value_BINARY,
+        Value.VALUETYPE_BOOLEAN    : definitions.Value_BOOLEAN,
+        Value.VALUETYPE_DATE       : definitions.Value_DATE,
+        Value.VALUETYPE_DATETIME   : definitions.Value_DATE_TIME,
+        Value.VALUETYPE_FLOAT      : definitions.Value_FLOAT,
+        Value.VALUETYPE_GEO        : definitions.Value_FLOAT,
+        Value.VALUETYPE_INTEGER    : definitions.Value_INTEGER,
+        Value.VALUETYPE_N          : definitions.Value_TEXT,
+        Value.VALUETYPE_ORG        : definitions.Value_TEXT,
+        Value.VALUETYPE_TEXT       : definitions.Value_TEXT,
+        Value.VALUETYPE_TIME       : definitions.Value_TIME,
+        Value.VALUETYPE_URI        : definitions.Value_URI,
+        Value.VALUETYPE_UTC_OFFSET : definitions.Value_UTCOFFSET,
+        Value.VALUETYPE_VCARD      : definitions.Value_VCARD,
     }
 
     sMultiValues = set((
@@ -135,20 +131,33 @@
         definitions.Property_ORG,
     ))
 
+    sUsesGroup = True
+
+    sVariant = "vcard"
+
+    sValue = definitions.Parameter_VALUE
+    sText = definitions.Value_TEXT
+
+    @classmethod
+    def registerDefaultValue(cls, propname, valuetype):
+        if propname not in cls.sDefaultValueTypeMap:
+            cls.sDefaultValueTypeMap[propname] = valuetype
+
+
     def __init__(self, group=None, name=None, value=None, valuetype=None):
-        self._init_PyCalendarProperty()
 
         self.mGroup = group
-
         self.mName = name if name is not None else ""
+        self.mParameters = {}
+        self.mValue = None
 
         if isinstance(value, int):
             self._init_attr_value_int(value)
 
         elif isinstance(value, str):
-            self._init_attr_value_text(value, valuetype if valuetype else Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT))
+            self._init_attr_value_text(value, valuetype if valuetype else self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_TEXT))
 
-        elif isinstance(value, PyCalendarDateTime):
+        elif isinstance(value, DateTime):
             self._init_attr_value_datetime(value)
 
         elif isinstance(value, Adr):
@@ -166,14 +175,14 @@
                 # Assume everything else is a text list
                 self._init_attr_value_text_list(value)
 
-        elif isinstance(value, PyCalendarUTCOffsetValue):
+        elif isinstance(value, UTCOffsetValue):
             self._init_attr_value_utcoffset(value)
 
 
     def duplicate(self):
         other = Property(self.mGroup, self.mName)
-        for attrname, attrs in self.mAttributes.items():
-            other.mAttributes[attrname] = [i.duplicate() for i in attrs]
+        for attrname, attrs in self.mParameters.items():
+            other.mParameters[attrname] = [i.duplicate() for i in attrs]
         other.mValue = self.mValue.duplicate()
 
         return other
@@ -181,16 +190,13 @@
 
     def __hash__(self):
         return hash((
+            self.mGroup,
             self.mName,
-            tuple([tuple(self.mAttributes[attrname]) for attrname in sorted(self.mAttributes.keys())]),
+            tuple([tuple(self.mParameters[attrname]) for attrname in sorted(self.mParameters.keys())]),
             self.mValue,
         ))
 
 
-    def __ne__(self, other):
-        return not self.__eq__(other)
-
-
     def __eq__(self, other):
         if not isinstance(other, Property):
             return False
@@ -198,89 +204,20 @@
             self.mGroup == self.mGroup and
             self.mName == other.mName and
             self.mValue == other.mValue and
-            self.mAttributes == other.mAttributes
+            self.mParameters == other.mParameters
         )
 
 
-    def __repr__(self):
-        return "vCard Property: %s" % (self.getText(),)
+    def parseParameters(self, txt, data):
+        """
+        Parse parameters, return string point at value.
+        """
 
-
-    def __str__(self):
-        return self.getText()
-
-
-    def getGroup(self):
-        return self.mGroup
-
-
-    def setGroup(self, group):
-        self.mGroup = group
-
-
-    def getName(self):
-        return self.mName
-
-
-    def setName(self, name):
-        self.mName = name
-
-
-    def getAttributes(self):
-        return self.mAttributes
-
-
-    def setAttributes(self, attributes):
-        self.mAttributes = dict([(k.upper(), v) for k, v in attributes.iteritems()])
-
-
-    def hasAttribute(self, attr):
-        return attr.upper() in self.mAttributes
-
-
-    def getAttributeValue(self, attr):
-        return self.mAttributes[attr.upper()][0].getFirstValue()
-
-
-    def addAttribute(self, attr):
-        self.mAttributes.setdefault(attr.getName().upper(), []).append(attr)
-
-
-    def replaceAttribute(self, attr):
-        self.mAttributes[attr.getName().upper()] = [attr]
-
-
-    def removeAttributes(self, attr):
-        if attr.upper() in self.mAttributes:
-            del self.mAttributes[attr.upper()]
-
-
-    def getValue(self):
-        return self.mValue
-
-
-    def parse(self, data):
-        # Look for attribute or value delimiter
-        prop_name, txt = stringutils.strduptokenstr(data, ";:")
-        if not prop_name:
-            raise PyCalendarInvalidProperty("Invalid property", data)
-
-        # Check for group prefix
-        splits = prop_name.split(".", 1)
-        if len(splits) == 2:
-            # We have both group and name
-            self.mGroup = splits[0]
-            self.mName = splits[1]
-        else:
-            # We have the name
-            self.mName = prop_name
-
-        # Now loop getting data
         try:
             stripValueSpaces = False    # Fix for AB.app base PHOTO properties that use two spaces at start of line
             while txt:
                 if txt[0] == ';':
-                    # Parse attribute
+                    # Parse parameter
 
                     # Move past delimiter
                     txt = txt[1:]
@@ -288,99 +225,72 @@
                     # Get quoted string or token - in iCalendar we only look for "=" here
                     # but for "broken" vCard BASE64 property we need to also terminate on
                     # ":;"
-                    attribute_name, txt = stringutils.strduptokenstr(txt, "=:;")
-                    if attribute_name is None:
-                        raise PyCalendarInvalidProperty("Invalid property", data)
+                    parameter_name, txt = stringutils.strduptokenstr(txt, "=:;")
+                    if parameter_name is None:
+                        raise InvalidProperty("Invalid property", data)
 
                     if txt[0] != "=":
                         # Deal with parameters without values
                         if ParserContext.VCARD_2_NO_PARAMETER_VALUES == ParserContext.PARSER_RAISE:
-                            raise PyCalendarInvalidProperty("Invalid property parameter", data)
+                            raise InvalidProperty("Invalid property parameter", data)
                         elif ParserContext.VCARD_2_NO_PARAMETER_VALUES == ParserContext.PARSER_ALLOW:
-                            attribute_value = None
+                            parameter_value = None
                         else: # PARSER_IGNORE and PARSER_FIX
-                            attribute_name = None
+                            parameter_name = None
 
-                        if attribute_name.upper() == "BASE64" and ParserContext.VCARD_2_BASE64 == ParserContext.PARSER_FIX:
-                            attribute_name = definitions.Parameter_ENCODING
-                            attribute_value = definitions.Parameter_Value_ENCODING_B
+                        if parameter_name.upper() == "BASE64" and ParserContext.VCARD_2_BASE64 == ParserContext.PARSER_FIX:
+                            parameter_name = definitions.Parameter_ENCODING
+                            parameter_value = definitions.Parameter_Value_ENCODING_B
                             stripValueSpaces = True
                     else:
                         txt = txt[1:]
-                        attribute_value, txt = stringutils.strduptokenstr(txt, ":;,")
-                        if attribute_value is None:
-                            raise PyCalendarInvalidProperty("Invalid property", data)
+                        parameter_value, txt = stringutils.strduptokenstr(txt, ":;,")
+                        if parameter_value is None:
+                            raise InvalidProperty("Invalid property", data)
 
-                    # Now add attribute value (decode ^-escaping)
-                    if attribute_name is not None:
-                        attrvalue = PyCalendarAttribute(name=attribute_name, value=decodeParameterValue(attribute_value))
-                        self.mAttributes.setdefault(attribute_name.upper(), []).append(attrvalue)
+                    # Now add parameter value (decode ^-escaping)
+                    if parameter_name is not None:
+                        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:]
-                        attribute_value2, txt = stringutils.strduptokenstr(txt, ":;,")
-                        if attribute_value2 is None:
-                            raise PyCalendarInvalidProperty("Invalid property", data)
-                        attrvalue.addValue(decodeParameterValue(attribute_value2))
+                        parameter_value2, txt = stringutils.strduptokenstr(txt, ":;,")
+                        if parameter_value2 is None:
+                            raise InvalidProperty("Invalid property", data)
+                        attrvalue.addValue(decodeParameterValue(parameter_value2))
                 elif txt[0] == ':':
                     txt = txt[1:]
                     if stripValueSpaces:
                         txt = txt.replace(" ", "")
-                    self.createValue(txt)
-                    txt = None
+                    return txt
 
         except IndexError:
-            raise PyCalendarInvalidProperty("Invalid property", data)
+            raise InvalidProperty("Invalid property", data)
 
-        # We must have a value of some kind
-        if self.mValue is None:
-            raise PyCalendarInvalidProperty("Invalid property", data)
 
-        return True
-
-
-    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.setupValueAttribute()
+        # Special case AB.app PHOTO values
+        if self.mName.upper() == "PHOTO" and self.mValue.getType() == Value.VALUETYPE_BINARY:
+            self.setupValueParameter()
 
-        # Must write to temp buffer and then wrap
-        sout = StringIO.StringIO()
-        if self.mGroup:
-            sout.write(self.mGroup + ".")
-        sout.write(self.mName)
+            # Must write to temp buffer and then wrap
+            sout = StringIO.StringIO()
+            if self.mGroup:
+                sout.write(self.mGroup + ".")
+            sout.write(self.mName)
 
-        # Write all attributes
-        for key in sorted(self.mAttributes.keys()):
-            for attr in self.mAttributes[key]:
-                sout.write(";")
-                attr.generate(sout)
+            # 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.mName.upper() == "PHOTO" and self.mValue.getType() == PyCalendarValue.VALUETYPE_BINARY:
-            # Handle AB.app PHOTO values
+            # Write value
+            sout.write(":")
             sout.write("\r\n")
 
             value = self.mValue.getText()
@@ -395,179 +305,31 @@
             sout.write(" ")
             sout.write(value[offset:])
             os.write(sout.getvalue())
+            os.write("\r\n")
         else:
-            if self.mValue and not novalue:
-                self.mValue.generate(sout)
+            super(Property, self).generateValue(os, novalue)
 
-            # 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 _init_PyCalendarProperty(self):
-        self.mGroup = None
-        self.mName = ""
-        self.mAttributes = {}
-        self.mValue = None
-
-
-    def createValue(self, data):
-        # Tidy first
-        self.mValue = None
-
-        # Get value type from property name
-        valueType = Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT)
-
-        # Check whether custom value is set
-        if definitions.Parameter_VALUE in self.mAttributes:
-            attr = self.getAttributeValue(definitions.Parameter_VALUE)
-            if attr != definitions.Value_TEXT or self.mName.upper() not in Property.sTextVariants:
-                valueType = Property.sValueTypeMap.get(attr, valueType)
-
-        # Check for multivalued
-        if self.mName.upper() in Property.sMultiValues:
-            self.mValue = PyCalendarMultiValue(valueType)
-        else:
-            # Create the type
-            self.mValue = PyCalendarValue.createFromType(valueType)
-
-        # Now parse the data
-        try:
-            if valueType in (PyCalendarValue.VALUETYPE_DATE, PyCalendarValue.VALUETYPE_DATETIME):
-                # vCard supports a slightly different, expanded form, of date
-                self.mValue.parse(data, fullISO=True)
-            else:
-                self.mValue.parse(data)
-        except ValueError:
-            raise PyCalendarInvalidProperty("Invalid property value", data)
-
-
-    def setValue(self, value):
-        # Tidy first
-        self.mValue = None
-
-        # Get value type from property name
-        valueType = Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarUnknownValue)
-
-        # Check whether custom value is set
-        if definitions.Parameter_VALUE in self.mAttributes:
-            attr = self.getAttributeValue(definitions.Parameter_VALUE)
-            if attr != definitions.Value_TEXT or self.mName.upper() not in Property.sTextVariants:
-                valueType = Property.sValueTypeMap.get(attr, valueType)
-
-        # Check for multivalued
-        if self.mName.upper() in Property.sMultiValues:
-            self.mValue = PyCalendarMultiValue(valueType)
-        else:
-            # Create the type
-            self.mValue = PyCalendarValue.createFromType(valueType)
-
-        self.mValue.setValue(value)
-
-
-    def setupValueAttribute(self):
-        if definitions.Parameter_VALUE in self.mAttributes:
-            del self.mAttributes[definitions.Parameter_VALUE]
-
-        # 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.
-        default_type = Property.sDefaultValueTypeMap.get(self.mName.upper())
-        actual_type = self.mValue.getType()
-        if default_type is None or default_type != actual_type:
-            actual_value = self.sTypeValueMap.get(actual_type)
-            if actual_value is not None and (default_type is not None or actual_type != PyCalendarValue.VALUETYPE_TEXT):
-                self.mAttributes.setdefault(definitions.Parameter_VALUE, []).append(PyCalendarAttribute(name=definitions.Parameter_VALUE, value=actual_value))
-
-
     # Creation
-    def _init_attr_value_int(self, ival):
-        # Value
-        self.mValue = PyCalendarIntegerValue(value=ival)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_text(self, txt, value_type):
-        # Value
-        self.mValue = PyCalendarValue.createFromType(value_type)
-        if isinstance(self.mValue, PyCalendarPlainTextValue) or isinstance(self.mValue, PyCalendarUnknownValue):
-            self.mValue.setValue(txt)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
     def _init_attr_value_adr(self, reqstatus):
         # Value
         self.mValue = AdrValue(reqstatus)
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()
 
 
     def _init_attr_value_n(self, reqstatus):
         # Value
         self.mValue = NValue(reqstatus)
 
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()
 
 
     def _init_attr_value_org(self, reqstatus):
         # Value
         self.mValue = OrgValue(reqstatus)
 
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_datetime(self, dt):
-        # Value
-        self.mValue = PyCalendarDateTimeValue(value=dt)
-
-        # Attributes
-        self.setupValueAttribute()
-
-
-    def _init_attr_value_utcoffset(self, utcoffset):
-        # Value
-        self.mValue = PyCalendarUTCOffsetValue()
-        self.mValue.setValue(utcoffset.getValue())
-
-        # Attributes
-        self.setupValueAttribute()
+        # Parameters
+        self.setupValueParameter()

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adr.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_adr.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adr.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adr.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,50 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.vcard.adr import Adr
+import unittest
+
+class TestAdrValue(unittest.TestCase):
+
+    def testInit(self):
+        data = (
+            (
+                ("pobox", "extended", "street", "locality", "region", "postalcode", "country"),
+                "pobox;extended;street;locality;region;postalcode;country",
+            ),
+            (
+                (("pobox",), ("extended",), ("street1", "street2",), "locality", "region", (), "country"),
+                "pobox;extended;street1,street2;locality;region;;country",
+            ),
+        )
+
+        for args, result in data:
+            a = Adr(*args)
+
+            self.assertEqual(
+                a.getValue(),
+                args,
+            )
+
+            self.assertEqual(
+                a.getText(),
+                result,
+            )
+
+            self.assertEqual(
+                a.duplicate().getText(),
+                result,
+            )

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_adrvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_adrvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,62 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.vcard.adrvalue import AdrValue
+from pycalendar.vcard.property import Property
+import unittest
+
+class TestAdrValue(unittest.TestCase):
+
+    def testParseValue(self):
+
+        items = (
+            ("", ";;;;;;"),
+            (";", ";;;;;;"),
+            (";;;;;;", ";;;;;;"),
+            (";;123 Main Street;Any Town;CA;91921-1234", ";;123 Main Street;Any Town;CA;91921-1234;"),
+            (";;;;;;USA", ";;;;;;USA"),
+            ("POB1", "POB1;;;;;;"),
+            (";EXT", ";EXT;;;;;"),
+            (";;123 Main Street,The Cards;Any Town;CA;91921-1234", ";;123 Main Street,The Cards;Any Town;CA;91921-1234;"),
+            (";;123 Main\, Street,The Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The Cards;Any Town;CA;91921-1234;"),
+            (";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234;"),
+        )
+
+        for item, result in items:
+            req = AdrValue()
+            req.parse(item, "vcard")
+            test = req.getText()
+            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
+
+
+    def testParseProperty(self):
+
+        items = (
+            ("ADR:", "ADR:;;;;;;"),
+            ("ADR:;", "ADR:;;;;;;"),
+            ("ADR:;;;;;;", "ADR:;;;;;;"),
+            ("ADR:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
+            ("ADR:;;;;;;USA", "ADR:;;;;;;USA"),
+            ("ADR:POB1", "ADR:POB1;;;;;;"),
+            ("ADR:;EXT", "ADR:;EXT;;;;;"),
+            ("ADR;VALUE=TEXT:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
+        )
+
+        for item, result in items:
+            prop = Property()
+            prop.parse(item)
+            test = prop.getText()
+            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_card.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_card.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_card.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-from pycalendar.exceptions import PyCalendarInvalidData
+from pycalendar.exceptions import InvalidData
 from pycalendar.parser import ParserContext
 from pycalendar.vcard.card import Card
 from pycalendar.vcard.property import Property
@@ -383,7 +383,7 @@
 REV:20110323T202004Z
 TEL;type=WORK;type=pref:555-1212
 TEL;type=HOME:532-1234
-X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43:ABPerson
+X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
 END:VCARD
 """.replace("\n", "\r\n")
 
@@ -473,7 +473,7 @@
         )
 
         for item in data:
-            self.assertRaises(PyCalendarInvalidData, Card.parseText, item)
+            self.assertRaises(InvalidData, Card.parseText, item)
 
 
     def testParseBlank(self):
@@ -561,7 +561,7 @@
         save = ParserContext.BLANK_LINES_IN_DATA
         for item in data:
             ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
-            self.assertRaises(PyCalendarInvalidData, Card.parseText, item)
+            self.assertRaises(InvalidData, Card.parseText, item)
 
             ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
             lines = item.split("\r\n")

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_n.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_n.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_n.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_n.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,92 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.vcard.n import N
+import unittest
+
+class TestAdrValue(unittest.TestCase):
+
+    def testInit(self):
+
+        data = (
+            (
+                ("last", "first", "middle", "prefix", "suffix"),
+                "last;first;middle;prefix;suffix",
+                "prefix first middle last suffix",
+            ),
+            (
+                ("last", ("first",), ("middle1", "middle2",), (), ("suffix",)),
+                "last;first;middle1,middle2;;suffix",
+                "first middle1 middle2 last suffix",
+            ),
+        )
+
+        for args, result, fullName in data:
+            n = N(*args)
+
+            self.assertEqual(
+                n.getValue(),
+                args,
+            )
+
+            self.assertEqual(
+                n.getText(),
+                result,
+            )
+
+            self.assertEqual(
+                n.getFullName(),
+                fullName,
+            )
+
+            self.assertEqual(
+                n.duplicate().getText(),
+                result,
+            )
+
+
+    def testInitWithKeywords(self):
+
+        data = (
+            (
+                {"first": "first", "last": "last", "middle": "middle", "prefix": "prefix", "suffix": "suffix"},
+                "last;first;middle;prefix;suffix",
+                "prefix first middle last suffix",
+            ),
+            (
+                {"first": ("first",), "last": "last", "middle": ("middle1", "middle2",), "prefix": (), "suffix": ("suffix",)},
+                "last;first;middle1,middle2;;suffix",
+                "first middle1 middle2 last suffix",
+            ),
+        )
+
+        for kwargs, result, fullName in data:
+            n = N(**kwargs)
+
+            self.assertEqual(
+                n.getText(),
+                result,
+            )
+
+            self.assertEqual(
+                n.getFullName(),
+                fullName,
+            )
+
+            self.assertEqual(
+                n.duplicate().getText(),
+                result,
+            )

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_nvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_nvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,59 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.vcard.nvalue import NValue
+from pycalendar.vcard.property import Property
+import unittest
+
+class TestNValue(unittest.TestCase):
+
+    def testParseValue(self):
+
+        items = (
+            ("", ";;;;"),
+            (";", ";;;;"),
+            (";;;;", ";;;;"),
+            ("Cyrus;Daboo;;Dr.", "Cyrus;Daboo;;Dr.;"),
+            (";;;;PhD.", ";;;;PhD."),
+            ("Cyrus", "Cyrus;;;;"),
+            (";Daboo", ";Daboo;;;"),
+        )
+
+        for item, result in items:
+            req = NValue()
+            req.parse(item, "vcard")
+            test = req.getText()
+            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
+
+
+    def testParseProperty(self):
+
+        items = (
+            ("N:", "N:;;;;"),
+            ("N:;", "N:;;;;"),
+            ("N:;;;;", "N:;;;;"),
+            ("N:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
+            ("N:;;;;PhD.", "N:;;;;PhD."),
+            ("N:Cyrus", "N:Cyrus;;;;"),
+            ("N:;Daboo", "N:;Daboo;;;"),
+            ("N;VALUE=TEXT:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
+        )
+
+        for item, result in items:
+            prop = Property()
+            prop.parse(item)
+            test = prop.getText()
+            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Copied: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py (from rev 11593, PyCalendar/branches/json-2/src/pycalendar/tests/test_orgvalue.py)
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_orgvalue.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,53 @@
+##
+#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+from pycalendar.vcard.orgvalue import OrgValue
+from pycalendar.vcard.property import Property
+import unittest
+
+class TestNValue(unittest.TestCase):
+
+    def testParseValue(self):
+
+        items = (
+            ("", ""),
+            ("Example", "Example"),
+            ("Example\, Inc.", "Example\, Inc."),
+            ("Example\; Inc;Dept. of Silly Walks", "Example\; Inc;Dept. of Silly Walks"),
+        )
+
+        for item, result in items:
+            req = OrgValue()
+            req.parse(item, "vcard")
+            test = req.getText()
+            self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
+
+
+    def testParseProperty(self):
+
+        items = (
+            ("ORG:", "ORG:"),
+            ("ORG:Example", "ORG:Example"),
+            ("ORG:Example\, Inc.", "ORG:Example\, Inc."),
+            ("ORG:Example\; Inc;Dept. of Silly Walks", "ORG:Example\; Inc;Dept. of Silly Walks"),
+            ("ORG;VALUE=TEXT:Example", "ORG:Example"),
+        )
+
+        for item, result in items:
+            prop = Property()
+            prop.parse(item)
+            test = prop.getText()
+            self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))

Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_property.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,8 +14,8 @@
 #    limitations under the License.
 ##
 
-from pycalendar.attribute import PyCalendarAttribute
-from pycalendar.exceptions import PyCalendarInvalidProperty
+from pycalendar.parameter import Parameter
+from pycalendar.exceptions import InvalidProperty
 from pycalendar.parser import ParserContext
 from pycalendar.vcard.property import Property
 import unittest
@@ -34,6 +34,8 @@
         "item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;CA;11111;USA",
         "X-Test:Some\, text.",
         "X-Test;VALUE=URI:geio:123.123,123.123",
+        "X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\\:ABPerson",
+        "X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43:ABPerson",
     )
 
     def testParseGenerate(self):
@@ -65,7 +67,7 @@
         for data in test_bad_data:
             ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
             prop = Property()
-            self.assertRaises(PyCalendarInvalidProperty, prop.parse, data)
+            self.assertRaises(InvalidProperty, prop.parse, data)
         ParserContext.INVALID_ESCAPE_SEQUENCES = save
 
 
@@ -98,19 +100,19 @@
     def testParameterEncodingDecoding(self):
 
         prop = Property(name="X-FOO", value="Test")
-        prop.addAttribute(PyCalendarAttribute("X-BAR", "\"Check\""))
+        prop.addParameter(Parameter("X-BAR", "\"Check\""))
         self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^':Test\r\n")
 
-        prop.addAttribute(PyCalendarAttribute("X-BAR2", "Check\nThis\tOut\n"))
+        prop.addParameter(Parameter("X-BAR2", "Check\nThis\tOut\n"))
         self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
 
         data = "X-FOO;X-BAR=^'Check^':Test"
         prop = Property()
         prop.parse(data)
-        self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
+        self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
 
         data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
         prop = Property()
         prop.parse(data)
-        self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
-        self.assertEqual(prop.getAttributeValue("X-BAR2"), "Check\nThis\tOut\n")
+        self.assertEqual(prop.getParameterValue("X-BAR"), "\"Check\"")
+        self.assertEqual(prop.getParameterValue("X-BAR2"), "Check\nThis\tOut\n")

Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_validation.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_validation.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/tests/test_validation.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,7 +14,7 @@
 #    limitations under the License.
 ##
 
-from pycalendar.exceptions import PyCalendarValidationError
+from pycalendar.exceptions import ValidationError
 from pycalendar.vcard.card import Card
 import unittest
 
@@ -210,7 +210,7 @@
         for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
             card = Card.parseText(test_old)
             if test_raises:
-                self.assertRaises(PyCalendarValidationError, card.validate, doFix=False, doRaise=True)
+                self.assertRaises(ValidationError, card.validate, doFix=False, doRaise=True)
             else:
                 try:
                     fixed, unfixed = card.validate(doFix=False, doRaise=False)

Added: PyCalendar/branches/json-2/src/pycalendar/vcard/xmldefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/xmldefinitions.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/xmldefinitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,19 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# vCard XML definitions
+
+vCard40_namespace = "urn:ietf:params:xml:ns:vcard-4.0"

Deleted: PyCalendar/branches/json-2/src/pycalendar/vevent.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vevent.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vevent.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,169 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import itipdefinitions
-from pycalendar.componentrecur import PyCalendarComponentRecur
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.property import PyCalendarProperty
-
-class PyCalendarVEvent(PyCalendarComponentRecur):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CLASS,
-        definitions.cICalProperty_CREATED,
-        definitions.cICalProperty_DESCRIPTION,
-        definitions.cICalProperty_GEO,
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_LOCATION,
-        definitions.cICalProperty_ORGANIZER,
-        definitions.cICalProperty_PRIORITY,
-        definitions.cICalProperty_SEQUENCE,
-        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
-        definitions.cICalProperty_SUMMARY,
-        definitions.cICalProperty_TRANSP,
-        definitions.cICalProperty_URL,
-        definitions.cICalProperty_RECURRENCE_ID,
-        definitions.cICalProperty_RRULE,
-        definitions.cICalProperty_DTEND,
-        definitions.cICalProperty_DURATION,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarVEvent, self).__init__(parent=parent)
-        self.mStatus = definitions.eStatus_VEvent_None
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVEvent, self).duplicate(parent=parent)
-        other.mStatus = self.mStatus
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VEVENT
-
-
-    def getMimeComponentName(self):
-        return itipdefinitions.cICalMIMEComponent_VEVENT
-
-
-    def addComponent(self, comp):
-        # We can embed the alarm components only
-        if comp.getType() == definitions.cICalComponent_VALARM:
-            super(PyCalendarVEvent, self).addComponent(comp)
-        else:
-            raise ValueError
-
-
-    def getStatus(self):
-        return self.mStatus
-
-
-    def setStatus(self, status):
-        self.mStatus = status
-
-
-    def finalise(self):
-        # Do inherited
-        super(PyCalendarVEvent, self).finalise()
-
-        temp = self.loadValueString(definitions.cICalProperty_STATUS)
-        if temp is not None:
-            if temp == definitions.cICalProperty_STATUS_TENTATIVE:
-                self.mStatus = definitions.eStatus_VEvent_Tentative
-            elif temp == definitions.cICalProperty_STATUS_CONFIRMED:
-                self.mStatus = definitions.eStatus_VEvent_Confirmed
-            elif temp == definitions.cICalProperty_STATUS_CANCELLED:
-                self.mStatus = definitions.eStatus_VEvent_Cancelled
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        fixed, unfixed = super(PyCalendarVEvent, self).validate(doFix)
-
-        # Extra constraint: if METHOD not present, DTSTART must be
-        if self.mParentComponent and not self.mParentComponent.hasProperty(definitions.cICalProperty_METHOD):
-            if not self.hasProperty(definitions.cICalProperty_DTSTART):
-                # Cannot fix a missing required property
-                logProblem = "[%s] Missing required property: %s" % (self.getType(), definitions.cICalProperty_DTSTART,)
-                unfixed.append(logProblem)
-
-        # Extra constraint: only one of DTEND or DURATION
-        if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
-            # Fix by removing the DTEND
-            logProblem = "[%s] Properties must not both be present: %s, %s" % (
-                self.getType(),
-                definitions.cICalProperty_DTEND,
-                definitions.cICalProperty_DURATION,
-            )
-            if doFix:
-                self.removeProperties(definitions.cICalProperty_DTEND)
-                fixed.append(logProblem)
-            else:
-                unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    # Editing
-    def editStatus(self, status):
-        # Only if it is different
-        if self.mStatus != status:
-            # Updated cached values
-            self.mStatus = status
-
-            # Remove existing STATUS items
-            self.removeProperties(definitions.cICalProperty_STATUS)
-
-            # Now create properties
-            value = None
-            if status == definitions.eStatus_VEvent_None:
-                pass
-            elif status == definitions.eStatus_VEvent_Tentative:
-                value = definitions.cICalProperty_STATUS_TENTATIVE
-            elif status == definitions.eStatus_VEvent_Confirmed:
-                value = definitions.cICalProperty_STATUS_CONFIRMED
-            elif status == definitions.eStatus_VEvent_Cancelled:
-                value = definitions.cICalProperty_STATUS_CANCELLED
-            else:
-                pass
-
-            if value is not None:
-                prop = PyCalendarProperty(definitions.cICalProperty_STATUS, value)
-                self.addProperty(prop)
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_RECURRENCE_ID,
-            definitions.cICalProperty_DTSTART,
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_DTEND,
-        )

Deleted: PyCalendar/branches/json-2/src/pycalendar/vfreebusy.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vfreebusy.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vfreebusy.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,345 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import itipdefinitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.freebusy import PyCalendarFreeBusy
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.periodvalue import PyCalendarPeriodValue
-from pycalendar.property import PyCalendarProperty
-from pycalendar.value import PyCalendarValue
-
-class PyCalendarVFreeBusy(PyCalendarComponent):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CONTACT,
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_DTEND,
-        definitions.cICalProperty_ORGANIZER,
-        definitions.cICalProperty_URL,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarVFreeBusy, self).__init__(parent=parent)
-        self.mStart = PyCalendarDateTime()
-        self.mHasStart = False
-        self.mEnd = PyCalendarDateTime()
-        self.mHasEnd = False
-        self.mDuration = False
-        self.mCachedBusyTime = False
-        self.mSpanPeriod = None
-        self.mBusyTime = None
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVFreeBusy, self).duplicate(parent=parent)
-        other.mStart = self.mStart.duplicate()
-        other.mHasStart = self.mHasStart
-        other.mEnd = self.mEnd.duplicate()
-        other.mHasEnd = self.mHasEnd
-        other.mDuration = self.mDuration
-        other.mCachedBusyTime = False
-        other.mBusyTime = None
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VFREEBUSY
-
-
-    def getMimeComponentName(self):
-        return itipdefinitions.cICalMIMEComponent_VFREEBUSY
-
-
-    def finalise(self):
-        # Do inherited
-        super(PyCalendarVFreeBusy, self).finalise()
-
-        # Get DTSTART
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
-        self.mHasStart = temp is not None
-        if self.mHasStart:
-            self.mStart = temp
-
-        # Get DTEND
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
-        if temp is None:
-            # Try DURATION instead
-            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
-            if temp is not None:
-                self.mEnd = self.mStart + temp
-                self.mDuration = True
-            else:
-                # Force end to start, which will then be fixed to sensible
-                # value later
-                self.mEnd = self.mStart
-        else:
-            self.mHasEnd = True
-            self.mDuration = False
-            self.mEnd = temp
-
-
-    def fixStartEnd(self):
-        # End is always greater than start if start exists
-        if self.mHasStart and self.mEnd <= self.mStart:
-            # Use the start
-            self.mEnd = self.mStart.duplicate()
-            self.mDuration = False
-
-            # Adjust to appropiate non-inclusive end point
-            if self.mStart.isDateOnly():
-                self.mEnd.offsetDay(1)
-
-                # For all day events it makes sense to use duration
-                self.mDuration = True
-            else:
-                # Use end of current day
-                self.mEnd.offsetDay(1)
-                self.mEnd.setHHMMSS(0, 0, 0)
-
-
-    def getStart(self):
-        return self.mStart
-
-
-    def hasStart(self):
-        return self.mHasStart
-
-
-    def getEnd(self):
-        return self.mEnd
-
-
-    def hasEnd(self):
-        return self.mHasEnd
-
-
-    def useDuration(self):
-        return self.mDuration
-
-
-    def getSpanPeriod(self):
-        return self.mSpanPeriod
-
-
-    def getBusyTime(self):
-        return self.mBusyTime
-
-
-    def editTiming(self):
-        # Updated cached values
-        self.mHasStart = False
-        self.mHasEnd = False
-        self.mDuration = False
-        self.mStart.setToday()
-        self.mEnd.setToday()
-
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-
-
-    def editTimingStartEnd(self, start, end):
-        # Updated cached values
-        self.mHasStart = self.mHasEnd = True
-        self.mStart = start
-        self.mEnd = end
-        self.mDuration = False
-        self.fixStartEnd()
-
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-
-        # Now create properties
-        prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
-        self.addProperty(prop)
-
-        # If its an all day event and the end one day after the start, ignore it
-        temp = start.duplicate()
-        temp.offsetDay(1)
-        if not start.isDateOnly() or end != temp:
-            prop = PyCalendarProperty(definitions.cICalProperty_DTEND, end)
-            self.addProperty(prop)
-
-
-    def editTimingStartDuration(self, start, duration):
-        # Updated cached values
-        self.mHasStart = True
-        self.mHasEnd = False
-        self.mStart = start
-        self.mEnd = start + duration
-        self.mDuration = True
-
-        # Remove existing DTSTART & DTEND & DURATION & DUE items
-        self.removeProperties(definitions.cICalProperty_DTSTART)
-        self.removeProperties(definitions.cICalProperty_DTEND)
-        self.removeProperties(definitions.cICalProperty_DURATION)
-        self.removeProperties(definitions.cICalProperty_DUE)
-
-        # Now create properties
-        prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
-        self.addProperty(prop)
-
-        # If its an all day event and the duration is one day, ignore it
-        if (not start.isDateOnly() or (duration.getWeeks() != 0)
-                or (duration.getDays() > 1)):
-            prop = PyCalendarProperty(definitions.cICalProperty_DURATION, duration)
-            self.addProperty(prop)
-
-
-    # Generating info
-    def expandPeriodComp(self, period, list):
-        # Cache the busy-time details if not done already
-        if not self.mCachedBusyTime:
-            self.cacheBusyTime()
-
-        # See if period intersects the busy time span range
-        if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
-            list.append(self)
-
-
-    def expandPeriodFB(self, period, list):
-        # Cache the busy-time details if not done already
-        if not self.mCachedBusyTime:
-            self.cacheBusyTime()
-
-        # See if period intersects the busy time span range
-        if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
-            for fb in self.mBusyTime:
-                list.append(PyCalendarFreeBusy(fb))
-
-
-    def cacheBusyTime(self):
-
-        # Clear out any existing cache
-        self.mBusyTime = []
-
-        # Get all FREEBUSY items and add those that are BUSY
-        min_start = PyCalendarDateTime()
-        max_end = PyCalendarDateTime()
-        props = self.getProperties()
-        result = props.get(definitions.cICalProperty_FREEBUSY, ())
-        for iter in result:
-
-            # Check the properties FBTYPE attribute
-            type = 0
-            is_busy = False
-            if iter.hasAttribute(definitions.cICalAttribute_FBTYPE):
-
-                fbyype = iter.getAttributeValue(definitions.cICalAttribute_FBTYPE)
-                if fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSY:
-
-                    is_busy = True
-                    type = PyCalendarFreeBusy.BUSY
-
-                elif fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSYUNAVAILABLE:
-
-                    is_busy = True
-                    type = PyCalendarFreeBusy.BUSYUNAVAILABLE
-
-                elif fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSYTENTATIVE:
-
-                    is_busy = True
-                    type = PyCalendarFreeBusy.BUSYTENTATIVE
-
-                else:
-
-                    is_busy = False
-                    type = PyCalendarFreeBusy.FREE
-
-            else:
-
-                # Default is busy when no attribute
-                is_busy = True
-                type = PyCalendarFreeBusy.BUSY
-
-            # Add this period
-            if is_busy:
-
-                multi = iter.getMultiValue()
-                if (multi is not None) and (multi.getType() == PyCalendarValue.VALUETYPE_PERIOD):
-
-                    for o in multi.getValues():
-
-                        # Double-check type
-                        period = None
-                        if isinstance(o, PyCalendarPeriodValue):
-                            period = o
-
-                        # Double-check type
-                        if period is not None:
-
-                            self.mBusyTime.append(PyCalendarFreeBusy(type, period.getValue()))
-
-                            if len(self.mBusyTime) == 1:
-
-                                min_start = period.getValue().getStart()
-                                max_end = period.getValue().getEnd()
-
-                            else:
-
-                                if min_start > period.getValue().getStart():
-                                    min_start = period.getValue().getStart()
-                                if max_end < period.getValue().getEnd():
-                                    max_end = period.getValue().getEnd()
-
-        # If nothing present, empty the list
-        if len(self.mBusyTime) == 0:
-
-            self.mBusyTime = None
-
-        else:
-
-            # Sort the list by period
-            self.mBusyTime.sort(cmp=lambda x, y: x.getPeriod().getStart().compareDateTime(y.getPeriod().getStart()))
-
-            # Determine range
-            start = PyCalendarDateTime()
-            end = PyCalendarDateTime()
-            if self.mHasStart:
-                start = self.mStart
-            else:
-                start = min_start
-            if self.mHasEnd:
-                end = self.mEnd
-            else:
-                end = max_end
-
-            self.mSpanPeriod = PyCalendarPeriod(start, end)
-
-        self.mCachedBusyTime = True
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_DTSTART,
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_DTEND,
-        )

Deleted: PyCalendar/branches/json-2/src/pycalendar/vjournal.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vjournal.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vjournal.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,70 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import itipdefinitions
-from pycalendar.componentrecur import PyCalendarComponentRecur
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-
-class PyCalendarVJournal(PyCalendarComponentRecur):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CLASS,
-        definitions.cICalProperty_CREATED,
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_ORGANIZER,
-        definitions.cICalProperty_RECURRENCE_ID,
-        definitions.cICalProperty_SEQUENCE,
-        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
-        definitions.cICalProperty_SUMMARY,
-        definitions.cICalProperty_URL,
-        definitions.cICalProperty_RRULE,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarVJournal, self).__init__(parent=parent)
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarVJournal, self).duplicate(parent=parent)
-
-
-    def getType(self):
-        return definitions.cICalComponent_VJOURNAL
-
-
-    def getMimeComponentName(self):
-        return itipdefinitions.cICalMIMEComponent_VJOURNAL
-
-
-    def finalise(self):
-        super(PyCalendarVJournal, self).finalise()
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_RECURRENCE_ID,
-            definitions.cICalProperty_DTSTART,
-        )

Deleted: PyCalendar/branches/json-2/src/pycalendar/vtimezone.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vtimezone.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vtimezone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,285 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-
-class PyCalendarVTimezone(PyCalendarComponent):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_TZID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_TZURL,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    UTCOFFSET_CACHE_MAX_ENTRIES = 100000
-
-    sortSubComponents = False
-
-    def __init__(self, parent=None):
-        super(PyCalendarVTimezone, self).__init__(parent=parent)
-        self.mID = ""
-        self.mUTCOffsetSortKey = None
-        self.mCachedExpandAllMaxYear = None
-        self.mCachedOffsets = None
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVTimezone, self).duplicate(parent=parent)
-        other.mID = self.mID
-        other.mUTCOffsetSortKey = self.mUTCOffsetSortKey
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VTIMEZONE
-
-
-    def getMimeComponentName(self):
-        # Cannot be sent as a separate MIME object
-        return None
-
-
-    def addComponent(self, comp):
-        # We can embed the timezone components only
-        if ((comp.getType() == definitions.cICalComponent_STANDARD)
-                or (comp.getType() == definitions.cICalComponent_DAYLIGHT)):
-            super(PyCalendarVTimezone, self).addComponent(comp)
-        else:
-            raise ValueError
-
-
-    def getMapKey(self):
-        return self.mID
-
-
-    def finalise(self):
-        # Get TZID
-        temp = self.loadValueString(definitions.cICalProperty_TZID)
-        if temp is not None:
-            self.mID = temp
-
-        # Sort sub-components by DTSTART
-        self.mComponents.sort(key=lambda x: x.getStart())
-
-        # Do inherited
-        super(PyCalendarVTimezone, self).finalise()
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        fixed, unfixed = super(PyCalendarVTimezone, self).validate(doFix)
-
-        # Must have at least one STANDARD or DAYLIGHT sub-component
-        for component in self.mComponents:
-            if component.getType() in (definitions.cICalComponent_STANDARD, definitions.cICalComponent_DAYLIGHT):
-                break
-        else:
-            # Cannot fix a missing required component
-            logProblem = "[%s] At least one component must be present: %s or %s" % (
-                self.getType(),
-                definitions.cICalComponent_STANDARD,
-                definitions.cICalComponent_DAYLIGHT,
-            )
-            unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    def getID(self):
-        return self.mID
-
-
-    def getUTCOffsetSortKey(self):
-        if self.mUTCOffsetSortKey is None:
-            # Take time from first element
-            if len(self.mComponents) > 0:
-                # Initial offset provides the primary key
-                utc_offset1 = self.mComponents[0].getUTCOffset()
-
-                # Presence of secondary is the next key
-                utc_offset2 = utc_offset1
-                if len(self.mComponents) > 1:
-                    utc_offset2 = self.mComponents[1].getUTCOffset()
-
-                # Create key
-                self.mUTCOffsetSortKey = (utc_offset1 + utc_offset2) / 2
-            else:
-                self.mUTCOffsetSortKey = 0
-
-        return self.mUTCOffsetSortKey
-
-
-    def getTimezoneOffsetSeconds(self, dt):
-        """
-        Caching implementation of expansion. We cache the entire set of transitions up to one year ahead
-        of the requested time.
-        """
-
-        # Need to make the incoming date-time relative to the DTSTART in the
-        # timezone component for proper comparison.
-        # This means making the incoming date-time a floating (no timezone)
-        # item
-        temp = dt.duplicate()
-        temp.setTimezoneID(None)
-
-        # Check whether we need to recache
-        if self.mCachedExpandAllMaxYear is None or temp.mYear >= self.mCachedExpandAllMaxYear:
-            cacheMax = temp.duplicate()
-            cacheMax.setHHMMSS(0, 0, 0)
-            cacheMax.offsetYear(2)
-            cacheMax.setMonth(1)
-            cacheMax.setDay(1)
-            self.mCachedExpandAll = self.expandAll(None, cacheMax)
-            self.mCachedExpandAllMaxYear = cacheMax.mYear
-            self.mCachedOffsets = {}
-
-        # Now search for the transition just below the time we want
-        if len(self.mCachedExpandAll):
-            cacheKey = (temp.mYear, temp.mMonth, temp.mDay, temp.mHours, temp.mMinutes,)
-            i = self.mCachedOffsets.get(cacheKey)
-            if i is None:
-                i = PyCalendarVTimezone.tuple_bisect_right(self.mCachedExpandAll, temp)
-                if len(self.mCachedOffsets) >= self.UTCOFFSET_CACHE_MAX_ENTRIES:
-                    self.mCachedOffsets = {}
-                self.mCachedOffsets[cacheKey] = i
-            if i != 0:
-                return self.mCachedExpandAll[i - 1][2]
-
-        return 0
-
-
-    def getTimezoneDescriptor(self, dt):
-        result = ""
-
-        # Get the closet matching element to the time
-        found = self.findTimezoneElement(dt)
-
-        # Get it
-        if found is not None:
-            if len(found.getTZName()) == 0:
-                tzoffset = found.getUTCOffset()
-                negative = False
-                if tzoffset < 0:
-                    tzoffset = -tzoffset
-                    negative = True
-                result = ("+", "-")[negative]
-                hours_offset = tzoffset / (60 * 60)
-                if hours_offset < 10:
-                    result += "0"
-                result += str(hours_offset)
-                mins_offset = (tzoffset / 60) % 60
-                if mins_offset < 10:
-                    result += "0"
-                result += str(mins_offset)
-            else:
-                result = "("
-                result += found.getTZName()
-                result += ")"
-
-        return result
-
-
-    def mergeTimezone(self, tz):
-        pass
-
-
-    @staticmethod
-    def tuple_bisect_right(a, x):
-        """
-        Same as bisect_right except that the values being compared are the first elements
-        of a tuple.
-        """
-
-        lo = 0
-        hi = len(a)
-        while lo < hi:
-            mid = (lo + hi) // 2
-            if x < a[mid][0]:
-                hi = mid
-            else:
-                lo = mid + 1
-        return lo
-
-
-    def findTimezoneElement(self, dt):
-        # Need to make the incoming date-time relative to the DTSTART in the
-        # timezone component for proper comparison.
-        # This means making the incoming date-time a floating (no timezone)
-        # item
-        temp = dt.duplicate()
-        temp.setTimezoneID(None)
-
-        # Had to rework this because some VTIMEZONEs have sub-components where the DST instances are interleaved. That
-        # means we have to evaluate each and every sub-component to find the instance immediately less than the time we are checking.
-
-        # Now do the expansion for each one found and pick the lowest
-        found = None
-        dt_found = PyCalendarDateTime()
-
-        for item in self.mComponents:
-            dt_item = item.expandBelow(temp)
-            if temp >= dt_item:
-                if found is not None:
-                    # Compare with the one previously cached and switch to this
-                    # one if newer
-                    if dt_item > dt_found:
-                        found = item
-                        dt_found = dt_item
-                else:
-                    found = item
-                    dt_found = dt_item
-
-        return found
-
-
-    def expandAll(self, start, end, with_name=False):
-        results = []
-        for item in self.mComponents:
-            results.extend(item.expandAll(start, end, with_name))
-        results = [x for x in set(results)]
-        results.sort(key=lambda x: x[0].getPosixTime())
-        return results
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_TZID,
-            definitions.cICalProperty_LAST_MODIFIED,
-            definitions.cICalProperty_TZURL,
-        )
-
-
-    @staticmethod
-    def sortByUTCOffsetComparator(tz1, tz2):
-        sort1 = tz1.getUTCOffsetSortKey()
-        sort2 = tz2.getUTCOffsetSortKey()
-        if sort1 == sort2:
-            return tz1.getID().compareToIgnoreCase(tz2.getID())
-        else:
-            return (1, -1)[sort1 < sort2]

Deleted: PyCalendar/branches/json-2/src/pycalendar/vtimezonedaylight.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vtimezonedaylight.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vtimezonedaylight.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,31 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from vtimezoneelement import PyCalendarVTimezoneElement
-
-class PyCalendarVTimezoneDaylight(PyCalendarVTimezoneElement):
-
-    def __init__(self, parent=None):
-        super(PyCalendarVTimezoneDaylight, self).__init__(parent=parent)
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarVTimezoneDaylight, self).duplicate(parent=parent)
-
-
-    def getType(self):
-        return definitions.cICalComponent_DAYLIGHT

Deleted: PyCalendar/branches/json-2/src/pycalendar/vtimezoneelement.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vtimezoneelement.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vtimezoneelement.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,219 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from bisect import bisect_right
-from pycalendar import definitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.period import PyCalendarPeriod
-from pycalendar.recurrenceset import PyCalendarRecurrenceSet
-from pycalendar.value import PyCalendarValue
-
-class PyCalendarVTimezoneElement(PyCalendarComponent):
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_TZOFFSETTO,
-        definitions.cICalProperty_TZOFFSETFROM,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_RRULE,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None, dt=None, offset=None):
-        super(PyCalendarVTimezoneElement, self).__init__(parent=parent)
-        self.mStart = dt if dt is not None else PyCalendarDateTime()
-        self.mTZName = ""
-        self.mUTCOffset = offset if offset is not None else 0
-        self.mUTCOffsetFrom = 0
-        self.mRecurrences = PyCalendarRecurrenceSet()
-        self.mCachedExpandBelow = None
-        self.mCachedExpandBelowItems = None
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVTimezoneElement, self).duplicate(parent=parent)
-        other.mStart = self.mStart.duplicate()
-        other.mTZName = self.mTZName
-        other.mUTCOffset = self.mUTCOffset
-        other.mUTCOffsetFrom = self.mUTCOffsetFrom
-        other.mRecurrences = self.mRecurrences.duplicate()
-        other.mCachedExpandBelow = None
-        other.mCachedExpandBelowItems = None
-        return other
-
-
-    def finalise(self):
-        # Get DTSTART
-        temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
-        if temp is not None:
-            self.mStart = temp
-
-        # Get TZOFFSETTO
-        temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, PyCalendarValue.VALUETYPE_UTC_OFFSET)
-        if temp is not None:
-            self.mUTCOffset = temp
-
-        # Get TZOFFSETFROM
-        temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, PyCalendarValue.VALUETYPE_UTC_OFFSET)
-        if temp is not None:
-            self.mUTCOffsetFrom = temp
-
-        # Get TZNAME
-        temps = self.loadValueString(definitions.cICalProperty_TZNAME)
-        if temps is not None:
-            self.mTZName = temps
-
-        # Get RRULEs
-        self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
-
-        # Get RDATEs
-        self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
-
-        # Do inherited
-        super(PyCalendarVTimezoneElement, self).finalise()
-
-
-    def getSortKey(self):
-        """
-        We do not want these components sorted.
-        """
-        return ""
-
-
-    def getStart(self):
-        return self.mStart
-
-
-    def getUTCOffset(self):
-        return self.mUTCOffset
-
-
-    def getUTCOffsetFrom(self):
-        return self.mUTCOffsetFrom
-
-
-    def getTZName(self):
-        return self.mTZName
-
-
-    def expandBelow(self, below):
-
-        # Look for recurrences
-        if not self.mRecurrences.hasRecurrence() or self.mStart > below:
-            # Return DTSTART even if it is newer
-            return self.mStart
-        else:
-            # We want to allow recurrence calculation caching to help us here
-            # as this method
-            # gets called a lot - most likely for ever increasing dt values
-            # (which will therefore
-            # invalidate the recurrence cache).
-            #
-            # What we will do is round up the date-time to the next year so
-            # that the recurrence
-            # cache is invalidated less frequently
-
-            temp = PyCalendarDateTime(below.getYear(), 1, 1, 0, 0, 0)
-
-            # Use cache of expansion
-            if self.mCachedExpandBelowItems is None:
-                self.mCachedExpandBelowItems = []
-            if self.mCachedExpandBelow is None:
-                self.mCachedExpandBelow = self.mStart.duplicate()
-            if temp > self.mCachedExpandBelow:
-                self.mCachedExpandBelowItems = []
-                period = PyCalendarPeriod(self.mStart, temp)
-                self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
-                self.mCachedExpandBelow = temp
-
-            if len(self.mCachedExpandBelowItems) != 0:
-                # List comes back sorted so we pick the element just less than
-                # the dt value we want
-                i = bisect_right(self.mCachedExpandBelowItems, below)
-                if i != 0:
-                    return self.mCachedExpandBelowItems[i - 1]
-
-                # The first one in the list is the one we want
-                return self.mCachedExpandBelowItems[0]
-
-            return self.mStart
-
-
-    def expandAll(self, start, end, with_name):
-
-        if start is None:
-            start = self.mStart
-
-        # Ignore if there is no change in offset
-        offsetto = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, PyCalendarValue.VALUETYPE_UTC_OFFSET)
-        offsetfrom = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, PyCalendarValue.VALUETYPE_UTC_OFFSET)
-#        if offsetto == offsetfrom:
-#            return ()
-
-        # Look for recurrences
-        if self.mStart > end:
-            # Return nothing
-            return ()
-        elif not self.mRecurrences.hasRecurrence():
-            # Return DTSTART even if it is newer
-            if self.mStart >= start:
-                result = (self.mStart, offsetfrom, offsetto,)
-                if with_name:
-                    result += (self.getTZName(),)
-                return (result,)
-            else:
-                return ()
-        else:
-            # We want to allow recurrence calculation caching to help us here
-            # as this method
-            # gets called a lot - most likely for ever increasing dt values
-            # (which will therefore
-            # invalidate the recurrence cache).
-            #
-            # What we will do is round up the date-time to the next year so
-            # that the recurrence
-            # cache is invalidated less frequently
-
-            temp = PyCalendarDateTime(end.getYear(), 1, 1, 0, 0, 0)
-
-            # Use cache of expansion
-            if self.mCachedExpandBelowItems is None:
-                self.mCachedExpandBelowItems = []
-            if self.mCachedExpandBelow is None:
-                self.mCachedExpandBelow = self.mStart.duplicate()
-            if temp > self.mCachedExpandBelow:
-                self.mCachedExpandBelowItems = []
-                period = PyCalendarPeriod(self.mStart, end)
-                self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
-                self.mCachedExpandBelow = temp
-
-            if len(self.mCachedExpandBelowItems) != 0:
-                # Return them all within the range
-                results = []
-                for dt in self.mCachedExpandBelowItems:
-                    if dt >= start and dt < end:
-                        result = (dt, offsetfrom, offsetto,)
-                        if with_name:
-                            result += (self.getTZName(),)
-                        results.append(result)
-                return results
-
-            return ()

Deleted: PyCalendar/branches/json-2/src/pycalendar/vtimezonestandard.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vtimezonestandard.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vtimezonestandard.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,31 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.vtimezoneelement import PyCalendarVTimezoneElement
-
-class PyCalendarVTimezoneStandard(PyCalendarVTimezoneElement):
-
-    def __init__(self, parent=None):
-        super(PyCalendarVTimezoneStandard, self).__init__(parent=parent)
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarVTimezoneStandard, self).duplicate(parent=parent)
-
-
-    def getType(self):
-        return definitions.cICalComponent_STANDARD

Deleted: PyCalendar/branches/json-2/src/pycalendar/vtodo.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vtodo.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vtodo.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,366 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar import itipdefinitions
-from pycalendar.componentrecur import PyCalendarComponentRecur
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-from pycalendar.property import PyCalendarProperty
-import cStringIO as StringIO
-
-class PyCalendarVToDo(PyCalendarComponentRecur):
-
-    OVERDUE = 0
-    DUE_NOW = 1
-    DUE_LATER = 2
-    DONE = 3
-    CANCELLED = 4
-
-    @staticmethod
-    def sort_for_display(e1, e2):
-        s1 = e1.getMaster()
-        s2 = e2.getMaster()
-
-        # Check status first (convert None -> Needs action for tests)
-        status1 = s1.self.mStatus
-        status2 = s2.self.mStatus
-        if status1 == definitions.eStatus_VToDo_None:
-            status1 = definitions.eStatus_VToDo_NeedsAction
-        if status2 == definitions.eStatus_VToDo_None:
-            status2 = definitions.eStatus_VToDo_NeedsAction
-        if status1 != status2:
-            # More important ones at the top
-            return status1 < status2
-
-        # At this point the status of each is the same
-
-        # If status is cancelled sort by start time
-        if s1.self.mStatus == definitions.eStatus_VToDo_Cancelled:
-            # Older ones at the bottom
-            return s1.mStart > s2.mStart
-
-        # If status is completed sort by completion time
-        if s1.self.mStatus == definitions.eStatus_VToDo_Completed:
-            # Older ones at the bottom
-            return s1.self.mCompleted > s2.self.mCompleted
-
-        # Check due date exists
-        if s1.mHasEnd != s2.mHasEnd:
-            now = PyCalendarDateTime()
-            now.setToday()
-
-            # Ones with due dates after today below ones without due dates
-            if s1.hasEnd():
-                return s1.mEnd <= now
-            elif s2.hasEnd():
-                return now < s2.mEnd
-
-        # Check due dates if present
-        if s1.mHasEnd:
-            if s1.mEnd != s2.mEnd:
-                # Soonest dues dates above later ones
-                return s1.mEnd < s2.mEnd
-
-        # Check priority next
-        if s1.self.mPriority != s2.self.mPriority:
-            # Higher priority above lower ones
-            return s1.self.mPriority < s2.self.mPriority
-
-        # Just use start time - older ones at the top
-        return s1.mStart < s2.mStart
-
-    propertyCardinality_1 = (
-        definitions.cICalProperty_DTSTAMP,
-        definitions.cICalProperty_UID,
-    )
-
-    propertyCardinality_0_1 = (
-        definitions.cICalProperty_CLASS,
-        definitions.cICalProperty_COMPLETED,
-        definitions.cICalProperty_CREATED,
-        definitions.cICalProperty_DESCRIPTION,
-        definitions.cICalProperty_DTSTART,
-        definitions.cICalProperty_GEO,
-        definitions.cICalProperty_LAST_MODIFIED,
-        definitions.cICalProperty_LOCATION,
-        definitions.cICalProperty_ORGANIZER,
-        definitions.cICalProperty_PERCENT_COMPLETE,
-        definitions.cICalProperty_PRIORITY,
-        definitions.cICalProperty_RECURRENCE_ID,
-        definitions.cICalProperty_SEQUENCE,
-        # definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
-        definitions.cICalProperty_SUMMARY,
-        definitions.cICalProperty_URL,
-        definitions.cICalProperty_RRULE,
-        definitions.cICalProperty_DUE,
-        definitions.cICalProperty_DURATION,
-    )
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None):
-        super(PyCalendarVToDo, self).__init__(parent=parent)
-        self.mPriority = 0
-        self.mStatus = definitions.eStatus_VToDo_None
-        self.mPercentComplete = 0
-        self.mCompleted = PyCalendarDateTime()
-        self.mHasCompleted = False
-
-
-    def duplicate(self, parent=None):
-        other = super(PyCalendarVToDo, self).duplicate(parent=parent)
-        other.mPriority = self.mPriority
-        other.mStatus = self.mStatus
-        other.mPercentComplete = self.mPercentComplete
-        other.mCompleted = self.mCompleted.duplicate()
-        other.mHasCompleted = self.mHasCompleted
-        return other
-
-
-    def getType(self):
-        return definitions.cICalComponent_VTODO
-
-
-    def getMimeComponentName(self):
-        return itipdefinitions.cICalMIMEComponent_VTODO
-
-
-    def addComponent(self, comp):
-        # We can embed the alarm components only
-        if comp.getType() == definitions.cICalComponent_VALARM:
-            super(PyCalendarVToDo, self).addComponent(comp)
-        else:
-            raise ValueError
-
-
-    def getStatus(self):
-        return self.mStatus
-
-
-    def setStatus(self, status):
-        self.mStatus = status
-
-
-    def getStatusText(self):
-        sout = StringIO()
-
-        if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
-            if self.hasEnd():
-                # Check due date
-                today = PyCalendarDateTime()
-                today.setToday()
-                if self.getEnd() > today:
-                    sout.append("Due: ")
-                    whendue = self.getEnd() - today
-                    if (whendue.getDays() > 0) and (whendue.getDays() <= 7):
-                        sout.write(whendue.getDays())
-                        sout.write(" days")
-                    else:
-                        sout.write(self.getEnd().getLocaleDate(PyCalendarDateTime.NUMERICDATE))
-                elif self.getEnd() == today:
-                    sout.write("Due today")
-                else:
-                    sout.write("Overdue: ")
-                    overdue = today - self.getEnd()
-                    if overdue.getWeeks() != 0:
-                        sout.write(overdue.getWeeks())
-                        sout.write(" weeks")
-                    else:
-                        sout.write(overdue.getDays() + 1)
-                        sout.write(" days")
-            else:
-                sout.write("Not Completed")
-        elif self.mStatus == definitions.eStatus_VToDo_Completed:
-            if self.hasCompleted():
-                sout.write("Completed: ")
-                sout.write(self.getCompleted().getLocaleDate(PyCalendarDateTime.NUMERICDATE))
-            else:
-                sout.write("Completed")
-        elif definitions.eStatus_VToDo_Cancelled:
-            sout.write("Cancelled")
-
-        return sout.toString()
-
-
-    def getCompletionState(self):
-        if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
-            if self.hasEnd():
-                # Check due date
-                today = PyCalendarDateTime()
-                today.setToday()
-                if self.getEnd() > today:
-                    return PyCalendarVToDo.DUE_LATER
-                elif self.getEnd() == today:
-                    return PyCalendarVToDo.DUE_NOW
-                else:
-                    return PyCalendarVToDo.OVERDUE
-            else:
-                return PyCalendarVToDo.DUE_NOW
-        elif self.mStatus == definitions.eStatus_VToDo_Completed:
-            return PyCalendarVToDo.DONE
-        elif self.mStatus == definitions.eStatus_VToDo_Cancelled:
-            return PyCalendarVToDo.CANCELLED
-
-
-    def getPriority(self):
-        return self.mPriority
-
-
-    def setPriority(self, priority):
-        self.mPriority = priority
-
-
-    def getCompleted(self):
-        return self.mCompleted
-
-
-    def hasCompleted(self):
-        return self.mHasCompleted
-
-
-    def finalise(self):
-        # Do inherited
-        super(PyCalendarVToDo, self).finalise()
-
-        # Get DUE
-        temp = self.loadValueDateTime(definitions.cICalProperty_DUE)
-        if temp is None:
-            # Try DURATION instead
-            temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
-            if temp is not None:
-                self.mEnd = self.mStart + temp
-                self.mHasEnd = True
-            else:
-                self.mHasEnd = False
-        else:
-            self.mHasEnd = True
-            self.mEnd = temp
-
-        # Get PRIORITY
-        self.mPriority = self.loadValueInteger(definitions.cICalProperty_PRIORITY)
-
-        # Get STATUS
-        temp = self.loadValueString(definitions.cICalProperty_STATUS)
-        if temp is not None:
-            if temp == definitions.cICalProperty_STATUS_NEEDS_ACTION:
-                self.mStatus = definitions.eStatus_VToDo_NeedsAction
-            elif temp == definitions.cICalProperty_STATUS_COMPLETED:
-                self.mStatus = definitions.eStatus_VToDo_Completed
-            elif temp == definitions.cICalProperty_STATUS_IN_PROCESS:
-                self.mStatus = definitions.eStatus_VToDo_InProcess
-            elif temp == definitions.cICalProperty_STATUS_CANCELLED:
-                self.mStatus = definitions.eStatus_VToDo_Cancelled
-
-        # Get PERCENT-COMPLETE
-        self.mPercentComplete = self.loadValueInteger(definitions.cICalProperty_PERCENT_COMPLETE)
-
-        # Get COMPLETED
-        temp = self.loadValueDateTime(definitions.cICalProperty_COMPLETED)
-        self.mHasCompleted = temp is not None
-        if self.mHasCompleted:
-            self.mCompleted = temp
-
-
-    def validate(self, doFix=False):
-        """
-        Validate the data in this component and optionally fix any problems, else raise. If
-        loggedProblems is not None it must be a C{list} and problem descriptions are appended
-        to that.
-        """
-
-        fixed, unfixed = super(PyCalendarVToDo, self).validate(doFix)
-
-        # Extra constraint: only one of DUE or DURATION
-        if self.hasProperty(definitions.cICalProperty_DUE) and self.hasProperty(definitions.cICalProperty_DURATION):
-            # Fix by removing the DURATION
-            logProblem = "[%s] Properties must not both be present: %s, %s" % (
-                self.getType(),
-                definitions.cICalProperty_DUE,
-                definitions.cICalProperty_DURATION,
-            )
-            if doFix:
-                self.removeProperties(definitions.cICalProperty_DURATION)
-                fixed.append(logProblem)
-            else:
-                unfixed.append(logProblem)
-
-        # Extra constraint: DTSTART must be present if DURATION is present
-        if self.hasProperty(definitions.cICalProperty_DURATION) and not self.hasProperty(definitions.cICalProperty_DTSTART):
-            # Cannot fix this one
-            logProblem = "[%s] Property must be present: %s with %s" % (
-                self.getType(),
-                definitions.cICalProperty_DTSTART,
-                definitions.cICalProperty_DURATION,
-            )
-            unfixed.append(logProblem)
-
-        return fixed, unfixed
-
-
-    # Editing
-    def editStatus(self, status):
-        # Only if it is different
-        if self.mStatus != status:
-            # Updated cached values
-            self.mStatus = status
-
-            # Remove existing STATUS & COMPLETED items
-            self.removeProperties(definitions.cICalProperty_STATUS)
-            self.removeProperties(definitions.cICalProperty_COMPLETED)
-            self.mHasCompleted = False
-
-            # Now create properties
-            value = None
-            if status == definitions.eStatus_VToDo_NeedsAction:
-                value = definitions.cICalProperty_STATUS_NEEDS_ACTION
-            if status == definitions.eStatus_VToDo_Completed:
-                value = definitions.cICalProperty_STATUS_COMPLETED
-                # Add the completed item
-                self.mCompleted.setNowUTC()
-                self.mHasCompleted = True
-                prop = PyCalendarProperty(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
-                self.addProperty(prop)
-            elif status == definitions.eStatus_VToDo_InProcess:
-                value = definitions.cICalProperty_STATUS_IN_PROCESS
-            elif status == definitions.eStatus_VToDo_Cancelled:
-                value = definitions.cICalProperty_STATUS_CANCELLED
-            prop = PyCalendarProperty(definitions.cICalProperty_STATUS, value)
-            self.addProperty(prop)
-
-
-    def editCompleted(self, completed):
-        # Remove existing COMPLETED item
-        self.removeProperties(definitions.cICalProperty_COMPLETED)
-        self.mHasCompleted = False
-
-        # Always UTC
-        self.mCompleted = completed.duplicate()
-        self.mCompleted.adjustToUTC()
-        self.mHasCompleted = True
-        prop = PyCalendarProperty(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
-        self.addProperty(prop)
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-            definitions.cICalProperty_RECURRENCE_ID,
-            definitions.cICalProperty_DTSTART,
-            definitions.cICalProperty_DURATION,
-            definitions.cICalProperty_DUE,
-            definitions.cICalProperty_COMPLETED,
-        )

Deleted: PyCalendar/branches/json-2/src/pycalendar/vunknown.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vunknown.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/vunknown.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,66 +0,0 @@
-##
-#    Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-from pycalendar import definitions
-from pycalendar.component import PyCalendarComponent
-from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
-import uuid
-
-class PyCalendarUnknownComponent(PyCalendarComponent):
-
-    propertyValueChecks = ICALENDAR_VALUE_CHECKS
-
-    def __init__(self, parent=None, comptype=""):
-        super(PyCalendarUnknownComponent, self).__init__(parent=parent)
-        self.mType = comptype
-        self.mMapKey = str(uuid.uuid4())
-
-
-    def duplicate(self, parent=None):
-        return super(PyCalendarUnknownComponent, self).duplicate(parent=parent, comptype=self.mType)
-
-
-    def getType(self):
-        return self.mType
-
-
-    def getBeginDelimiter(self):
-        return "BEGIN:" + self.mType
-
-
-    def getEndDelimiter(self):
-        return "END:" + self.mType
-
-
-    def getMimeComponentName(self):
-        return "unknown"
-
-
-    def getMapKey(self):
-        return self.mMapKey
-
-
-    def getSortKey(self):
-        """
-        We do not want unknown components sorted.
-        """
-        return ""
-
-
-    def sortedPropertyKeyOrder(self):
-        return (
-            definitions.cICalProperty_UID,
-        )

Added: PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,40 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+# Generic XML definitions
+
+iCalendar20_namespace = "urn:ietf:params:xml:ns:icalendar-2.0"
+
+components = "components"
+properties = "properties"
+parameters = "parameters"
+
+value_binary = "binary"
+value_boolean = "boolean"
+value_cal_address = "cal-address"
+value_date = "date"
+value_date_time = "date-time"
+value_duration = "duration"
+value_integer = "integer"
+value_period = "period"
+value_text = "text"
+value_unknown = "unknown"
+value_uri = "uri"
+value_utc_offset = "utc-offset"
+
+period_start = "start"
+period_end = "end"
+period_duration = "duration"

Deleted: PyCalendar/branches/json-2/src/pycalendar/xmldefs.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/xmldefs.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/pycalendar/xmldefs.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -1,104 +0,0 @@
-##
-#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-##
-
-import xml.etree.cElementTree as XML
-
-# iCalendar/vCard XML definitions
-
-iCalendar20_namespace = "urn:ietf:params:xml:ns:icalendar-2.0"
-
-icalendar = "icalendar"
-components = "components"
-properties = "properties"
-parameters = "parameters"
-
-value_binary = "binary"
-value_boolean = "boolean"
-value_cal_address = "cal-address"
-value_date = "date"
-value_date_time = "date-time"
-value_duration = "duration"
-value_integer = "integer"
-value_period = "period"
-value_recur = "recur"
-value_text = "text"
-value_unknown = "unknown"
-value_uri = "uri"
-value_utc_offset = "utc-offset"
-
-period_start = "start"
-period_end = "end"
-period_duration = "duration"
-
-recur_freq = "freq"
-recur_freq_secondly = "SECONDLY"
-recur_freq_minutely = "MINUTELY"
-recur_freq_hourly = "HOURLY"
-recur_freq_daily = "DAILY"
-recur_freq_weekly = "WEEKLY"
-recur_freq_monthly = "MONTHLY"
-recur_freq_yearly = "YEARLY"
-
-recur_count = "count"
-recur_until = "until"
-recur_interval = "interval"
-
-recur_bysecond = "bysecond"
-recur_byminute = "byminute"
-recur_byhour = "byhour"
-recur_byday = "byday"
-recur_bymonthday = "bymonthday"
-recur_byyearday = "byyearday"
-recur_byweekno = "byweekno"
-recur_bymonth = "bymonth"
-recur_bysetpos = "bysetpos"
-recur_wkst = "wkst"
-
-req_status_code = "code"
-req_status_description = "description"
-req_status_data = "data"
-
-vCard40_namespace = "urn:ietf:params:xml:ns:vcard-4.0"
-
-def makeTag(namespace, name):
-    return "{%s}%s" % (namespace, name.lower(),)
-
-
-
-def toString(root):
-
-    data = """<?xml version="1.0" encoding="utf-8"?>\n"""
-
-    INDENT = 2
-
-    # Generate indentation
-    def _indentNode(node, level=0):
-
-        if node.text is not None and node.text.strip():
-            return
-        elif len(node.getchildren()):
-            indent = "\n" + " " * (level + 1) * INDENT
-            node.text = indent
-            for child in node.getchildren():
-                child.tail = indent
-                _indentNode(child, level + 1)
-            if len(node.getchildren()):
-                node.getchildren()[-1].tail = "\n" + " " * level * INDENT
-
-    _indentNode(root, 0)
-    data += XML.tostring(root) + "\n"
-
-    return data

Added: PyCalendar/branches/json-2/src/pycalendar/xmlutils.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/xmlutils.py	                        (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/xmlutils.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -0,0 +1,47 @@
+##
+#    Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+##
+
+import xml.etree.cElementTree as XML
+
+def makeTag(namespace, name):
+    return "{%s}%s" % (namespace, name.lower(),)
+
+
+
+def toString(root):
+
+    data = """<?xml version="1.0" encoding="utf-8"?>\n"""
+
+    INDENT = 2
+
+    # Generate indentation
+    def _indentNode(node, level=0):
+
+        if node.text is not None and node.text.strip():
+            return
+        elif len(node.getchildren()):
+            indent = "\n" + " " * (level + 1) * INDENT
+            node.text = indent
+            for child in node.getchildren():
+                child.tail = indent
+                _indentNode(child, level + 1)
+            if len(node.getchildren()):
+                node.getchildren()[-1].tail = "\n" + " " * level * INDENT
+
+    _indentNode(root, 0)
+    data += XML.tostring(root) + "\n"
+
+    return data

Modified: PyCalendar/branches/json-2/src/zonal/rule.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/rule.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/rule.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,14 +14,14 @@
 #    limitations under the License.
 ##
 
-from pycalendar import definitions
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.recurrence import Recurrence
+from pycalendar.icalendar.vtimezonedaylight import Daylight
+from pycalendar.icalendar.vtimezonestandard import Standard
+from pycalendar.utcoffsetvalue import UTCOffsetValue
 from pycalendar.utils import daysInMonth
-from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
-from pycalendar.vtimezonedaylight import PyCalendarVTimezoneDaylight
-from pycalendar.property import PyCalendarProperty
-from pycalendar.recurrence import PyCalendarRecurrence
 import utils
 
 """
@@ -114,23 +114,23 @@
     # Some useful mapping tables
 
     LASTDAY_NAME_TO_DAY = {
-        "lastSun": PyCalendarDateTime.SUNDAY,
-        "lastMon": PyCalendarDateTime.MONDAY,
-        "lastTue": PyCalendarDateTime.TUESDAY,
-        "lastWed": PyCalendarDateTime.WEDNESDAY,
-        "lastThu": PyCalendarDateTime.THURSDAY,
-        "lastFri": PyCalendarDateTime.FRIDAY,
-        "lastSat": PyCalendarDateTime.SATURDAY,
+        "lastSun": DateTime.SUNDAY,
+        "lastMon": DateTime.MONDAY,
+        "lastTue": DateTime.TUESDAY,
+        "lastWed": DateTime.WEDNESDAY,
+        "lastThu": DateTime.THURSDAY,
+        "lastFri": DateTime.FRIDAY,
+        "lastSat": DateTime.SATURDAY,
     }
 
     DAY_NAME_TO_DAY = {
-        "Sun": PyCalendarDateTime.SUNDAY,
-        "Mon": PyCalendarDateTime.MONDAY,
-        "Tue": PyCalendarDateTime.TUESDAY,
-        "Wed": PyCalendarDateTime.WEDNESDAY,
-        "Thu": PyCalendarDateTime.THURSDAY,
-        "Fri": PyCalendarDateTime.FRIDAY,
-        "Sat": PyCalendarDateTime.SATURDAY,
+        "Sun": DateTime.SUNDAY,
+        "Mon": DateTime.MONDAY,
+        "Tue": DateTime.TUESDAY,
+        "Wed": DateTime.WEDNESDAY,
+        "Thu": DateTime.THURSDAY,
+        "Fri": DateTime.FRIDAY,
+        "Sat": DateTime.SATURDAY,
     }
 
     LASTDAY_NAME_TO_RDAY = {
@@ -144,13 +144,13 @@
     }
 
     DAY_NAME_TO_RDAY = {
-        PyCalendarDateTime.SUNDAY: definitions.eRecurrence_WEEKDAY_SU,
-        PyCalendarDateTime.MONDAY: definitions.eRecurrence_WEEKDAY_MO,
-        PyCalendarDateTime.TUESDAY: definitions.eRecurrence_WEEKDAY_TU,
-        PyCalendarDateTime.WEDNESDAY: definitions.eRecurrence_WEEKDAY_WE,
-        PyCalendarDateTime.THURSDAY: definitions.eRecurrence_WEEKDAY_TH,
-        PyCalendarDateTime.FRIDAY: definitions.eRecurrence_WEEKDAY_FR,
-        PyCalendarDateTime.SATURDAY: definitions.eRecurrence_WEEKDAY_SA,
+        DateTime.SUNDAY: definitions.eRecurrence_WEEKDAY_SU,
+        DateTime.MONDAY: definitions.eRecurrence_WEEKDAY_MO,
+        DateTime.TUESDAY: definitions.eRecurrence_WEEKDAY_TU,
+        DateTime.WEDNESDAY: definitions.eRecurrence_WEEKDAY_WE,
+        DateTime.THURSDAY: definitions.eRecurrence_WEEKDAY_TH,
+        DateTime.FRIDAY: definitions.eRecurrence_WEEKDAY_FR,
+        DateTime.SATURDAY: definitions.eRecurrence_WEEKDAY_SA,
     }
 
     MONTH_NAME_TO_POS = {
@@ -288,11 +288,11 @@
         @param year:  the year to determine the transition for
         @type year: C{int}
 
-        @return: C{tuple} of L{PyCalendarDateTime} and C{str} (which is the special
+        @return: C{tuple} of L{DateTime} and C{str} (which is the special
             tzdata mode character
         """
         # Create a floating date-time
-        dt = PyCalendarDateTime()
+        dt = DateTime()
 
         # Setup base year/month/day
         dt.setYear(year)
@@ -342,7 +342,7 @@
         Get RRULE BYxxx part items from the Rule data.
 
         @param start: start date-time for the recurrence set
-        @type start: L{PyCalendarDateTime}
+        @type start: L{DateTime}
         @param indicatedDay: the day that the Rule indicates for recurrence
         @type indicatedDay: C{int}
         @param indicatedOffset: the offset that the Rule indicates for recurrence
@@ -449,13 +449,13 @@
         Generate a VTIMEZONE sub-component for this Rule.
 
         @param vtz: VTIMEZONE to add to
-        @type vtz: L{PyCalendarVTimezone}
+        @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{PyCalendarDateTime}
+        @type start: L{DateTime}
         @param end: the start time for the last instance
-        @type end: L{PyCalendarDateTime}
+        @type end: L{DateTime}
         @param offsetfrom: the UTC offset-from
         @type offsetfrom: C{int}
         @param offsetto: the UTC offset-to
@@ -467,31 +467,31 @@
         # Determine type of component based on offset
         dstoffset = self.getOffset()
         if dstoffset == 0:
-            comp = PyCalendarVTimezoneStandard(parent=vtz)
+            comp = Standard(parent=vtz)
         else:
-            comp = PyCalendarVTimezoneDaylight(parent=vtz)
+            comp = Daylight(parent=vtz)
 
         # Do offsets
-        tzoffsetfrom = PyCalendarUTCOffsetValue(offsetfrom)
-        tzoffsetto = PyCalendarUTCOffsetValue(offsetto)
+        tzoffsetfrom = UTCOffsetValue(offsetfrom)
+        tzoffsetto = UTCOffsetValue(offsetto)
 
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))
+        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(PyCalendarProperty(definitions.cICalProperty_TZNAME, tzname))
+        comp.addProperty(Property(definitions.cICalProperty_TZNAME, tzname))
 
         # Do DTSTART
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_DTSTART, start))
+        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 = PyCalendarRecurrence()
+            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:
@@ -566,9 +566,9 @@
                 rrule.setUseUntil(True)
                 rrule.setUntil(until)
 
-            comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RRULE, rrule))
+            comp.addProperty(Property(definitions.cICalProperty_RRULE, rrule))
         else:
-            comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RDATE, start))
+            comp.addProperty(Property(definitions.cICalProperty_RDATE, start))
 
         comp.finalise()
         vtz.addComponent(comp)

Modified: PyCalendar/branches/json-2/src/zonal/tests/test_rule.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/tests/test_rule.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/tests/test_rule.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -16,7 +16,7 @@
 
 import unittest
 from zonal.rule import Rule, RuleSet
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
 
 class TestRule(unittest.TestCase):
 
@@ -37,9 +37,9 @@
 
     def test_datetimeforyear(self):
         data = (
-            ("Rule\tGuat\t2006\tonly\t-\tOct\t1\t0:00\t0\tS", 2006, PyCalendarDateTime(2006, 10, 1, 0, 0, 0), ""),
-            ("Rule\tAlgeria\t1916\t1919\t-\tOct\tSun>=1\t23:00s\t0\t-", 1916, PyCalendarDateTime(1916, 10, 1, 23, 0, 0), "s"),
-            ("Rule\tGhana\t1936\t1942\t-\tSep\t1\t0:00\t0:20\tGHST", 1937, PyCalendarDateTime(1937, 9, 1, 0, 0, 0), ""),
+            ("Rule\tGuat\t2006\tonly\t-\tOct\t1\t0:00\t0\tS", 2006, DateTime(2006, 10, 1, 0, 0, 0), ""),
+            ("Rule\tAlgeria\t1916\t1919\t-\tOct\tSun>=1\t23:00s\t0\t-", 1916, DateTime(1916, 10, 1, 23, 0, 0), "s"),
+            ("Rule\tGhana\t1936\t1942\t-\tSep\t1\t0:00\t0:20\tGHST", 1937, DateTime(1937, 9, 1, 0, 0, 0), ""),
         )
 
         for ruletext, year, dt, special in data:

Modified: PyCalendar/branches/json-2/src/zonal/tests/test_zone.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/tests/test_zone.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/tests/test_zone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -17,7 +17,7 @@
 import unittest
 from zonal.zone import Zone
 from zonal.rule import RuleSet
-from pycalendar.calendar import PyCalendar
+from pycalendar.icalendar.calendar import Calendar
 
 class TestZone(unittest.TestCase):
 
@@ -92,7 +92,7 @@
         ruleset.parse(rules)
         rules = {ruleset.name: ruleset}
 
-        cal = PyCalendar()
+        cal = Calendar()
         vtz = zone.vtimezone(cal, rules, 2006, 2011)
 
         self.assertEqual(str(vtz), result)

Modified: PyCalendar/branches/json-2/src/zonal/tzconvert.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/tzconvert.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/tzconvert.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -18,7 +18,7 @@
 from __future__ import with_statement
 
 from difflib import unified_diff
-from pycalendar.calendar import PyCalendar
+from pycalendar.icalendar.calendar import Calendar
 import cStringIO as StringIO
 import getopt
 import os
@@ -126,7 +126,7 @@
         Generate iCalendar data for all VTIMEZONEs or just those specified
         """
 
-        cal = PyCalendar()
+        cal = Calendar()
         for zone in self.zones.itervalues():
             if filterzones and zone.name not in filterzones:
                 continue
@@ -151,7 +151,7 @@
         for zone in self.zones.itervalues():
             if filterzones and zone.name not in filterzones:
                 continue
-            cal = PyCalendar()
+            cal = Calendar()
             vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
             cal.addComponent(vtz)
 
@@ -251,7 +251,7 @@
     if len(args) == 1:
         rootdir = os.path.expanduser(args[0])
 
-    PyCalendar.sProdID = prodid
+    Calendar.sProdID = prodid
 
     zonedir = os.path.join(rootdir, "tzdata")
     zonefiles = (
@@ -284,14 +284,14 @@
         ))
 
         icsdir = "../2008i/zoneinfo"
-        cal = PyCalendar()
+        cal = Calendar()
         for file in (checkName,):
             fin = open(os.path.join(icsdir, file + ".ics"), "r")
             cal.parse(fin)
 
         for vtz in cal.getVTimezoneDB():
-            #from pycalendar.vtimezoneelement import PyCalendarVTimezoneElement
-            #vtz.mEmbedded.sort(PyCalendarVTimezoneElement.sort_dtstart)
+            #from pycalendar.vtimezoneelement import VTimezoneElement
+            #vtz.mEmbedded.sort(VTimezoneElement.sort_dtstart)
             for embedded in vtz.mEmbedded:
                 embedded.finalise()
             vtz.finalise()

Modified: PyCalendar/branches/json-2/src/zonal/tzdump.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/tzdump.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/tzdump.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,22 +15,22 @@
 #    limitations under the License.
 ##
 
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData
+from pycalendar.icalendar.calendar import Calendar
+import getopt
 import os
 import sys
-import getopt
-from pycalendar.exceptions import PyCalendarInvalidData
 
 def loadCalendar(file, verbose):
 
-    cal = PyCalendar()
+    cal = Calendar()
     if verbose:
         print "Parsing calendar data: %s" % (file,)
     fin = open(file, "r")
     try:
         cal.parse(fin)
-    except PyCalendarInvalidData, e:
+    except InvalidData, e:
         print "Failed to parse bad data: %s" % (e.mData,)
         raise
     return cal
@@ -41,7 +41,7 @@
 
     vtz = cal.getComponents()[0]
     expanded = vtz.expandAll(start, end)
-    expanded.sort(cmp=lambda x, y: PyCalendarDateTime.sort(x[0], y[0]))
+    expanded.sort(cmp=lambda x, y: DateTime.sort(x[0], y[0]))
     return expanded
 
 
@@ -125,8 +125,8 @@
         usage("Must have one argument")
     fpath = os.path.expanduser(args[0])
 
-    start = PyCalendarDateTime(year=startYear, month=1, day=1)
-    end = PyCalendarDateTime(year=endYear, month=1, day=1)
+    start = DateTime(year=startYear, month=1, day=1)
+    end = DateTime(year=endYear, month=1, day=1)
 
     cal = loadCalendar(fpath, verbose)
     dates = getExpandedDates(cal, start, end)

Modified: PyCalendar/branches/json-2/src/zonal/tzverify.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/tzverify.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/tzverify.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -15,13 +15,13 @@
 #    limitations under the License.
 ##
 
-from pycalendar.calendar import PyCalendar
-from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.datetime import DateTime
+from pycalendar.exceptions import InvalidData
+from pycalendar.icalendar.calendar import Calendar
 from tzconvert import tzconvert
+import getopt
 import os
 import sys
-import getopt
-from pycalendar.exceptions import PyCalendarInvalidData
 
 def loadCalendarFromZoneinfo(zoneinfopath, skips=(), verbose=False, quiet=False):
 
@@ -51,14 +51,14 @@
 
 def loadCalendar(files, verbose):
 
-    cal = PyCalendar()
+    cal = Calendar()
     for file in files:
         if verbose:
             print "Parsing calendar data: %s" % (file,)
         fin = open(file, "r")
         try:
             cal.parse(fin)
-        except PyCalendarInvalidData, e:
+        except InvalidData, e:
             print "Failed to parse bad data: %s" % (e.mData,)
             raise
     return CalendarZonesWrapper(calendar=cal)
@@ -164,7 +164,7 @@
 
 def sortedList(setdata):
     l = list(setdata)
-    l.sort(cmp=lambda x, y: PyCalendarDateTime.sort(x[0], y[0]))
+    l.sort(cmp=lambda x, y: DateTime.sort(x[0], y[0]))
     return l
 
 
@@ -249,8 +249,8 @@
     zonedir1 = os.path.expanduser(args[0])
     zonedir2 = os.path.expanduser(args[1])
 
-    start = PyCalendarDateTime(year=startYear, month=1, day=1)
-    end = PyCalendarDateTime(year=endYear, month=1, day=1)
+    start = DateTime(year=startYear, month=1, day=1)
+    end = DateTime(year=endYear, month=1, day=1)
 
     zonefiles = (
         "northamerica",

Modified: PyCalendar/branches/json-2/src/zonal/zone.py
===================================================================
--- PyCalendar/branches/json-2/src/zonal/zone.py	2013-08-15 20:44:21 UTC (rev 11608)
+++ PyCalendar/branches/json-2/src/zonal/zone.py	2013-08-16 15:54:29 UTC (rev 11609)
@@ -14,14 +14,14 @@
 #    limitations under the License.
 ##
 
-from pycalendar.datetime import PyCalendarDateTime
-from pycalendar.vtimezone import PyCalendarVTimezone
-from pycalendar.property import PyCalendarProperty
-from pycalendar import definitions
-from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
-from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
+from pycalendar.datetime import DateTime
+from pycalendar.icalendar import definitions
+from pycalendar.icalendar.property import Property
+from pycalendar.icalendar.vtimezone import VTimezone
+from pycalendar.icalendar.vtimezonestandard import Standard
+from pycalendar.utcoffsetvalue import UTCOffsetValue
+import rule
 import utils
-import rule
 
 """
 Class that maintains a TZ data Zone.
@@ -129,7 +129,7 @@
         """
 
         # Start at 1/1/1800 with the offset from the initial zone rule
-        start = PyCalendarDateTime(year=1800, month=1, day=1, hours=0, minutes=0, seconds=0)
+        start = DateTime(year=1800, month=1, day=1, hours=0, minutes=0, seconds=0)
         start_offset = self.rules[0].getUTCOffset()
         start_stdoffset = self.rules[0].getUTCOffset()
         startdt = start.duplicate()
@@ -178,7 +178,7 @@
         """
         Generate a VTIMEZONE for this Zone.
 
-        @param calendar: the L{PyCalendar} object for the VCALENDAR in which the VTIMEZONE
+        @param calendar: the L{Calendar} object for the VCALENDAR in which the VTIMEZONE
             will be created.
         @param rules: the C{dict} containing the set of Rules currently defined.
         @param startYear: a C{int} containing the first year that should be present
@@ -187,11 +187,11 @@
         """
 
         # Get a VTIMEZONE component
-        vtz = PyCalendarVTimezone(parent=calendar)
+        vtz = VTimezone(parent=calendar)
 
         # Add TZID property
-        vtz.addProperty(PyCalendarProperty(definitions.cICalProperty_TZID, self.name))
-        vtz.addProperty(PyCalendarProperty("X-LIC-LOCATION", self.name))
+        vtz.addProperty(Property(definitions.cICalProperty_TZID, self.name))
+        vtz.addProperty(Property("X-LIC-LOCATION", self.name))
 
         transitions = self.expand(rules, minYear, maxYear)
 
@@ -270,7 +270,7 @@
         RDATEs assuming all other properties are the same.
 
         @param vtz: the VTIMEZONE object to compress
-        @type vtz: L{PyCalendarVTimezone}
+        @type vtz: L{VTimezone}
         """
 
         # Map the similar sub-components together
@@ -385,16 +385,16 @@
                 month = int(rule.Rule.MONTH_NAME_TO_POS[splits[1]])
                 if len(splits) > 2 and not splits[2].startswith("#"):
                     if splits[2] == "lastSun":
-                        dt = PyCalendarDateTime(year=year, month=month, day=1)
-                        dt.setDayOfWeekInMonth(-1, PyCalendarDateTime.SUNDAY)
+                        dt = DateTime(year=year, month=month, day=1)
+                        dt.setDayOfWeekInMonth(-1, DateTime.SUNDAY)
                         splits[2] = dt.getDay()
                     elif splits[2] == "lastSat":
-                        dt = PyCalendarDateTime(year=year, month=month, day=1)
-                        dt.setDayOfWeekInMonth(-1, PyCalendarDateTime.SATURDAY)
+                        dt = DateTime(year=year, month=month, day=1)
+                        dt.setDayOfWeekInMonth(-1, DateTime.SATURDAY)
                         splits[2] = dt.getDay()
                     elif splits[2] == "Sun>=1":
-                        dt = PyCalendarDateTime(year=year, month=month, day=1)
-                        dt.setDayOfWeekInMonth(1, PyCalendarDateTime.SUNDAY)
+                        dt = DateTime(year=year, month=month, day=1)
+                        dt.setDayOfWeekInMonth(1, DateTime.SUNDAY)
                         splits[2] = dt.getDay()
                     day = int(splits[2])
                     if len(splits) > 3 and not splits[3].startswith("#"):
@@ -408,7 +408,7 @@
                         if len(splits) > 2:
                             seconds = int(splits[2])
 
-        dt = PyCalendarDateTime(year=year, month=month, day=day, hours=hours, minutes=minutes, seconds=seconds)
+        dt = DateTime(year=year, month=month, day=day, hours=hours, minutes=minutes, seconds=seconds)
         self._cached_until = utils.DateTime(dt, mode)
         return self._cached_until
 
@@ -493,27 +493,27 @@
     def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto):
 
         # Determine type of component based on offset
-        comp = PyCalendarVTimezoneStandard(parent=vtz)
+        comp = Standard(parent=vtz)
 
         # Do offsets
-        tzoffsetfrom = PyCalendarUTCOffsetValue(offsetfrom)
-        tzoffsetto = PyCalendarUTCOffsetValue(offsetto)
+        tzoffsetfrom = UTCOffsetValue(offsetfrom)
+        tzoffsetto = UTCOffsetValue(offsetto)
 
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))
+        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
+        comp.addProperty(Property(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))
 
         # Do TZNAME
         if self.format.find("%") != -1:
             tzname = self.format % ("S",)
         else:
             tzname = self.format
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZNAME, tzname))
+        comp.addProperty(Property(definitions.cICalProperty_TZNAME, tzname))
 
         # Do DTSTART
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_DTSTART, start))
+        comp.addProperty(Property(definitions.cICalProperty_DTSTART, start))
 
         # Recurrence
-        comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RDATE, start))
+        comp.addProperty(Property(definitions.cICalProperty_RDATE, start))
 
         comp.finalise()
         vtz.addComponent(comp)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130816/3fdfbce7/attachment-0001.html>


More information about the calendarserver-changes mailing list