001package biweekly;
002
003import java.io.File;
004import java.io.IOException;
005import java.io.OutputStream;
006import java.io.Writer;
007import java.util.ArrayList;
008import java.util.List;
009
010import javax.xml.transform.TransformerException;
011
012import biweekly.ValidationWarnings.WarningsGroup;
013import biweekly.component.ICalComponent;
014import biweekly.component.VEvent;
015import biweekly.component.VFreeBusy;
016import biweekly.component.VJournal;
017import biweekly.component.VTodo;
018import biweekly.property.CalendarScale;
019import biweekly.property.Geo;
020import biweekly.property.Method;
021import biweekly.property.ProductId;
022
023/*
024 Copyright (c) 2013-2015, Michael Angstadt
025 All rights reserved.
026
027 Redistribution and use in source and binary forms, with or without
028 modification, are permitted provided that the following conditions are met: 
029
030 1. Redistributions of source code must retain the above copyright notice, this
031 list of conditions and the following disclaimer. 
032 2. Redistributions in binary form must reproduce the above copyright notice,
033 this list of conditions and the following disclaimer in the documentation
034 and/or other materials provided with the distribution. 
035
036 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
037 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
038 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
040 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
041 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
042 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
044 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
045 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
046 */
047
048/**
049 * <p>
050 * Represents an iCalendar object.
051 * </p>
052 * 
053 * <p>
054 * <b>Examples:</b>
055 * 
056 * <pre class="brush:java">
057 * ICalendar ical = new ICalendar();
058 * 
059 * VEvent event = new VEvent();
060 * event.setSummary("Team Meeting");
061 * Date start = ...;
062 * event.setDateStart(start);
063 * Date end = ...;
064 * event.setDateEnd(end);
065 * ical.addEvent(event);
066 * </pre>
067 * 
068 * </p>
069 * @author Michael Angstadt
070 * @see <a href="http://tools.ietf.org/html/rfc5545">RFC 5545</a>
071 */
072public class ICalendar extends ICalComponent {
073        private ICalVersion version;
074
075        /**
076         * <p>
077         * Creates a new iCalendar object.
078         * </p>
079         * <p>
080         * The following properties are auto-generated on object creation. These
081         * properties <b>must</b> be present in order for the iCalendar object to be
082         * valid:
083         * <ul>
084         * <li>{@link ProductId} - Set to a value that represents this library.</li>
085         * </ul>
086         * </p>
087         */
088        public ICalendar() {
089                setProductId(ProductId.biweekly());
090        }
091
092        /**
093         * Gets the version of this iCalendar object.
094         * @return the version
095         */
096        public ICalVersion getVersion() {
097                return version;
098        }
099
100        /**
101         * Sets the version of this iCalendar object.
102         * @param version the version
103         */
104        public void setVersion(ICalVersion version) {
105                this.version = version;
106        }
107
108        /**
109         * Gets the name of the application that created the iCalendar object. All
110         * {@link ICalendar} objects are initialized with a product ID representing
111         * this library. It is a <b>required</b> property.
112         * @return the property instance or null if not set
113         * @see <a href="http://tools.ietf.org/html/rfc5545#page-78">RFC 5545
114         * p.78-9</a>
115         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.24</a>
116         */
117        public ProductId getProductId() {
118                return getProperty(ProductId.class);
119        }
120
121        /**
122         * Sets the name of the application that created the iCalendar object. All
123         * {@link ICalendar} objects are initialized with a product ID representing
124         * this library. It is a <b>required</b> property.
125         * @param prodId the property instance or null to remove
126         * @see <a href="http://tools.ietf.org/html/rfc5545#page-78">RFC 5545
127         * p.78-9</a>
128         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.24</a>
129         */
130        public void setProductId(ProductId prodId) {
131                setProperty(ProductId.class, prodId);
132        }
133
134        /**
135         * Sets the application that created the iCalendar object. All
136         * {@link ICalendar} objects are initialized with a product ID representing
137         * this library.
138         * @param prodId a unique string representing the application (e.g.
139         * "-//Company//Application//EN") or null to remove
140         * @return the property that was created
141         * @see <a href="http://tools.ietf.org/html/rfc5545#page-78">RFC 5545
142         * p.78-9</a>
143         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.24</a>
144         */
145        public ProductId setProductId(String prodId) {
146                ProductId prop = (prodId == null) ? null : new ProductId(prodId);
147                setProductId(prop);
148                return prop;
149        }
150
151        /**
152         * Gets the calendar system that this iCalendar object uses. If none is
153         * specified, then the calendar is assumed to be in Gregorian format.
154         * @return the calendar system or null if not set
155         * @see <a href="http://tools.ietf.org/html/rfc5545#page-76">RFC 5545
156         * p.76-7</a>
157         */
158        public CalendarScale getCalendarScale() {
159                return getProperty(CalendarScale.class);
160        }
161
162        /**
163         * Sets the calendar system that this iCalendar object uses. If none is
164         * specified, then the calendar is assumed to be in Gregorian format.
165         * @param calendarScale the calendar system or null to remove
166         * @see <a href="http://tools.ietf.org/html/rfc5545#page-76">RFC 5545
167         * p.76-7</a>
168         */
169        public void setCalendarScale(CalendarScale calendarScale) {
170                setProperty(CalendarScale.class, calendarScale);
171        }
172
173        /**
174         * Gets the value of the Content-Type "method" parameter if the iCalendar
175         * object is defined as a MIME message entity.
176         * @return the property or null if not set
177         * @see <a href="http://tools.ietf.org/html/rfc5545#page-77">RFC 5545
178         * p.77-8</a>
179         */
180        public Method getMethod() {
181                return getProperty(Method.class);
182        }
183
184        /**
185         * Sets the value of the Content-Type "method" parameter if the iCalendar
186         * object is defined as a MIME message entity.
187         * @param method the property or null to remove
188         * @see <a href="http://tools.ietf.org/html/rfc5545#page-77">RFC 5545
189         * p.77-8</a>
190         */
191        public void setMethod(Method method) {
192                setProperty(Method.class, method);
193        }
194
195        /**
196         * Sets the value of the Content-Type "method" parameter if the iCalendar
197         * object is defined as a MIME message entity.
198         * @param method the method or null to remove
199         * @return the property that was created
200         * @see <a href="http://tools.ietf.org/html/rfc5545#page-77">RFC 5545
201         * p.77-8</a>
202         */
203        public Method setMethod(String method) {
204                Method prop = (method == null) ? null : new Method(method);
205                setMethod(prop);
206                return prop;
207        }
208
209        /**
210         * Gets the events.
211         * @return the events
212         * @see <a href="http://tools.ietf.org/html/rfc5545#page-52">RFC 5545
213         * p.52-5</a>
214         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.13</a>
215         */
216        public List<VEvent> getEvents() {
217                return getComponents(VEvent.class);
218        }
219
220        /**
221         * Adds an event.
222         * @param event the event
223         * @see <a href="http://tools.ietf.org/html/rfc5545#page-52">RFC 5545
224         * p.52-5</a>
225         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.13</a>
226         */
227        public void addEvent(VEvent event) {
228                addComponent(event);
229        }
230
231        /**
232         * Gets the to-dos.
233         * @return the to-dos
234         * @see <a href="http://tools.ietf.org/html/rfc5545#page-55">RFC 5545
235         * p.55-7</a>
236         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.14</a>
237         */
238        public List<VTodo> getTodos() {
239                return getComponents(VTodo.class);
240        }
241
242        /**
243         * Adds a to-do.
244         * @param todo the to-do
245         * @see <a href="http://tools.ietf.org/html/rfc5545#page-55">RFC 5545
246         * p.55-7</a>
247         * @see <a href="http://www.imc.org/pdi/vcal-10.doc">vCal 1.0 p.14</a>
248         */
249        public void addTodo(VTodo todo) {
250                addComponent(todo);
251        }
252
253        /**
254         * Gets the journal entries.
255         * @return the journal entries
256         * @see <a href="http://tools.ietf.org/html/rfc5545#page-55">RFC 5545
257         * p.57-9</a>
258         */
259        public List<VJournal> getJournals() {
260                return getComponents(VJournal.class);
261        }
262
263        /**
264         * Adds a journal entry.
265         * @param journal the journal entry
266         * @see <a href="http://tools.ietf.org/html/rfc5545#page-55">RFC 5545
267         * p.57-9</a>
268         */
269        public void addJournal(VJournal journal) {
270                addComponent(journal);
271        }
272
273        /**
274         * Gets the free/busy entries.
275         * @return the free/busy entries
276         * @see <a href="http://tools.ietf.org/html/rfc5545#page-59">RFC 5545
277         * p.59-62</a>
278         */
279        public List<VFreeBusy> getFreeBusies() {
280                return getComponents(VFreeBusy.class);
281        }
282
283        /**
284         * Adds a free/busy entry.
285         * @param freeBusy the free/busy entry
286         * @see <a href="http://tools.ietf.org/html/rfc5545#page-59">RFC 5545
287         * p.59-62</a>
288         */
289        public void addFreeBusy(VFreeBusy freeBusy) {
290                addComponent(freeBusy);
291        }
292
293        /**
294         * Checks this iCalendar object for data consistency problems or deviations
295         * from the spec. These problems will not prevent the iCalendar object from
296         * being written to a data stream, but may prevent it from being parsed
297         * correctly by the consuming application. These problems can largely be
298         * avoided by reading the Javadocs of the component and property classes, or
299         * by being familiar with the iCalendar standard.
300         * @param version the version to validate against
301         * @return the validation warnings
302         */
303        public ValidationWarnings validate(ICalVersion version) {
304                List<WarningsGroup> warnings = validate(new ArrayList<ICalComponent>(0), version);
305                return new ValidationWarnings(warnings);
306        }
307
308        @SuppressWarnings("unchecked")
309        @Override
310        protected void validate(List<ICalComponent> components, ICalVersion version, List<Warning> warnings) {
311                if (version != ICalVersion.V1_0) {
312                        checkRequiredCardinality(warnings, ProductId.class);
313
314                        if (this.components.isEmpty()) {
315                                warnings.add(Warning.validate(4));
316                        }
317
318                        if (getProperty(Geo.class) != null) {
319                                warnings.add(Warning.validate(44));
320                        }
321                }
322        }
323
324        /**
325         * Marshals this iCalendar object to its plain text representation.
326         * @return the plain text representation
327         */
328        public String write() {
329                ICalVersion version = (this.version == null) ? ICalVersion.V2_0 : this.version;
330                return Biweekly.write(this).version(version).go();
331        }
332
333        /**
334         * Marshals this iCalendar object to its plain text representation.
335         * @param file the file to write to
336         * @throws IOException if there's an I/O problem
337         */
338        public void write(File file) throws IOException {
339                ICalVersion version = (this.version == null) ? ICalVersion.V2_0 : this.version;
340                Biweekly.write(this).version(version).go(file);
341        }
342
343        /**
344         * Marshals this iCalendar object to its plain text representation.
345         * @param out the data stream to write to
346         * @throws IOException if there's an I/O problem
347         */
348        public void write(OutputStream out) throws IOException {
349                ICalVersion version = (this.version == null) ? ICalVersion.V2_0 : this.version;
350                Biweekly.write(this).version(version).go(out);
351        }
352
353        /**
354         * Marshals this iCalendar object to its plain text representation.
355         * @param writer the data stream to write to
356         * @throws IOException if there's an I/O problem
357         */
358        public void write(Writer writer) throws IOException {
359                ICalVersion version = (this.version == null) ? ICalVersion.V2_0 : this.version;
360                Biweekly.write(this).version(version).go(writer);
361        }
362
363        /**
364         * Marshals this iCalendar object to its XML representation (xCal). If the
365         * iCalendar object contains user-defined property or component objects, use
366         * the {@link Biweekly} class instead, in order to register the scribe
367         * classes.
368         * @return the XML document
369         * @throws IllegalArgumentException if the iCalendar object contains
370         * user-defined property or component objects
371         */
372        public String writeXml() {
373                return Biweekly.writeXml(this).indent(2).go();
374        }
375
376        /**
377         * Marshals this iCalendar object to its XML representation (xCal). If the
378         * iCalendar object contains user-defined property or component objects, use
379         * the {@link Biweekly} class instead, in order to register the scribe
380         * classes.
381         * @param file the file to write to
382         * @throws IllegalArgumentException if the iCalendar object contains
383         * user-defined property or component objects
384         * @throws TransformerException if there's an I/O problem
385         * @throws IOException if the file cannot be written to
386         */
387        public void writeXml(File file) throws TransformerException, IOException {
388                Biweekly.writeXml(this).indent(2).go(file);
389        }
390
391        /**
392         * Marshals this iCalendar object to its XML representation (xCal). If the
393         * iCalendar object contains user-defined property or component objects, use
394         * the {@link Biweekly} class instead, in order to register the scribe
395         * classes.
396         * @param out the data stream to write to
397         * @throws IllegalArgumentException if the iCalendar object contains
398         * user-defined property or component objects
399         * @throws TransformerException if there's an I/O problem
400         */
401        public void writeXml(OutputStream out) throws TransformerException {
402                Biweekly.writeXml(this).indent(2).go(out);
403        }
404
405        /**
406         * Marshals this iCalendar object to its XML representation (xCal). If the
407         * iCalendar object contains user-defined property or component objects, use
408         * the {@link Biweekly} class instead, in order to register the scribe
409         * classes.
410         * @param writer the data stream to write to
411         * @throws IllegalArgumentException if the iCalendar object contains
412         * user-defined property or component objects
413         * @throws TransformerException if there's an I/O problem
414         */
415        public void writeXml(Writer writer) throws TransformerException {
416                Biweekly.writeXml(this).indent(2).go(writer);
417        }
418
419        /**
420         * Marshals this iCalendar object to its JSON representation (jCal). If the
421         * iCalendar object contains user-defined property or component objects, use
422         * the {@link Biweekly} class instead, in order to register the scribe
423         * classes.
424         * @return the JSON string
425         * @throws IllegalArgumentException if the iCalendar object contains
426         * user-defined property or component objects
427         */
428        public String writeJson() {
429                return Biweekly.writeJson(this).go();
430        }
431
432        /**
433         * Marshals this iCalendar object to its JSON representation (jCal). If the
434         * iCalendar object contains user-defined property or component objects, use
435         * the {@link Biweekly} class instead, in order to register the scribe
436         * classes.
437         * @param file the file to write to
438         * @throws IllegalArgumentException if the iCalendar object contains
439         * user-defined property or component objects
440         * @throws IOException if there's a problem writing to the file
441         */
442        public void writeJson(File file) throws IOException {
443                Biweekly.writeJson(this).go(file);
444        }
445
446        /**
447         * Marshals this iCalendar object to its JSON representation (jCal). If the
448         * iCalendar object contains user-defined property or component objects, use
449         * the {@link Biweekly} class instead, in order to register the scribe
450         * classes.
451         * @param out the data stream to write to
452         * @throws IllegalArgumentException if the iCalendar object contains
453         * user-defined property or component objects
454         * @throws IOException if there's a problem writing to the output stream
455         */
456        public void writeJson(OutputStream out) throws IOException {
457                Biweekly.writeJson(this).go(out);
458        }
459
460        /**
461         * Marshals this iCalendar object to its JSON representation (jCal). If the
462         * iCalendar object contains user-defined property or component objects, use
463         * the {@link Biweekly} class instead, in order to register the scribe
464         * classes.
465         * @param writer the data stream to write to
466         * @throws IllegalArgumentException if the iCalendar object contains
467         * user-defined property or component objects
468         * @throws IOException if there's a problem writing to the writer
469         */
470        public void writeJson(Writer writer) throws IOException {
471                Biweekly.writeJson(this).go(writer);
472        }
473}