001package biweekly.io.scribe; 002 003import java.util.HashMap; 004import java.util.Map; 005 006import javax.xml.namespace.QName; 007 008import biweekly.ICalVersion; 009import biweekly.ICalendar; 010import biweekly.component.ICalComponent; 011import biweekly.component.RawComponent; 012import biweekly.io.scribe.component.DaylightSavingsTimeScribe; 013import biweekly.io.scribe.component.ICalComponentScribe; 014import biweekly.io.scribe.component.ICalendarScribe; 015import biweekly.io.scribe.component.RawComponentScribe; 016import biweekly.io.scribe.component.StandardTimeScribe; 017import biweekly.io.scribe.component.VAlarmScribe; 018import biweekly.io.scribe.component.VEventScribe; 019import biweekly.io.scribe.component.VFreeBusyScribe; 020import biweekly.io.scribe.component.VJournalScribe; 021import biweekly.io.scribe.component.VTimezoneScribe; 022import biweekly.io.scribe.component.VTodoScribe; 023import biweekly.io.scribe.property.ActionScribe; 024import biweekly.io.scribe.property.AttachmentScribe; 025import biweekly.io.scribe.property.AttendeeScribe; 026import biweekly.io.scribe.property.AudioAlarmScribe; 027import biweekly.io.scribe.property.CalendarScaleScribe; 028import biweekly.io.scribe.property.CategoriesScribe; 029import biweekly.io.scribe.property.ClassificationScribe; 030import biweekly.io.scribe.property.CommentScribe; 031import biweekly.io.scribe.property.CompletedScribe; 032import biweekly.io.scribe.property.ContactScribe; 033import biweekly.io.scribe.property.CreatedScribe; 034import biweekly.io.scribe.property.DateDueScribe; 035import biweekly.io.scribe.property.DateEndScribe; 036import biweekly.io.scribe.property.DateStartScribe; 037import biweekly.io.scribe.property.DateTimeStampScribe; 038import biweekly.io.scribe.property.DaylightScribe; 039import biweekly.io.scribe.property.DescriptionScribe; 040import biweekly.io.scribe.property.DisplayAlarmScribe; 041import biweekly.io.scribe.property.DurationPropertyScribe; 042import biweekly.io.scribe.property.EmailAlarmScribe; 043import biweekly.io.scribe.property.ExceptionDatesScribe; 044import biweekly.io.scribe.property.ExceptionRuleScribe; 045import biweekly.io.scribe.property.FreeBusyScribe; 046import biweekly.io.scribe.property.GeoScribe; 047import biweekly.io.scribe.property.ICalPropertyScribe; 048import biweekly.io.scribe.property.LastModifiedScribe; 049import biweekly.io.scribe.property.LocationScribe; 050import biweekly.io.scribe.property.MethodScribe; 051import biweekly.io.scribe.property.OrganizerScribe; 052import biweekly.io.scribe.property.PercentCompleteScribe; 053import biweekly.io.scribe.property.PriorityScribe; 054import biweekly.io.scribe.property.ProcedureAlarmScribe; 055import biweekly.io.scribe.property.ProductIdScribe; 056import biweekly.io.scribe.property.RawPropertyScribe; 057import biweekly.io.scribe.property.RecurrenceDatesScribe; 058import biweekly.io.scribe.property.RecurrenceIdScribe; 059import biweekly.io.scribe.property.RecurrenceRuleScribe; 060import biweekly.io.scribe.property.RelatedToScribe; 061import biweekly.io.scribe.property.RepeatScribe; 062import biweekly.io.scribe.property.RequestStatusScribe; 063import biweekly.io.scribe.property.ResourcesScribe; 064import biweekly.io.scribe.property.SequenceScribe; 065import biweekly.io.scribe.property.StatusScribe; 066import biweekly.io.scribe.property.SummaryScribe; 067import biweekly.io.scribe.property.TimezoneIdScribe; 068import biweekly.io.scribe.property.TimezoneNameScribe; 069import biweekly.io.scribe.property.TimezoneOffsetFromScribe; 070import biweekly.io.scribe.property.TimezoneOffsetToScribe; 071import biweekly.io.scribe.property.TimezoneScribe; 072import biweekly.io.scribe.property.TimezoneUrlScribe; 073import biweekly.io.scribe.property.TransparencyScribe; 074import biweekly.io.scribe.property.TriggerScribe; 075import biweekly.io.scribe.property.UidScribe; 076import biweekly.io.scribe.property.UrlScribe; 077import biweekly.io.scribe.property.VersionScribe; 078import biweekly.io.scribe.property.XmlScribe; 079import biweekly.io.xml.XCalNamespaceContext; 080import biweekly.property.Created; 081import biweekly.property.ICalProperty; 082import biweekly.property.RawProperty; 083import biweekly.property.Xml; 084 085/* 086 Copyright (c) 2013-2015, Michael Angstadt 087 All rights reserved. 088 089 Redistribution and use in source and binary forms, with or without 090 modification, are permitted provided that the following conditions are met: 091 092 1. Redistributions of source code must retain the above copyright notice, this 093 list of conditions and the following disclaimer. 094 2. Redistributions in binary form must reproduce the above copyright notice, 095 this list of conditions and the following disclaimer in the documentation 096 and/or other materials provided with the distribution. 097 098 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 099 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 100 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 101 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 102 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 103 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 104 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 105 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 106 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 107 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 108 */ 109 110/** 111 * <p> 112 * Manages a listing of component and property scribes. This is useful for 113 * injecting the scribes of any experimental components or properties you have 114 * defined into a reader or writer object. The same ScribeIndex instance can be 115 * reused and injected into multiple reader/writer classes. 116 * </p> 117 * <p> 118 * <b>Example:</b> 119 * 120 * <pre class="brush:java"> 121 * //init the index 122 * ScribeIndex index = new ScribeIndex(); 123 * index.register(new CustomPropertyScribe()); 124 * index.register(new AnotherCustomPropertyScribe()); 125 * index.register(new CustomComponentScribe()); 126 * 127 * //inject into a reader class 128 * ICalReader reader = new ICalReader(...); 129 * textReader.setScribeIndex(index); 130 * List<ICalendar> icals = new ArrayList<ICalendar>(); 131 * ICalendar ical; 132 * while ((ical = reader.readNext()) != null){ 133 * icals.add(ical); 134 * } 135 * 136 * //inject the same instance in another reader/writer class 137 * JCalWriter writer = new JCalWriter(...); 138 * writer.setScribeIndex(index); 139 * for (ICalendar ical : icals){ 140 * writer.write(ical); 141 * } 142 * </pre> 143 * 144 * </p> 145 * @author Michael Angstadt 146 */ 147public class ScribeIndex { 148 //define standard component scribes 149 private static final Map<String, ICalComponentScribe<? extends ICalComponent>> standardCompByName = new HashMap<String, ICalComponentScribe<? extends ICalComponent>>(); 150 private static final Map<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>> standardCompByClass = new HashMap<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>>(); 151 static { 152 registerStandard(new ICalendarScribe()); 153 registerStandard(new VAlarmScribe()); 154 registerStandard(new VEventScribe()); 155 registerStandard(new VFreeBusyScribe()); 156 registerStandard(new VJournalScribe()); 157 registerStandard(new VTodoScribe()); 158 registerStandard(new VTimezoneScribe()); 159 registerStandard(new StandardTimeScribe()); 160 registerStandard(new DaylightSavingsTimeScribe()); 161 } 162 163 //define standard property scribes 164 private static final Map<String, ICalPropertyScribe<? extends ICalProperty>> standardPropByName = new HashMap<String, ICalPropertyScribe<? extends ICalProperty>>(); 165 private static final Map<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>> standardPropByClass = new HashMap<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>>(); 166 private static final Map<QName, ICalPropertyScribe<? extends ICalProperty>> standardPropByQName = new HashMap<QName, ICalPropertyScribe<? extends ICalProperty>>(); 167 static { 168 //RFC 5545 169 registerStandard(new ActionScribe()); 170 registerStandard(new AttachmentScribe()); 171 registerStandard(new AttendeeScribe()); 172 registerStandard(new CalendarScaleScribe()); 173 registerStandard(new CategoriesScribe()); 174 registerStandard(new ClassificationScribe()); 175 registerStandard(new CommentScribe()); 176 registerStandard(new CompletedScribe()); 177 registerStandard(new ContactScribe()); 178 registerStandard(new CreatedScribe()); 179 registerStandard(new DateDueScribe()); 180 registerStandard(new DateEndScribe()); 181 registerStandard(new DateStartScribe()); 182 registerStandard(new DateTimeStampScribe()); 183 registerStandard(new DescriptionScribe()); 184 registerStandard(new DurationPropertyScribe()); 185 registerStandard(new ExceptionDatesScribe()); 186 registerStandard(new FreeBusyScribe()); 187 registerStandard(new GeoScribe()); 188 registerStandard(new LastModifiedScribe()); 189 registerStandard(new LocationScribe()); 190 registerStandard(new MethodScribe()); 191 registerStandard(new OrganizerScribe()); 192 registerStandard(new PercentCompleteScribe()); 193 registerStandard(new PriorityScribe()); 194 registerStandard(new ProductIdScribe()); 195 registerStandard(new RecurrenceDatesScribe()); 196 registerStandard(new RecurrenceIdScribe()); 197 registerStandard(new RecurrenceRuleScribe()); 198 registerStandard(new RelatedToScribe()); 199 registerStandard(new RepeatScribe()); 200 registerStandard(new RequestStatusScribe()); 201 registerStandard(new ResourcesScribe()); 202 registerStandard(new SequenceScribe()); 203 registerStandard(new StatusScribe()); 204 registerStandard(new SummaryScribe()); 205 registerStandard(new TimezoneIdScribe()); 206 registerStandard(new TimezoneNameScribe()); 207 registerStandard(new TimezoneOffsetFromScribe()); 208 registerStandard(new TimezoneOffsetToScribe()); 209 registerStandard(new TimezoneUrlScribe()); 210 registerStandard(new TransparencyScribe()); 211 registerStandard(new TriggerScribe()); 212 registerStandard(new UidScribe()); 213 registerStandard(new UrlScribe()); 214 registerStandard(new VersionScribe()); 215 216 //RFC 6321 217 registerStandard(new XmlScribe()); 218 219 //RFC 2445 220 registerStandard(new ExceptionRuleScribe()); 221 222 //vCal 223 registerStandard(new AudioAlarmScribe()); 224 registerStandard(new DaylightScribe()); 225 registerStandard(new DisplayAlarmScribe()); 226 registerStandard(new EmailAlarmScribe()); 227 registerStandard(new ProcedureAlarmScribe()); 228 registerStandard(new TimezoneScribe()); 229 } 230 231 private final Map<String, ICalComponentScribe<? extends ICalComponent>> experimentalCompByName = new HashMap<String, ICalComponentScribe<? extends ICalComponent>>(0); 232 private final Map<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>> experimentalCompByClass = new HashMap<Class<? extends ICalComponent>, ICalComponentScribe<? extends ICalComponent>>(0); 233 234 private final Map<String, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByName = new HashMap<String, ICalPropertyScribe<? extends ICalProperty>>(0); 235 private final Map<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByClass = new HashMap<Class<? extends ICalProperty>, ICalPropertyScribe<? extends ICalProperty>>(0); 236 private final Map<QName, ICalPropertyScribe<? extends ICalProperty>> experimentalPropByQName = new HashMap<QName, ICalPropertyScribe<? extends ICalProperty>>(0); 237 238 /** 239 * Gets a component scribe by name. 240 * @param componentName the component name (e.g. "VEVENT") 241 * @param version the version of the iCalendar object being parsed 242 * @return the component scribe or a {@link RawComponentScribe} if not found 243 */ 244 public ICalComponentScribe<? extends ICalComponent> getComponentScribe(String componentName, ICalVersion version) { 245 componentName = componentName.toUpperCase(); 246 247 ICalComponentScribe<? extends ICalComponent> scribe = experimentalCompByName.get(componentName); 248 if (scribe == null) { 249 scribe = standardCompByName.get(componentName); 250 } 251 252 if (scribe == null) { 253 return new RawComponentScribe(componentName); 254 } 255 256 if (version != null && !scribe.getSupportedVersions().contains(version)) { 257 //treat the component as a raw component if the current iCal version doesn't support it 258 return new RawComponentScribe(componentName); 259 } 260 261 return scribe; 262 } 263 264 /** 265 * Gets a property scribe by name. 266 * @param propertyName the property name (e.g. "UID") 267 * @param version the version of the iCalendar object being parsed 268 * @return the property scribe or a {@link RawPropertyScribe} if not found 269 */ 270 public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(String propertyName, ICalVersion version) { 271 propertyName = propertyName.toUpperCase(); 272 273 //the vCal 1.0 "DCREATED" property is the same as the iCal 2.0 "CREATED" property 274 if ((version == null || version == ICalVersion.V1_0) && "DCREATED".equals(propertyName)) { 275 return getPropertyScribe(Created.class); 276 } 277 278 ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByName.get(propertyName); 279 if (scribe == null) { 280 scribe = standardPropByName.get(propertyName); 281 } 282 283 if (scribe == null) { 284 return new RawPropertyScribe(propertyName); 285 } 286 287 if (version != null && !scribe.getSupportedVersions().contains(version)) { 288 //treat the property as a raw property if the current iCal version doesn't support it 289 return new RawPropertyScribe(propertyName); 290 } 291 292 return scribe; 293 } 294 295 /** 296 * Gets a component scribe by class. 297 * @param clazz the component class 298 * @return the component scribe or null if not found 299 */ 300 public ICalComponentScribe<? extends ICalComponent> getComponentScribe(Class<? extends ICalComponent> clazz) { 301 ICalComponentScribe<? extends ICalComponent> scribe = experimentalCompByClass.get(clazz); 302 if (scribe != null) { 303 return scribe; 304 } 305 306 return standardCompByClass.get(clazz); 307 } 308 309 /** 310 * Gets a property scribe by class. 311 * @param clazz the property class 312 * @return the property scribe or null if not found 313 */ 314 public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(Class<? extends ICalProperty> clazz) { 315 ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByClass.get(clazz); 316 if (scribe != null) { 317 return scribe; 318 } 319 320 return standardPropByClass.get(clazz); 321 } 322 323 /** 324 * Gets the appropriate component scribe for a given component instance. 325 * @param component the component instance 326 * @return the component scribe or null if not found 327 */ 328 public ICalComponentScribe<? extends ICalComponent> getComponentScribe(ICalComponent component) { 329 if (component instanceof RawComponent) { 330 RawComponent raw = (RawComponent) component; 331 return new RawComponentScribe(raw.getName()); 332 } 333 334 return getComponentScribe(component.getClass()); 335 } 336 337 /** 338 * Gets the appropriate property scribe for a given property instance. 339 * @param property the property instance 340 * @return the property scribe or null if not found 341 */ 342 public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(ICalProperty property) { 343 if (property instanceof RawProperty) { 344 RawProperty raw = (RawProperty) property; 345 return new RawPropertyScribe(raw.getName()); 346 } 347 348 return getPropertyScribe(property.getClass()); 349 } 350 351 /** 352 * Gets a property scribe by XML local name and namespace. 353 * @param qname the XML local name and namespace 354 * @return the property scribe or a {@link XmlScribe} if not found 355 */ 356 public ICalPropertyScribe<? extends ICalProperty> getPropertyScribe(QName qname) { 357 ICalPropertyScribe<? extends ICalProperty> scribe = experimentalPropByQName.get(qname); 358 if (scribe == null) { 359 scribe = standardPropByQName.get(qname); 360 } 361 362 if (scribe == null || !scribe.getSupportedVersions().contains(ICalVersion.V2_0)) { 363 if (XCalNamespaceContext.XCAL_NS.equals(qname.getNamespaceURI())) { 364 return new RawPropertyScribe(qname.getLocalPart().toUpperCase()); 365 } 366 return getPropertyScribe(Xml.class); 367 } 368 369 return scribe; 370 } 371 372 /** 373 * Registers a component scribe. 374 * @param scribe the scribe to register 375 */ 376 public void register(ICalComponentScribe<? extends ICalComponent> scribe) { 377 experimentalCompByName.put(scribe.getComponentName().toUpperCase(), scribe); 378 experimentalCompByClass.put(scribe.getComponentClass(), scribe); 379 } 380 381 /** 382 * Registers a property scribe. 383 * @param scribe the scribe to register 384 */ 385 public void register(ICalPropertyScribe<? extends ICalProperty> scribe) { 386 experimentalPropByName.put(scribe.getPropertyName().toUpperCase(), scribe); 387 experimentalPropByClass.put(scribe.getPropertyClass(), scribe); 388 experimentalPropByQName.put(scribe.getQName(), scribe); 389 } 390 391 /** 392 * Unregisters a component scribe. 393 * @param scribe the scribe to unregister 394 */ 395 public void unregister(ICalComponentScribe<? extends ICalComponent> scribe) { 396 experimentalCompByName.remove(scribe.getComponentName().toUpperCase()); 397 experimentalCompByClass.remove(scribe.getComponentClass()); 398 } 399 400 /** 401 * Unregisters a property scribe 402 * @param scribe the scribe to unregister 403 */ 404 public void unregister(ICalPropertyScribe<? extends ICalProperty> scribe) { 405 experimentalPropByName.remove(scribe.getPropertyName().toUpperCase()); 406 experimentalPropByClass.remove(scribe.getPropertyClass()); 407 experimentalPropByQName.remove(scribe.getQName()); 408 } 409 410 /** 411 * Convenience method for getting the scribe of the root iCalendar component 412 * ("VCALENDAR"). 413 * @return the scribe 414 */ 415 public static ICalendarScribe getICalendarScribe() { 416 return (ICalendarScribe) standardCompByClass.get(ICalendar.class); 417 } 418 419 private static void registerStandard(ICalComponentScribe<? extends ICalComponent> scribe) { 420 standardCompByName.put(scribe.getComponentName().toUpperCase(), scribe); 421 standardCompByClass.put(scribe.getComponentClass(), scribe); 422 } 423 424 private static void registerStandard(ICalPropertyScribe<? extends ICalProperty> scribe) { 425 standardPropByName.put(scribe.getPropertyName().toUpperCase(), scribe); 426 standardPropByClass.put(scribe.getPropertyClass(), scribe); 427 standardPropByQName.put(scribe.getQName(), scribe); 428 } 429}