1## 2# Copyright (c) 2007-2011 Cyrus Daboo. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15## 16 17from cStringIO import StringIO 18from pycalendar.datetimevalue import PyCalendarDateTimeValue 19from pycalendar.periodvalue import PyCalendarPeriodValue 20from pycalendar.property import PyCalendarProperty 21from pycalendar.value import PyCalendarValue 22 23class PyCalendarComponentBase(object): 24 25 def __init__(self, parent=None): 26 self.mParentComponent = parent 27 self.mComponents = [] 28 self.mProperties = {} 29 30 def duplicate(self, **args): 31 other = self.__class__(**args) 32 33 other.mComponents = [component.duplicate(parent=other) for component in self.mComponents] 34 35 other.mProperties = {} 36 for propname, props in self.mProperties.iteritems(): 37 other.mProperties[propname] = [i.duplicate() for i in props] 38 return other 39 40 def __str__(self): 41 return self.getText() 42 43 def __ne__(self, other): return not self.__eq__(other) 44 def __eq__(self, other): 45 if not isinstance(other, PyCalendarComponentBase): return False 46 return self.getType() == other.getType() and self.compareProperties(other) and self.compareComponents(other) 47 48 def getType(self): 49 raise NotImplementedError 50 51 def getBeginDelimiter(self): 52 return "BEGIN:" + self.getType() 53 54 def getEndDelimiter(self): 55 return "END:" + self.getType() 56 57 def getSortKey(self): 58 return "" 59 60 def getParentComponent(self): 61 return self.mParentComponent 62 63 def setParentComponent(self, parent): 64 self.mParentComponent = parent 65 66 def compareComponents(self, other): 67 mine = set(self.mComponents) 68 theirs = set(other.mComponents) 69 70 for item in mine: 71 for another in theirs: 72 if item == another: 73 theirs.remove(another) 74 break 75 else: 76 return False 77 return len(theirs) == 0 78 79 def getComponents(self, compname=None): 80 compname = compname.upper() if compname else None 81 return [component for component in self.mComponents if compname is None or component.getType().upper() == compname] 82 83 def getComponentByKey(self, key): 84 for component in self.mComponents: 85 if component.getMapKey() == key: 86 return component 87 else: 88 return None 89 90 def removeComponentByKey(self, key): 91 92 for component in self.mComponents: 93 if component.getMapKey() == key: 94 self.mComponents.remove(component) 95 return 96 97 def addComponent(self, component): 98 self.mComponents.append(component) 99 100 def hasComponent(self, compname): 101 return self.countComponents(compname) != 0 102 103 def countComponents(self, compname): 104 return len(self.getComponents(compname)) 105 106 def removeComponent(self, component): 107 self.mComponents.remove(component) 108 109 def removeAllComponent(self, compname=None): 110 if compname: 111 compname = compname.upper() 112 for component in tuple(self.mComponents): 113 if component.getType().upper() == compname: 114 self.mComponents.remove(component) 115 else: 116 self.mComponents = [] 117 118 def sortedComponentNames(self): 119 return () 120 121 def compareProperties(self, other): 122 mine = set() 123 for props in self.mProperties.values(): 124 mine.update(props) 125 theirs = set() 126 for props in other.mProperties.values(): 127 theirs.update(props) 128 return mine == theirs 129 130 def getProperties(self, propname=None): 131 return self.mProperties.get(propname.upper(), []) if propname else self.mProperties 132 133 def setProperties(self, props): 134 self.mProperties = props 135 136 def addProperty(self, prop): 137 self.mProperties.setdefault(prop.getName().upper(), []).append(prop) 138 139 def hasProperty(self, propname): 140 return self.mProperties.has_key(propname.upper()) 141 142 def countProperty(self, propname): 143 return len(self.mProperties.get(propname.upper(), [])) 144 145 def findFirstProperty(self, propname): 146 return self.mProperties.get(propname.upper(), [None])[0] 147 148 def removeProperty(self, prop): 149 if self.mProperties.has_key(prop.getName().upper()): 150 self.mProperties[prop.getName().upper()].remove(prop) 151 if len(self.mProperties[prop.getName().upper()]) == 0: 152 del self.mProperties[prop.getName().upper()] 153 154 def removeProperties(self, propname): 155 if self.mProperties.has_key(propname.upper()): 156 del self.mProperties[propname.upper()] 157 158 def getPropertyInteger(self, prop, type = None): 159 return self.loadValueInteger(prop, type) 160 161 def getPropertyString(self, prop): 162 return self.loadValueString(prop) 163 164 def getProperty(self, prop, value): 165 return self.loadValue(prop, value) 166 167 def finalise(self): 168 raise NotImplemented 169 170 def getText(self): 171 s = StringIO() 172 self.generate(s) 173 return s.getvalue() 174 175 def generate(self, os): 176 # Header 177 os.write(self.getBeginDelimiter()) 178 os.write("\r\n") 179 180 # Write each property 181 self.writeProperties(os) 182 183 # Write each embedded component based on specific order 184 self.writeComponents(os) 185 186 # Footer 187 os.write(self.getEndDelimiter()) 188 os.write("\r\n") 189 190 def generateFiltered(self, os, filter): 191 # Header 192 os.write(self.getBeginDelimiter()) 193 os.write("\r\n") 194 195 # Write each property 196 self.writePropertiesFiltered(os, filter) 197 198 # Write each property 199 self.writeComponentsFiltered(os, filter) 200 201 # Footer 202 os.write(self.getEndDelimiter()) 203 os.write("\r\n") 204 205 def sortedComponents(self): 206 207 components = self.mComponents[:] 208 sortedcomponents = [] 209 210 # Write each component based on specific order 211 orderedNames = self.sortedComponentNames() 212 for name in orderedNames: 213 214 # Group by name then sort by map key (UID/R-ID) 215 namedcomponents = [] 216 for component in tuple(components): 217 if component.getType().upper() == name: 218 namedcomponents.append(component) 219 components.remove(component) 220 for component in sorted(namedcomponents, key=lambda x:x.getSortKey()): 221 sortedcomponents.append(component) 222 223 # Write out the remainder 224 for component in components: 225 sortedcomponents.append(component) 226 227 return sortedcomponents 228 229 def writeComponents(self, os): 230 231 # Write out the remainder 232 for component in self.sortedComponents(): 233 component.generate(os) 234 235 def writeComponentsFiltered(self, os, filter): 236 # Shortcut for all sub-components 237 if filter.isAllSubComponents(): 238 self.writeComponents(os) 239 elif filter.hasSubComponentFilters(): 240 for subcomp in self.sortedcomponents(): 241 subfilter = filter.getSubComponentFilter(subcomp.getType()) 242 if subfilter is not None: 243 subcomp.generateFiltered(os, subfilter) 244 245 def loadValue(self, value_name): 246 if self.hasProperty(value_name): 247 return self.findFirstProperty(value_name) 248 249 return None 250 251 def loadValueInteger(self, value_name, type=None): 252 if type: 253 if self.hasProperty(value_name): 254 if type == PyCalendarValue.VALUETYPE_INTEGER: 255 ivalue = self.findFirstProperty(value_name).getIntegerValue() 256 if ivalue is not None: 257 return ivalue.getValue() 258 elif type == PyCalendarValue.VALUETYPE_UTC_OFFSET: 259 uvalue = self.findFirstProperty(value_name).getUTCOffsetValue() 260 if (uvalue is not None): 261 return uvalue.getValue() 262 263 return None 264 else: 265 return self.loadValueInteger(value_name, PyCalendarValue.VALUETYPE_INTEGER) 266 267 def loadValueString(self, value_name): 268 if self.hasProperty(value_name): 269 tvalue = self.findFirstProperty(value_name).getTextValue() 270 if (tvalue is not None): 271 return tvalue.getValue() 272 273 return None 274 275 def loadValueDateTime(self, value_name): 276 if self.hasProperty(value_name): 277 dtvalue = self.findFirstProperty(value_name).getDateTimeValue() 278 if dtvalue is not None: 279 return dtvalue.getValue() 280 281 return None 282 283 def loadValueDuration(self, value_name): 284 if self.hasProperty(value_name): 285 dvalue = self.findFirstProperty(value_name).getDurationValue() 286 if (dvalue is not None): 287 return dvalue.getValue() 288 289 return None 290 291 def loadValuePeriod(self, value_name): 292 if self.hasProperty(value_name): 293 pvalue = self.findFirstProperty(value_name).getPeriodValue() 294 if (pvalue is not None): 295 return pvalue.getValue() 296 297 return None 298 299 def loadValueRRULE(self, value_name, value, add): 300 # Get RRULEs 301 if self.hasProperty(value_name): 302 items = self.getProperties()[value_name] 303 for iter in items: 304 rvalue = iter.getRecurrenceValue() 305 if (rvalue is not None): 306 if add: 307 value.addRule(rvalue.getValue()) 308 else: 309 value.subtractRule(rvalue.getValue()) 310 return True 311 else: 312 return False 313 314 def loadValueRDATE(self, value_name, value, add): 315 # Get RDATEs 316 if self.hasProperty(value_name): 317 for iter in self.getProperties(value_name): 318 mvalue = iter.getMultiValue() 319 if (mvalue is not None): 320 for obj in mvalue.getValues(): 321 # cast to date-time 322 if isinstance(obj, PyCalendarDateTimeValue): 323 if add: 324 value.addDT(obj.getValue()) 325 else: 326 value.subtractDT(obj.getValue()) 327 elif isinstance(obj, PyCalendarPeriodValue): 328 if add: 329 value.addPeriod(obj.getValue().getStart()) 330 else: 331 value.subtractPeriod(obj.getValue().getStart()) 332 333 return True 334 else: 335 return False 336 337 def sortedPropertyKeys(self): 338 keys = self.mProperties.keys() 339 keys.sort() 340 341 results = [] 342 for skey in self.sortedPropertyKeyOrder(): 343 if skey in keys: 344 results.append(skey) 345 keys.remove(skey) 346 results.extend(keys) 347 return results 348 349 def sortedPropertyKeyOrder(self): 350 return () 351 352 def writeProperties(self, os): 353 # Sort properties by name 354 keys = self.sortedPropertyKeys() 355 for key in keys: 356 props = self.mProperties[key] 357 for prop in props: 358 prop.generate(os) 359 360 def writePropertiesFiltered(self, os, filter): 361 362 # Sort properties by name 363 keys = self.sortedPropertyKeys() 364 365 # Shortcut for all properties 366 if filter.isAllProperties(): 367 for key in keys: 368 for prop in self.getProperties(key): 369 prop.generate(os) 370 elif filter.hasPropertyFilters(): 371 for key in keys: 372 for prop in self.getProperties(key): 373 prop.generateFiltered(os, filter) 374 375 def loadPrivateValue(self, value_name): 376 # Read it in from properties list and then delete the property from the 377 # main list 378 result = self.loadValueString(value_name) 379 if (result is not None): 380 self.removeProperties(value_name) 381 return result 382 383 def writePrivateProperty(self, os, key, value): 384 prop = PyCalendarProperty(name=key, value=value) 385 prop.generate(os) 386 387 def editProperty(self, propname, propvalue): 388 389 # Remove existing items 390 self.removeProperties(propname) 391 392 # Now create properties 393 if propvalue: 394 self.addProperty(PyCalendarProperty(name=propname, value=propvalue)) 395